Section 10: Outline and Glow Effects - Lecture 182

Disable Trace while Interping

Other fixes:
* Fixed item overlap events not being generated on spawn
* Player rotation not being set correctly at startup when PlayerStart
isn't at default Yaw=0, where turn in place logic was being used
instead.
This commit is contained in:
charnet3d 2024-01-07 10:11:37 +01:00
parent a703aca938
commit 59ad009db4
32 changed files with 311 additions and 29 deletions

91
.editorconfig Normal file
View File

@ -0,0 +1,91 @@
[*.{cpp,h}]
# Naming convention rules (note: currently need to be ordered from more to less specific)
cpp_naming_rule.aactor_prefixed.symbols = aactor_class
cpp_naming_rule.aactor_prefixed.style = aactor_style
cpp_naming_rule.swidget_prefixed.symbols = swidget_class
cpp_naming_rule.swidget_prefixed.style = swidget_style
cpp_naming_rule.uobject_prefixed.symbols = uobject_class
cpp_naming_rule.uobject_prefixed.style = uobject_style
cpp_naming_rule.booleans_prefixed.symbols = boolean_vars
cpp_naming_rule.booleans_prefixed.style = boolean_style
cpp_naming_rule.structs_prefixed.symbols = structs
cpp_naming_rule.structs_prefixed.style = unreal_engine_structs
cpp_naming_rule.enums_prefixed.symbols = enums
cpp_naming_rule.enums_prefixed.style = unreal_engine_enums
cpp_naming_rule.templates_prefixed.symbols = templates
cpp_naming_rule.templates_prefixed.style = unreal_engine_templates
cpp_naming_rule.general_names.symbols = all_symbols
cpp_naming_rule.general_names.style = unreal_engine_default
# Naming convention symbols
cpp_naming_symbols.aactor_class.applicable_kinds = class
cpp_naming_symbols.aactor_class.applicable_type = AActor
cpp_naming_symbols.swidget_class.applicable_kinds = class
cpp_naming_symbols.swidget_class.applicable_type = SWidget
cpp_naming_symbols.uobject_class.applicable_kinds = class
cpp_naming_symbols.uobject_class.applicable_type = UObject
cpp_naming_symbols.boolean_vars.applicable_kinds = local,parameter,field
cpp_naming_symbols.boolean_vars.applicable_type = bool
cpp_naming_symbols.enums.applicable_kinds = enum
cpp_naming_symbols.templates.applicable_kinds = template_class
cpp_naming_symbols.structs.applicable_kinds = struct
cpp_naming_symbols.all_symbols.applicable_kinds = *
# Naming convention styles
cpp_naming_style.unreal_engine_default.capitalization = pascal_case
cpp_naming_style.unreal_engine_default.required_prefix =
cpp_naming_style.unreal_engine_default.required_suffix =
cpp_naming_style.unreal_engine_default.word_separator =
cpp_naming_style.unreal_engine_enums.capitalization = pascal_case
cpp_naming_style.unreal_engine_enums.required_prefix = E
cpp_naming_style.unreal_engine_enums.required_suffix =
cpp_naming_style.unreal_engine_enums.word_separator =
cpp_naming_style.unreal_engine_templates.capitalization = pascal_case
cpp_naming_style.unreal_engine_templates.required_prefix = T
cpp_naming_style.unreal_engine_templates.required_suffix =
cpp_naming_style.unreal_engine_templates.word_separator =
cpp_naming_style.unreal_engine_structs.capitalization = pascal_case
cpp_naming_style.unreal_engine_structs.required_prefix = F
cpp_naming_style.unreal_engine_structs.required_suffix =
cpp_naming_style.unreal_engine_structs.word_separator =
cpp_naming_style.uobject_style.capitalization = pascal_case
cpp_naming_style.uobject_style.required_prefix = U
cpp_naming_style.uobject_style.required_suffix =
cpp_naming_style.uobject_style.word_separator =
cpp_naming_style.aactor_style.capitalization = pascal_case
cpp_naming_style.aactor_style.required_prefix = A
cpp_naming_style.aactor_style.required_suffix =
cpp_naming_style.aactor_style.word_separator =
cpp_naming_style.swidget_style.capitalization = pascal_case
cpp_naming_style.swidget_style.required_prefix = S
cpp_naming_style.swidget_style.required_suffix =
cpp_naming_style.swidget_style.word_separator =
cpp_naming_style.boolean_style.capitalization = pascal_case
cpp_naming_style.boolean_style.required_prefix = b
cpp_naming_style.boolean_style.required_suffix =
cpp_naming_style.boolean_style.word_separator =

