Section 11: Multiple Weapon Types - Lecture 229

Stop Aiming when exchanging weapons

* Also fixed a case where firing wasn't resumed after reloading when the fire button was still pressed, same for equipping.
This commit is contained in:
charnet3d 2024-01-19 02:12:26 +01:00
parent d16ac0cef1
commit 898f7e60e3
17 changed files with 158 additions and 24 deletions

BIN
Content/_Game/Character/Animations/AimingPose_AR.uasset (Stored with Git LFS) Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
Content/_Game/Curves/SlideDisplacementCurve.uasset (Stored with Git LFS) Normal file

Binary file not shown.

Binary file not shown.

BIN
Content/_Game/Maps/DefaultMap.umap (Stored with Git LFS)

Binary file not shown.

Binary file not shown.

BIN
Content/_Game/Materials/PistolMaterials/M_Pistol_Mat.uasset (Stored with Git LFS) Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
Content/_Game/Weapons/BaseWeapon/PistolAnimBP.uasset (Stored with Git LFS) Normal file

Binary file not shown.

View File

@ -288,7 +288,7 @@ void AShooterCharacter::LookUp(float Value)
void AShooterCharacter::AimingButtonPressed() void AShooterCharacter::AimingButtonPressed()
{ {
bAimingButtonPressed = true; bAimingButtonPressed = true;
if (CombatState != ECombatState::ECS_Reloading) if (CombatState != ECombatState::ECS_Reloading && CombatState != ECombatState::ECS_Equipping)
{ {
Aim(); Aim();
} }
@ -420,6 +420,11 @@ void AShooterCharacter::ExchangeInventoryItems(int32 CurrentItemIndex, int32 New
|| (CombatState != ECombatState::ECS_Unoccupied && CombatState != ECombatState::ECS_Equipping)) || (CombatState != ECombatState::ECS_Unoccupied && CombatState != ECombatState::ECS_Equipping))
return; return;
if (bAiming)
{
StopAiming();
}
auto OldEquippedWeapon = EquippedWeapon; auto OldEquippedWeapon = EquippedWeapon;
auto NewWeapon = Cast<AWeapon>(Inventory[NewItemIndex]); auto NewWeapon = Cast<AWeapon>(Inventory[NewItemIndex]);
@ -572,6 +577,12 @@ void AShooterCharacter::FireWeapon()
EquippedWeapon->DecrementAmmo(); EquippedWeapon->DecrementAmmo();
StartCrosshairBulletFire(); StartCrosshairBulletFire();
StartFireTimer(); StartFireTimer();
if (EquippedWeapon->GetWeaponType() == EWeaponType::EWT_Pistol)
{
// Start moving slide timer
EquippedWeapon->StartSlideTimer();
}
} }
void AShooterCharacter::StartFireTimer() void AShooterCharacter::StartFireTimer()
@ -585,10 +596,13 @@ void AShooterCharacter::StartFireTimer()
void AShooterCharacter::AutoFireReset() void AShooterCharacter::AutoFireReset()
{ {
if (!EquippedWeapon) return;
CombatState = ECombatState::ECS_Unoccupied; CombatState = ECombatState::ECS_Unoccupied;
if (WeaponHasAmmo()) if (WeaponHasAmmo())
{ {
if (bFireButtonPressed) if (bFireButtonPressed && EquippedWeapon->GetAutomatic())
{ {
FireWeapon(); FireWeapon();
} }
@ -1046,15 +1060,10 @@ void AShooterCharacter::ReloadWeapon()
void AShooterCharacter::FinishReloading() void AShooterCharacter::FinishReloading()
{ {
CombatState = ECombatState::ECS_Unoccupied;
if (bAimingButtonPressed)
{
Aim();
}
if (!EquippedWeapon) return; if (!EquippedWeapon) return;
CombatState = ECombatState::ECS_Unoccupied;
if (const auto AmmoType { EquippedWeapon->GetAmmoType() }; AmmoMap.Contains(AmmoType)) if (const auto AmmoType { EquippedWeapon->GetAmmoType() }; AmmoMap.Contains(AmmoType))
{ {
int32 CarriedAmmo = AmmoMap[AmmoType]; int32 CarriedAmmo = AmmoMap[AmmoType];
@ -1075,11 +1084,37 @@ void AShooterCharacter::FinishReloading()
AmmoMap.Add(AmmoType, CarriedAmmo); AmmoMap.Add(AmmoType, CarriedAmmo);
} }
} }
if (bAimingButtonPressed)
{
Aim();
}
if (WeaponHasAmmo())
{
if (bFireButtonPressed && EquippedWeapon->GetAutomatic())
{
FireWeapon();
}
}
} }
void AShooterCharacter::FinishEquipping() void AShooterCharacter::FinishEquipping()
{ {
CombatState = ECombatState::ECS_Unoccupied; CombatState = ECombatState::ECS_Unoccupied;
if (bAimingButtonPressed)
{
Aim();
}
if (WeaponHasAmmo())
{
if (bFireButtonPressed && EquippedWeapon->GetAutomatic())
{
FireWeapon();
}
}
} }
bool AShooterCharacter::CarryingAmmo() bool AShooterCharacter::CarryingAmmo()
@ -1123,6 +1158,7 @@ void AShooterCharacter::CrouchButtonPressed()
{ {
bCrouching = !bCrouching; bCrouching = !bCrouching;
} }
if (bCrouching) if (bCrouching)
{ {
GetCharacterMovement()->MaxWalkSpeed = CrouchMovementSpeed; GetCharacterMovement()->MaxWalkSpeed = CrouchMovementSpeed;

View File

@ -11,7 +11,13 @@ AWeapon::AWeapon() :
WeaponType(EWeaponType::EWT_SubmachineGun), WeaponType(EWeaponType::EWT_SubmachineGun),
AmmoType(EAmmoType::EAT_9mm), AmmoType(EAmmoType::EAT_9mm),
ReloadMontageSection(FName(TEXT("Reload SMG"))), ReloadMontageSection(FName(TEXT("Reload SMG"))),
ClipBoneName(TEXT("smg_clip")) ClipBoneName(TEXT("smg_clip")),
SlideDisplacement(0.f),
SlideDisplacementTime(0.2f),
bMovingSlide(false),
MaxSlideDisplacement(4.f),
MaxRecoilRotation(20.f),
bAutomatic(true)
{ {
PrimaryActorTick.bCanEverTick = true; PrimaryActorTick.bCanEverTick = true;
} }
@ -27,6 +33,9 @@ void AWeapon::Tick(float DeltaTime)
GetItemMesh()->SetWorldRotation(MeshRotation, false, GetItemMesh()->SetWorldRotation(MeshRotation, false,
nullptr, ETeleportType::TeleportPhysics); nullptr, ETeleportType::TeleportPhysics);
} }
// Update slide on pistol
UpdateSlideDisplacement();
} }
void AWeapon::ThrowWeapon() void AWeapon::ThrowWeapon()
@ -61,6 +70,12 @@ void AWeapon::DecrementAmmo()
--Ammo; --Ammo;
} }
void AWeapon::StartSlideTimer()
{
bMovingSlide = true;
GetWorldTimerManager().SetTimer(SlideTimer, this, &AWeapon::FinishMovingSlide, SlideDisplacementTime);
}
void AWeapon::ReloadAmmo(int32 Amount) void AWeapon::ReloadAmmo(int32 Amount)
{ {
checkf(Ammo + Amount <= MagazineCapacity, TEXT("Attempted to reload with more than the magazine capacity!")) checkf(Ammo + Amount <= MagazineCapacity, TEXT("Attempted to reload with more than the magazine capacity!"))
@ -139,6 +154,8 @@ void AWeapon::OnConstruction(const FTransform& Transform)
{ {
GetItemMesh()->HideBoneByName(BoneToHide, EPhysBodyOp::PBO_None); GetItemMesh()->HideBoneByName(BoneToHide, EPhysBodyOp::PBO_None);
} }
bAutomatic = WeaponDataRow->bAutomatic;
} }
if (GetMaterialInstance()) if (GetMaterialInstance())
@ -161,3 +178,19 @@ void AWeapon::BeginPlay()
GetItemMesh()->HideBoneByName(BoneToHide, EPhysBodyOp::PBO_None); GetItemMesh()->HideBoneByName(BoneToHide, EPhysBodyOp::PBO_None);
} }
} }
void AWeapon::FinishMovingSlide()
{
bMovingSlide = false;
}
void AWeapon::UpdateSlideDisplacement()
{
if (!SlideDisplacementCurve && !bMovingSlide) return;
const float ElapsedTime{ GetWorldTimerManager().GetTimerElapsed(SlideTimer) };
const float CurveValue{ SlideDisplacementCurve->GetFloatValue(ElapsedTime) };
SlideDisplacement = CurveValue * MaxSlideDisplacement;
RecoilRotation = CurveValue * MaxRecoilRotation;
}