View File

@ -68,6 +68,7 @@ bCaptureMouseOnLaunch=True
bEnableLegacyInputScales=True
bEnableMotionControls=True
bFilterInputByPlatformUser=False
bEnableInputDeviceSubsystem=True
bShouldFlushPressedKeysOnViewportFocusLost=True
bEnableDynamicComponentInputBinding=True
bAlwaysShowTouchInterface=False
@ -90,6 +91,12 @@ DoubleClickTime=0.200000
+ActionMappings=(ActionName="Reload",bShift=False,bCtrl=False,bAlt=False,bCmd=False,Key=Gamepad_FaceButton_Left)
+ActionMappings=(ActionName="Crouch",bShift=False,bCtrl=False,bAlt=False,bCmd=False,Key=C)
+ActionMappings=(ActionName="Crouch",bShift=False,bCtrl=False,bAlt=False,bCmd=False,Key=Gamepad_RightThumbstick)
+ActionMappings=(ActionName="FKey",bShift=False,bCtrl=False,bAlt=False,bCmd=False,Key=F)
+ActionMappings=(ActionName="1Key",bShift=False,bCtrl=False,bAlt=False,bCmd=False,Key=One)
+ActionMappings=(ActionName="2Key",bShift=False,bCtrl=False,bAlt=False,bCmd=False,Key=Two)
+ActionMappings=(ActionName="3Key",bShift=False,bCtrl=False,bAlt=False,bCmd=False,Key=Three)
+ActionMappings=(ActionName="4Key",bShift=False,bCtrl=False,bAlt=False,bCmd=False,Key=Four)
+ActionMappings=(ActionName="5Key",bShift=False,bCtrl=False,bAlt=False,bCmd=False,Key=Five)
+AxisMappings=(AxisName="MoveForward",Scale=1.000000,Key=W)
+AxisMappings=(AxisName="MoveForward",Scale=-1.000000,Key=S)
+AxisMappings=(AxisName="MoveForward",Scale=1.000000,Key=Gamepad_LeftY)

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -41,6 +41,14 @@
{
"Name": "BlueprintSnapNodes",
"Enabled": true
},
{
"Name": "VisualStudioTools",
"Enabled": true,
"MarketplaceURL": "com.epicgames.launcher://ue/marketplace/product/362651520df94e4fa65492dbcba44ae2",
"SupportedTargetPlatforms": [
"Win64"
]
}
]
}

View File

@ -31,10 +31,11 @@ AItem::AItem() :
MaterialIndex(0),
bCanChangeCustomDepth(true),
// Dynamic Material Parameters
PulseCurveTime(5.f),
GlowAmount(150.f),
FresnelExponent(3.f),
FresnelReflectFraction(4.f),
PulseCurveTime(5.f)
SlotIndex(0)
{
// Set this actor to call Tick() every frame. You can turn this off to improve performance if you don't need it.
PrimaryActorTick.bCanEverTick = true;
@ -77,6 +78,17 @@ void AItem::BeginPlay()
InitializeCustomDepth();
StartPulseTimer();
// We wait a small delay to make sure all actors are spawned,
// then we set a small sphere radius and reset it again,
// this generates any overlap events that weren't accounted for on spawn
FTimerHandle TimerHandle;
GetWorldTimerManager().SetTimer(TimerHandle, [&]()
{
AreaSphereRadius = AreaSphere->GetUnscaledSphereRadius();
AreaSphere->SetSphereRadius(1.f);
AreaSphere->SetSphereRadius(AreaSphereRadius);
}, 0.5, false);
}
void AItem::setActiveStars()
@ -425,6 +437,24 @@ void AItem::SetItemProperties(EItemState State)
AreaSphere->SetCollisionResponseToAllChannels(ECollisionResponse::ECR_Ignore);
AreaSphere->SetCollisionEnabled(ECollisionEnabled::NoCollision);
// Set CollisionBox properties
CollisionBox->SetCollisionResponseToAllChannels(ECollisionResponse::ECR_Ignore);
CollisionBox->SetCollisionEnabled(ECollisionEnabled::NoCollision);
break;
case EItemState::EIS_PickedUp:
PickupWidget->SetVisibility(false);
// Set Mesh properties
ItemMesh->SetSimulatePhysics(false);
ItemMesh->SetEnableGravity(false);
ItemMesh->SetVisibility(false);
ItemMesh->SetCollisionResponseToAllChannels(ECollisionResponse::ECR_Ignore);
ItemMesh->SetCollisionEnabled(ECollisionEnabled::NoCollision);
// Set AreaSphere properties
AreaSphere->SetCollisionResponseToAllChannels(ECollisionResponse::ECR_Ignore);
AreaSphere->SetCollisionEnabled(ECollisionEnabled::NoCollision);
// Set CollisionBox properties
CollisionBox->SetCollisionResponseToAllChannels(ECollisionResponse::ECR_Ignore);
CollisionBox->SetCollisionEnabled(ECollisionEnabled::NoCollision);
@ -477,7 +507,6 @@ void AItem::FinishInterping()
// Subtract 1 from the Item Count of the interp location struct
Character->IncrementInterpLocItemCount(InterpLocIndex, -1);
Character->GetPickupItem(this);
SetItemState(EItemState::EIS_PickedUp);
}
// Set scale back to normal
SetActorScale3D(FVector(1.f));

View File

@ -120,6 +120,9 @@ private:
UPROPERTY(VisibleAnywhere, BlueprintReadonly, Category = "Item Properties", meta = (AllowPrivateAccess = true))
USkeletalMeshComponent* ItemMesh;
UPROPERTY(VisibleAnywhere, BlueprintReadonly, Category = "Item Properties", meta = (AllowPrivateAccess = true))
float AreaSphereRadius;
/* The name which appears on the Pickup Widget */
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Item Properties", meta = (AllowPrivateAccess = true))
FString ItemName;
@ -233,6 +236,22 @@ private:
UPROPERTY(VisibleAnywhere, Category = "Item Properties", meta = (AllowPrivateAccess = true))
float FresnelReflectFraction;
/** Background for this item in the inventory */
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = Inventory, meta = (AllowPrivateAccess = true))
UTexture2D* IconBackground;
/** Icon for this item in the inventory */
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = Inventory, meta = (AllowPrivateAccess = true))
UTexture2D* IconItem;
/** Icon for this item's ammo in the inventory */
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = Inventory, meta = (AllowPrivateAccess = true))
UTexture2D* IconAmmo;
/** Slot in the inventory array */
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = Inventory, meta = (AllowPrivateAccess = true))
int32 SlotIndex;
public:
FORCEINLINE UWidgetComponent* GetPickupWidget() const { return PickupWidget; }
@ -250,7 +269,10 @@ public:
FORCEINLINE USoundCue* GetEquipSound() const { return EquipSound; }
FORCEINLINE int32 GetItemCount() const { return ItemCount; }
FORCEINLINE int32 GetSlotIndex() const { return SlotIndex; }
FORCEINLINE void SetSlotIndex(int32 Index) { SlotIndex = Index; }
/** Called from the AShooterCharacter class */
void StartItemCurve(AShooterCharacter* Char);

View File