View File

@ -87,6 +87,9 @@ struct FWeaponDataTable : public FTableRowBase
UPROPERTY(EditAnywhere, BlueprintReadWrite) UPROPERTY(EditAnywhere, BlueprintReadWrite)
FName BoneToHide; FName BoneToHide;
UPROPERTY(EditAnywhere, BlueprintReadWrite)
bool bAutomatic;
}; };
/** /**
@ -109,6 +112,9 @@ protected:
virtual void BeginPlay() override; virtual void BeginPlay() override;
void FinishMovingSlide();
void UpdateSlideDisplacement();
private: private:
FTimerHandle ThrowWeaponTimer; FTimerHandle ThrowWeaponTimer;
float ThrowWeaponTime; float ThrowWeaponTime;
@ -180,6 +186,41 @@ private:
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = DataTable, meta = (AllowPrivateAccess = "true")) UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = DataTable, meta = (AllowPrivateAccess = "true"))
FName BoneToHide; FName BoneToHide;
/** Amount that the slide is pushed back during pistol fire */
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = Pistol, meta = (AllowPrivateAccess = "true"))
float SlideDisplacement;
/** Curve for the slide displacement */
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = Pistol, meta = (AllowPrivateAccess = "true"))
UCurveFloat* SlideDisplacementCurve;
/** Timer handle for updating SlideDisplacement */
FTimerHandle SlideTimer;
/** Time for displacing the slide during pistol fire */
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = Pistol, meta = (AllowPrivateAccess = "true"))
float SlideDisplacementTime;
/** True when moving the pistol slide */
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = Pistol, meta = (AllowPrivateAccess = "true"))
bool bMovingSlide;
/** Max distance for the slide on the pistol */
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = Pistol, meta = (AllowPrivateAccess = "true"))
float MaxSlideDisplacement;
/** Max rotation for pistol recoil */
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = Pistol, meta = (AllowPrivateAccess = "true"))
float MaxRecoilRotation;
/** Amount that the pistol will rotate during pistol fire */
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = Pistol, meta = (AllowPrivateAccess = "true"))
float RecoilRotation;
/** True for auto gunfire */
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Weapon Properties", meta = (AllowPrivateAccess = "true"))
bool bAutomatic;
public: public:
/** Adds an impulse to the weapon */ /** Adds an impulse to the weapon */
void ThrowWeapon(); void ThrowWeapon();
@ -197,6 +238,9 @@ public:
FORCEINLINE float GetAutoFireRate() const { return AutoFireRate; } FORCEINLINE float GetAutoFireRate() const { return AutoFireRate; }
FORCEINLINE UParticleSystem* GetMuzzleFlash() const { return MuzzleFlash; } FORCEINLINE UParticleSystem* GetMuzzleFlash() const { return MuzzleFlash; }
FORCEINLINE USoundCue* GetFireSound() const { return FireSound; } FORCEINLINE USoundCue* GetFireSound() const { return FireSound; }
FORCEINLINE bool GetAutomatic() const { return bAutomatic; }
void StartSlideTimer();
void ReloadAmmo(int32 Amount); void ReloadAmmo(int32 Amount);