@ -17,6 +17,8 @@ UShooterAnimInstance::UShooterAnimInstance() :
TIPCharacterYaw(0.f),
TIPCharacterYawLastFrame(0.f),
RootYawOffset(0.f),
RotationCurve(0.f),
RotationCurveLastFrame(0.f),
Pitch(0.f),
bReloading(false),
OffsetState(EOffsetState::EOS_Hip),
@ -87,6 +89,11 @@ void UShooterAnimInstance::NativeInitializeAnimation()
Super::NativeInitializeAnimation();
ShooterCharacter = Cast<AShooterCharacter>(TryGetPawnOwner());
if (!ShooterCharacter) return;
TIPCharacterYaw = ShooterCharacter->GetActorRotation().Yaw;
TIPCharacterYawLastFrame = TIPCharacterYaw;
}
void UShooterAnimInstance::TurnInPlace()

View File

@ -150,6 +150,8 @@ void AShooterCharacter::BeginPlay()
// Spawn the default weapon and equip it
EquipWeapon(SpawnDefaultWeapon());
Inventory.Add(EquippedWeapon);
EquippedWeapon->SetSlotIndex(0);
InitializeAmmoMap();
@ -175,26 +177,25 @@ void AShooterCharacter::SetupPlayerInputComponent(UInputComponent* PlayerInputCo
PlayerInputComponent->BindAction("Jump", IE_Pressed, this, &AShooterCharacter::Jump);
PlayerInputComponent->BindAction("Jump", IE_Released, this, &ACharacter::StopJumping);
PlayerInputComponent->BindAction("FireButton", IE_Pressed, this,
&AShooterCharacter::FireButtonPressed);
PlayerInputComponent->BindAction("FireButton", IE_Released, this,
&AShooterCharacter::FireButtonReleased);
PlayerInputComponent->BindAction("FireButton", IE_Pressed, this, &AShooterCharacter::FireButtonPressed);
PlayerInputComponent->BindAction("FireButton", IE_Released, this, &AShooterCharacter::FireButtonReleased);
PlayerInputComponent->BindAction("Aiming", IE_Pressed, this,
&AShooterCharacter::AimingButtonPressed);
PlayerInputComponent->BindAction("Aiming", IE_Released, this,
&AShooterCharacter::AimingButtonReleased);
PlayerInputComponent->BindAction("Aiming", IE_Pressed, this, &AShooterCharacter::AimingButtonPressed);
PlayerInputComponent->BindAction("Aiming", IE_Released, this, &AShooterCharacter::AimingButtonReleased);
PlayerInputComponent->BindAction("Select", IE_Pressed, this,
&AShooterCharacter::SelectButtonPressed);
PlayerInputComponent->BindAction("Select", IE_Released, this,
&AShooterCharacter::SelectButtonReleased);
PlayerInputComponent->BindAction("Select", IE_Pressed, this, &AShooterCharacter::SelectButtonPressed);
PlayerInputComponent->BindAction("Select", IE_Released, this, &AShooterCharacter::SelectButtonReleased);
PlayerInputComponent->BindAction("Reload", IE_Pressed, this,
&AShooterCharacter::ReloadButtonPressed);
PlayerInputComponent->BindAction("Reload", IE_Pressed, this, &AShooterCharacter::ReloadButtonPressed);
PlayerInputComponent->BindAction("Crouch", IE_Pressed, this,
&AShooterCharacter::CrouchButtonPressed);
PlayerInputComponent->BindAction("Crouch", IE_Pressed, this, &AShooterCharacter::CrouchButtonPressed);
PlayerInputComponent->BindAction("FKey", IE_Pressed, this, &AShooterCharacter::KeyFPressed);
PlayerInputComponent->BindAction("1Key", IE_Pressed, this, &AShooterCharacter::Key1Pressed);
PlayerInputComponent->BindAction("2Key", IE_Pressed, this, &AShooterCharacter::Key2Pressed);
PlayerInputComponent->BindAction("3Key", IE_Pressed, this, &AShooterCharacter::Key3Pressed);
PlayerInputComponent->BindAction("4Key", IE_Pressed, this, &AShooterCharacter::Key4Pressed);
PlayerInputComponent->BindAction("5Key", IE_Pressed, this, &AShooterCharacter::Key5Pressed);
}
// Called every frame
@ -373,6 +374,56 @@ void AShooterCharacter::ResetEquipSoundTimer()
bShouldPlayEquipSound = true;
}
void AShooterCharacter::KeyFPressed()
{
if (EquippedWeapon->GetSlotIndex() == 0) return;
ExchangeInventoryItems(EquippedWeapon->GetSlotIndex(), 0);
}
void AShooterCharacter::Key1Pressed()
{
if (EquippedWeapon->GetSlotIndex() == 1) return;
ExchangeInventoryItems(EquippedWeapon->GetSlotIndex(), 1);
}
void AShooterCharacter::Key2Pressed()
{
if (EquippedWeapon->GetSlotIndex() == 2) return;
ExchangeInventoryItems(EquippedWeapon->GetSlotIndex(), 2);
}
void AShooterCharacter::Key3Pressed()
{
if (EquippedWeapon->GetSlotIndex() == 3) return;
ExchangeInventoryItems(EquippedWeapon->GetSlotIndex(), 3);
}
void AShooterCharacter::Key4Pressed()
{
if (EquippedWeapon->GetSlotIndex() == 4) return;
ExchangeInventoryItems(EquippedWeapon->GetSlotIndex(), 4);
}
void AShooterCharacter::Key5Pressed()
{
if (EquippedWeapon->GetSlotIndex() == 5) return;
ExchangeInventoryItems(EquippedWeapon->GetSlotIndex(), 5);
}
void AShooterCharacter::ExchangeInventoryItems(int32 CurrentItemIndex, int32 NewItemIndex)
{
if (CurrentItemIndex == NewItemIndex || NewItemIndex >= Inventory.Num()) return;
auto OldEquippedWeapon = EquippedWeapon;
auto NewWeapon = Cast<AWeapon>(Inventory[NewItemIndex]);
EquipWeapon(NewWeapon);
OldEquippedWeapon->SetItemState(EItemState::EIS_PickedUp);
NewWeapon->SetItemState(EItemState::EIS_Equipped);
}
int32 AShooterCharacter::GetInterpLocationIndex()
{
int32 LowestIndex = 1;
@ -695,20 +746,27 @@ void AShooterCharacter::TraceForItems()
FHitResult ItemTraceResult;
FVector HitLocation;
TraceUnderCrosshairs(ItemTraceResult, HitLocation);
TraceHitItem = Cast<AItem>(ItemTraceResult.GetActor());
if (ItemTraceResult.bBlockingHit)
if (TraceHitItem && ItemTraceResult.bBlockingHit)
{
// If an item was being looked at from last frame, we should hide its widget
SetItemPickupWidgetVisibility(TraceHitItem, false);
if (OldTraceHitItem != TraceHitItem)
SetItemPickupWidgetVisibility(OldTraceHitItem, false);
// Show the new item's widget
TraceHitItem = Cast<AItem>(ItemTraceResult.GetActor());
OldTraceHitItem = TraceHitItem;
if (TraceHitItem && TraceHitItem->GetItemState() == EItemState::EIS_EquipInterping)
TraceHitItem = nullptr;
SetItemPickupWidgetVisibility(TraceHitItem, true);
}
else if (TraceHitItem) // Hide widget for the item from last frame
else if (OldTraceHitItem) // Hide widget for the item from last frame
{
SetItemPickupWidgetVisibility(TraceHitItem, false);
TraceHitItem = nullptr;
SetItemPickupWidgetVisibility(OldTraceHitItem, false);
OldTraceHitItem = nullptr;
}
}
else if (TraceHitItem) // Hide widget for the item from last frame
@ -751,6 +809,16 @@ void AShooterCharacter::EquipWeapon(AWeapon* WeaponToEquip)
// Attach the weapon to the hand socket RightHandSocket
HandSocket->AttachActor(WeaponToEquip, GetMesh());
if (EquippedWeapon == nullptr)
{
// -1 == no EquippedWeapon yet. No need to reverse the icon animation
EquipItemDelegate.Broadcast(-1, WeaponToEquip->GetSlotIndex());
}
else
{
EquipItemDelegate.Broadcast(EquippedWeapon->GetSlotIndex(), WeaponToEquip->GetSlotIndex());
}
EquippedWeapon = WeaponToEquip;
EquippedWeapon->SetItemState(EItemState::EIS_Equipped);
@ -776,6 +844,7 @@ void AShooterCharacter::SelectButtonPressed()
if (TraceHitItem)
{
TraceHitItem->StartItemCurve(this);
TraceHitItem = nullptr;
}
}
@ -785,6 +854,12 @@ void AShooterCharacter::SelectButtonReleased()
void AShooterCharacter::SwapWeapon(AWeapon* WeaponToSwap)
{
if (EquippedWeapon->GetSlotIndex() <= Inventory.Num() - 1)
{
Inventory[EquippedWeapon->GetSlotIndex()] = WeaponToSwap;
WeaponToSwap->SetSlotIndex(EquippedWeapon->GetSlotIndex());
}
DropWeapon();
EquipWeapon(WeaponToSwap);
}
@ -840,7 +915,16 @@ void AShooterCharacter::GetPickupItem(AItem* Item)
if (Weapon)
{
SwapWeapon(Weapon);
if (Inventory.Num() < INVENTORY_CAPACITY)
{
Weapon->SetSlotIndex(Inventory.Num());
Inventory.Add(Weapon);
Weapon->SetItemState(EItemState::EIS_PickedUp);
}
else // Inventory is full! Swap with EquippedWeapon
{
SwapWeapon(Weapon);
}
}
auto Ammo = Cast<AAmmo>(Item);

View File

@ -42,6 +42,9 @@ struct FInterpLocation
int32 ItemCount;
};
DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FEquipItemDelegate, int32, CurrentSlotIndex, int32, NewSlotIndex);
UCLASS()
class SHOOTER_API AShooterCharacter : public ACharacter
{
@ -194,6 +197,15 @@ protected:
void ResetPickupSoundTimer();
void ResetEquipSoundTimer();
void KeyFPressed();
void Key1Pressed();
void Key2Pressed();
void Key3Pressed();
void Key4Pressed();
void Key5Pressed();
void ExchangeInventoryItems(int32 CurrentItemIndex, int32 NewItemIndex);
private:
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = Camera, meta = (AllowPrivateAccess = true))
USpringArmComponent* CameraBoom;
@ -338,6 +350,10 @@ private:
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = Combat, meta = (AllowPrivateAccess = true))
AItem* TraceHitItem;
/** Item hit by our trace in TraceForItems() from last frame (could be null) */
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = Combat, meta = (AllowPrivateAccess = true))
AItem* OldTraceHitItem;
/** Distance outward from the camera for the interp destination */
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = Items, meta = (AllowPrivateAccess = true))
float CameraInterpDistance;
@ -443,6 +459,16 @@ private:
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = Items, meta = (AllowPrivateAccess = true))
float EquipSoundResetTime;
/** An array of AItems for our inventory */
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = Inventory, meta = (AllowPrivateAccess = true))
TArray<AItem*> Inventory;
const int32 INVENTORY_CAPACITY{ 6 };
/** Delegate for sending slot information to InventoryBar when equipping */
UPROPERTY(BlueprintAssignable, Category = Delegates, meta = (AllowPrivateAccess = true))
FEquipItemDelegate EquipItemDelegate;
public:
/* Returns CameraBoom SubObject */
FORCEINLINE USpringArmComponent* GetCameraBoom() const { return CameraBoom; }

View File

@ -8,8 +8,8 @@ public class ShooterEditorTarget : TargetRules
public ShooterEditorTarget( TargetInfo Target) : base(Target)
{
Type = TargetType.Editor;
DefaultBuildSettings = BuildSettingsVersion.V2;
IncludeOrderVersion = EngineIncludeOrderVersion.Unreal5_2;
DefaultBuildSettings = BuildSettingsVersion.V4;
IncludeOrderVersion = EngineIncludeOrderVersion.Latest;
ExtraModuleNames.AddRange( new string[] { "Shooter" } );
}
}

8
shadertoolsconfig.json Normal file
View File

@ -0,0 +1,8 @@
{
"hlsl.preprocessorDefinitions": {
},
" hlsl.additionalIncludeDirectories": [
],
"hlsl.virtualDirectoryMappings": {
}
}