feat: gun attaching to socket

Added FirstPersonArm from Unreal FPS template.
Modified ShootingComponent for using socket in character.
This commit is contained in:
Kubson96 2025-04-22 23:28:59 +02:00
parent 797ec44907
commit 672b19326b
51 changed files with 88 additions and 101 deletions

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -5,18 +5,12 @@
#include "Items/ThrowableBase.h" #include "Items/ThrowableBase.h"
// Sets default values for this component's properties
UShootingComponent::UShootingComponent() UShootingComponent::UShootingComponent()
{ {
// Set this component to be initialized when the game starts, and to be ticked every frame. You can turn these features
// off to improve performance if you don't need them.
PrimaryComponentTick.bCanEverTick = true; PrimaryComponentTick.bCanEverTick = true;
// ...
} }
// Called when the game starts
void UShootingComponent::BeginPlay() void UShootingComponent::BeginPlay()
{ {
Super::BeginPlay(); Super::BeginPlay();
@ -30,27 +24,27 @@ void UShootingComponent::BeginPlay()
void UShootingComponent::Shoot() void UShootingComponent::Shoot()
{ {
if (!IsValid(CurrentGun) || CurrentGun->CurrentAmmo == 0 || bIsReloading || !bCanShoot) return; if (!IsValid(CurrentGunBase) || CurrentGunBase->CurrentAmmo == 0 || bIsReloading || !bCanShoot) return;
bCanShoot = false; bCanShoot = false;
CurrentGun->CurrentAmmo--; CurrentGunBase->CurrentAmmo--;
AActor* HitActor = ExecuteLineTrace(CurrentGun->MaxRange); AActor* HitActor = ExecuteLineTrace(CurrentGunBase->MaxRange);
if (HitActor) if (HitActor)
{ {
IDamageable::Execute_TakeDamage(HitActor, CurrentGun->DamageValue); IDamageable::Execute_TakeDamage(HitActor, CurrentGunBase->DamageValue);
UE_LOG(LogTemp, Display, TEXT("Shoot. Ammo: %d/%d"), CurrentGun->CurrentAmmo, CurrentGun->MaxAmmo); // Docelowo tutaj wywołanie UI aktualizujące stan ammo UE_LOG(LogTemp, Display, TEXT("Shoot. Ammo: %d/%d"), CurrentGunBase->CurrentAmmo, CurrentGunBase->MaxAmmo); // Docelowo tutaj wywołanie UI aktualizujące stan ammo
} }
PlayerCharacter->GetWorldTimerManager().SetTimer(ShootCooldownTimer, this, &UShootingComponent::ResetFireCooldown, CurrentGun->FireRateCooldown, false); PlayerCharacter->GetWorldTimerManager().SetTimer(ShootCooldownTimer, this, &UShootingComponent::ResetFireCooldown, CurrentGunBase->FireRateCooldown, false);
// Odrzut // Odrzut
float RecoilPitch = FMath::RandRange(-1.0f, -0.5f); float RecoilPitch = FMath::RandRange(-1.0f, -0.5f);
float RecoilYaw = FMath::RandRange(-0.5f, 0.5f); float RecoilYaw = FMath::RandRange(-0.5f, 0.5f);
PlayerCharacter->AddControllerPitchInput(RecoilPitch * CurrentGun->RecoilForceMultiplier); PlayerCharacter->AddControllerPitchInput(RecoilPitch * CurrentGunBase->RecoilForceMultiplier);
PlayerCharacter->AddControllerYawInput(RecoilYaw * CurrentGun->RecoilForceMultiplier); PlayerCharacter->AddControllerYawInput(RecoilYaw * CurrentGunBase->RecoilForceMultiplier);
} }
void UShootingComponent::MeleAttack() void UShootingComponent::MeleAttack()
@ -88,30 +82,30 @@ AActor* UShootingComponent::ExecuteLineTrace(float LineRange)
void UShootingComponent::Reload() void UShootingComponent::Reload()
{ {
if (!IsValid(CurrentGun) || bIsReloading) return; if (!IsValid(CurrentGunBase) || bIsReloading) return;
bIsReloading = true; bIsReloading = true;
StopAiming(); StopAiming();
CurrentGun->CurrentAmmo = CurrentGun->MaxAmmo; CurrentGunBase->CurrentAmmo = CurrentGunBase->MaxAmmo;
PlayerCharacter->GetWorldTimerManager().SetTimer(ReloadTimer, this, &UShootingComponent::ReloadCompleted, CurrentGun->ReloadTime, false); PlayerCharacter->GetWorldTimerManager().SetTimer(ReloadTimer, this, &UShootingComponent::ReloadCompleted, CurrentGunBase->ReloadTime, false);
UE_LOG(LogTemp, Display, TEXT("Reloaded. Ammo: %d/%d"), CurrentGun->CurrentAmmo, CurrentGun->MaxAmmo); // Docelowo tutaj wywo<77>anie UI aktualizuj<75>ce stan ammo UE_LOG(LogTemp, Display, TEXT("Reloaded. Ammo: %d/%d"), CurrentGunBase->CurrentAmmo, CurrentGunBase->MaxAmmo); // Docelowo tutaj wywo<77>anie UI aktualizuj<75>ce stan ammo
} }
bool UShootingComponent::AddAmmo(EAmmoType CheckingAmmo, int AddValue) bool UShootingComponent::AddAmmo(EAmmoType CheckingAmmo, int AddValue)
{ {
if (IsValid(CurrentGun)) if (IsValid(CurrentGunBase))
{ {
// Adding ammo only if type is compatible // Adding ammo only if type is compatible
if (CurrentGun->AmmoType == CheckingAmmo) if (CurrentGunBase->AmmoType == CheckingAmmo)
{ {
CurrentGun->AddAmmo(AddValue); CurrentGunBase->AddAmmo(AddValue);
return true; return true;
} }
if (SecondaryGun->AmmoType == CheckingAmmo) if (SecondaryGunBase->AmmoType == CheckingAmmo)
{ {
SecondaryGun->AddAmmo(AddValue); SecondaryGunBase->AddAmmo(AddValue);
return true; return true;
} }
} }
@ -119,27 +113,31 @@ bool UShootingComponent::AddAmmo(EAmmoType CheckingAmmo, int AddValue)
return false; return false;
} }
void UShootingComponent::PickUpGun(AGunBase* gunItem) void UShootingComponent::PickUpGun(UStaticMeshComponent* PickedGunMesh)
{ {
if (IsValid(CurrentGun) && IsValid(SecondaryGun)) if (IsValid(CurrentGunBase) && IsValid(SecondaryGunBase))
DropGun(); DropGun();
AGunBase* NewGun = NewObject<AGunBase>(); FAttachmentTransformRules AttachmentRules(EAttachmentRule::SnapToTarget, EAttachmentRule::SnapToTarget,
EAttachmentRule::KeepRelative, true);
NewGun->MaxRange = gunItem->MaxRange; PickedGunMesh->SetSimulatePhysics(false);
NewGun->DamageValue = gunItem->DamageValue; PickedGunMesh->SetCastShadow(false);
NewGun->FireRateCooldown = gunItem->FireRateCooldown; PickedGunMesh->AttachToComponent(PlayerCharacter->Weapon, AttachmentRules, FName(TEXT("GripPoint")));
NewGun->RecoilForceMultiplier = gunItem->RecoilForceMultiplier;
NewGun->ReloadTime = gunItem->ReloadTime;
NewGun->AmmoType = gunItem->AmmoType;
NewGun->CurrentAmmo = gunItem->CurrentAmmo;
NewGun->MaxAmmo = gunItem->MaxAmmo;
NewGun->SceneItemClass = gunItem->GetClass();
if (IsValid(CurrentGun)) AGunBase* PickedGunBase = Cast<AGunBase>(PickedGunMesh->GetOwner());
SecondaryGun = NewGun;
if (IsValid(CurrentGunBase))
{
SecondaryGunBase = PickedGunBase;
SecondaryGunMesh = PickedGunMesh;
SecondaryGunMesh->SetVisibility(false);
}
else else
CurrentGun = NewGun; {
CurrentGunBase = PickedGunBase;
CurrentGunMesh = PickedGunMesh;
}
} }
bool UShootingComponent::PickUpThrow(AThrowableBase* ThrowItem) bool UShootingComponent::PickUpThrow(AThrowableBase* ThrowItem)
@ -176,33 +174,20 @@ bool UShootingComponent::PickUpThrow(AThrowableBase* ThrowItem)
void UShootingComponent::DropGun() void UShootingComponent::DropGun()
{ {
if (!IsValid(CurrentGun)) return; if (!IsValid(CurrentGunBase)) return;
StopAiming(); StopAiming();
FVector ForwardVector = PlayerCharacter->GetActorForwardVector(); FDetachmentTransformRules DetachmentRules(EDetachmentRule::KeepRelative, EDetachmentRule::KeepRelative,
FVector PlayerPos = PlayerCharacter->GetActorLocation(); EDetachmentRule::KeepRelative, false);
FVector DroppedGunPos = PlayerPos + (ForwardVector * DropGunRange);
FTransform DroppedGunTransform = PlayerCharacter->GetActorTransform(); CurrentGunMesh->DetachFromComponent(DetachmentRules);
DroppedGunTransform.SetLocation(DroppedGunPos); CurrentGunMesh->SetSimulatePhysics(true);
CurrentGunMesh->bCastDynamicShadow = true;
CurrentGunMesh->CastShadow = true;
AGunBase* DroppedGun = GetWorld()->SpawnActor<AGunBase>(CurrentGun->SceneItemClass, DroppedGunTransform); CurrentGunMesh = nullptr;
CurrentGunBase = nullptr;
if (DroppedGun)
{
DroppedGun->MaxRange = CurrentGun->MaxRange;
DroppedGun->DamageValue = CurrentGun->DamageValue;
DroppedGun->FireRateCooldown = CurrentGun->FireRateCooldown;
DroppedGun->RecoilForceMultiplier = CurrentGun->RecoilForceMultiplier;
DroppedGun->ReloadTime = CurrentGun->ReloadTime;
DroppedGun->AmmoType = CurrentGun->AmmoType;
DroppedGun->CurrentAmmo = CurrentGun->CurrentAmmo;
DroppedGun->MaxAmmo = CurrentGun->MaxAmmo;
CurrentGun->Destroy();
CurrentGun = nullptr;
}
} }
void UShootingComponent::DropThrow() void UShootingComponent::DropThrow()
@ -247,9 +232,19 @@ void UShootingComponent::SelectGun(bool bSelectFirstSlot)
void UShootingComponent::SwitchGun() void UShootingComponent::SwitchGun()
{ {
AGunBase* temp = CurrentGun; AGunBase* tempBase = CurrentGunBase;
CurrentGun = SecondaryGun; CurrentGunBase = SecondaryGunBase;
SecondaryGun = temp; SecondaryGunBase = tempBase;
UStaticMeshComponent* tempMesh = CurrentGunMesh;
CurrentGunMesh = SecondaryGunMesh;
SecondaryGunMesh = tempMesh;
if (IsValid(SecondaryGunMesh))
SecondaryGunMesh->SetVisibility(false);
if (IsValid(CurrentGunMesh))
CurrentGunMesh->SetVisibility(true);
bIsFirstGunSelected = !bIsFirstGunSelected; bIsFirstGunSelected = !bIsFirstGunSelected;
StopAiming(); StopAiming();
@ -267,12 +262,12 @@ void UShootingComponent::SwitchGun()
void UShootingComponent::StartAiming() void UShootingComponent::StartAiming()
{ {
if (IsValid(CurrentGun)) if (IsValid(CurrentGunBase))
{ {
PlayerCharacter->bIsAimingMode = true; PlayerCharacter->bIsAimingMode = true;
PlayerCharacter->GetCharacterMovement()->MaxWalkSpeed = PlayerCharacter->WalkSpeed * AimingMoveSpeedScale; PlayerCharacter->GetCharacterMovement()->MaxWalkSpeed = PlayerCharacter->WalkSpeed * AimingMoveSpeedScale;
TargetFOV = CurrentGun->AimingFOV; TargetFOV = CurrentGunBase->AimingFOV;
} }
} }
@ -317,7 +312,14 @@ void UShootingComponent::Throw()
} }
// Called every frame bool UShootingComponent::IsHoldingGun()
{
if (IsValid(CurrentGunBase))
return true;
return false;
}
void UShootingComponent::TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction) void UShootingComponent::TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction)
{ {
Super::TickComponent(DeltaTime, TickType, ThisTickFunction); Super::TickComponent(DeltaTime, TickType, ThisTickFunction);

View File

@ -6,11 +6,6 @@
AExoCharacterBase::AExoCharacterBase() AExoCharacterBase::AExoCharacterBase()
{ {
PrimaryActorTick.bCanEverTick = false; PrimaryActorTick.bCanEverTick = false;
Weapon = CreateDefaultSubobject<USkeletalMeshComponent>("Weapon");
Weapon->SetupAttachment(GetMesh(), FName("WeaponHandSocket"));
Weapon->SetCollisionEnabled(ECollisionEnabled::NoCollision);
} }
void AExoCharacterBase::BeginPlay() void AExoCharacterBase::BeginPlay()

View File

@ -13,9 +13,15 @@ AExoPlayerCharacter::AExoPlayerCharacter()
GetCharacterMovement()->bSnapToPlaneAtStart = true; GetCharacterMovement()->bSnapToPlaneAtStart = true;
InteractionComponent = CreateDefaultSubobject<UInteractionComponent>(TEXT("Interaction Component")); InteractionComponent = CreateDefaultSubobject<UInteractionComponent>(TEXT("Interaction Component"));
ShootingComponent = CreateDefaultSubobject<UShootingComponent>(TEXT("Shooting Component")); ShootingComponent = CreateDefaultSubobject<UShootingComponent>(TEXT("Shooting Component"));
Weapon = CreateDefaultSubobject<USkeletalMeshComponent>("Weapon");
Weapon->SetupAttachment(GetMesh());
Weapon->SetCollisionEnabled(ECollisionEnabled::NoCollision);
Weapon->bCastDynamicShadow = false;
Weapon->CastShadow = false;
Weapon->SetRelativeLocation(FVector(-50.f, 0.f, -90.f));
bUseControllerRotationPitch = false; bUseControllerRotationPitch = false;
bUseControllerRotationYaw = true; bUseControllerRotationYaw = true;
bUseControllerRotationRoll = false; bUseControllerRotationRoll = false;

View File

@ -5,35 +5,24 @@
#include "Characters/Components/ShootingComponent.h" #include "Characters/Components/ShootingComponent.h"
// Sets default values
AGunBase::AGunBase() AGunBase::AGunBase()
{ {
// 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; PrimaryActorTick.bCanEverTick = true;
} }
// Called when the game starts or when spawned
void AGunBase::BeginPlay() void AGunBase::BeginPlay()
{ {
Super::BeginPlay(); Super::BeginPlay();
} }
// Called every frame
void AGunBase::Tick(float DeltaTime) void AGunBase::Tick(float DeltaTime)
{ {
Super::Tick(DeltaTime); Super::Tick(DeltaTime);
} }
void AGunBase::Interact_Implementation(AExoPlayerCharacter* playerCharacter)
{
playerCharacter->ShootingComponent->PickUpGun(this);
Destroy();
}
void AGunBase::AddAmmo(int AddValue) void AGunBase::AddAmmo(int AddValue)
{ {
CurrentAmmo += AddValue; CurrentAmmo += AddValue;

View File

@ -19,7 +19,6 @@ class EXO_API UShootingComponent : public UActorComponent
GENERATED_BODY() GENERATED_BODY()
public: public:
// Sets default values for this component's properties
UShootingComponent(); UShootingComponent();
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Debug") UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Debug")
@ -32,11 +31,9 @@ public:
TObjectPtr<APlayerCameraManager> CameraManager; TObjectPtr<APlayerCameraManager> CameraManager;
protected: protected:
// Called when the game starts
virtual void BeginPlay() override; virtual void BeginPlay() override;
public: public:
// Called every frame
virtual void TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction) override; virtual void TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction) override;
UFUNCTION(Category = "Shooting") UFUNCTION(Category = "Shooting")
@ -52,7 +49,7 @@ public:
bool AddAmmo(EAmmoType CheckingAmmo, int AddValue); bool AddAmmo(EAmmoType CheckingAmmo, int AddValue);
UFUNCTION(BlueprintCallable, Category = "Shooting") UFUNCTION(BlueprintCallable, Category = "Shooting")
void PickUpGun(AGunBase* gunItem); void PickUpGun(UStaticMeshComponent* PickedGunMesh);
UFUNCTION(BlueprintCallable, Category = "Shooting") UFUNCTION(BlueprintCallable, Category = "Shooting")
bool PickUpThrow(AThrowableBase* ThrowItem); bool PickUpThrow(AThrowableBase* ThrowItem);
@ -78,6 +75,9 @@ public:
UFUNCTION(Category = "Shooting") UFUNCTION(Category = "Shooting")
void Throw(); void Throw();
UFUNCTION(BlueprintCallable, Category = "Shooting")
bool IsHoldingGun();
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Aiming") UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Aiming")
float AimingAnimationSpeed = 30.0f; float AimingAnimationSpeed = 30.0f;
@ -93,8 +93,11 @@ public:
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Shooting") UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Shooting")
AThrowableBase* ThrowableItem = nullptr; AThrowableBase* ThrowableItem = nullptr;
AGunBase* CurrentGun = nullptr; AGunBase* CurrentGunBase = nullptr;
AGunBase* SecondaryGun = nullptr; UStaticMeshComponent* CurrentGunMesh = nullptr;
AGunBase* SecondaryGunBase = nullptr;
UStaticMeshComponent* SecondaryGunMesh = nullptr;
private: private:
void ResetFireCooldown(); void ResetFireCooldown();

View File

@ -16,8 +16,4 @@ public:
protected: protected:
virtual void BeginPlay() override; virtual void BeginPlay() override;
UPROPERTY(EditAnywhere, Category = "Combat")
TObjectPtr<USkeletalMeshComponent> Weapon;
}; };

View File

@ -18,6 +18,9 @@ class EXO_API AExoPlayerCharacter : public AExoCharacterBase
public: public:
AExoPlayerCharacter(); AExoPlayerCharacter();
UPROPERTY(EditAnywhere, Category = "Combat")
TObjectPtr<USkeletalMeshComponent> Weapon;
UPROPERTY(VisibleAnywhere, BlueprintReadOnly) UPROPERTY(VisibleAnywhere, BlueprintReadOnly)
TObjectPtr<UInteractionComponent> InteractionComponent; TObjectPtr<UInteractionComponent> InteractionComponent;

View File

@ -14,7 +14,6 @@ class EXO_API AGunBase : public AActor, public IInteractable
GENERATED_BODY() GENERATED_BODY()
public: public:
// Sets default values for this actor's properties
AGunBase(); AGunBase();
UPROPERTY(EditAnywhere, Category = "Shooting") UPROPERTY(EditAnywhere, Category = "Shooting")
@ -44,18 +43,12 @@ public:
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Ammo") UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Ammo")
EAmmoType AmmoType = EAmmoType::Revolver; EAmmoType AmmoType = EAmmoType::Revolver;
TSubclassOf<AActor> SceneItemClass;
UFUNCTION(Category = "Ammo") UFUNCTION(Category = "Ammo")
void AddAmmo(int AddValue); void AddAmmo(int AddValue);
protected: protected:
// Called when the game starts or when spawned
virtual void BeginPlay() override; virtual void BeginPlay() override;
public: public:
// Called every frame
virtual void Tick(float DeltaTime) override; virtual void Tick(float DeltaTime) override;
virtual void Interact_Implementation(AExoPlayerCharacter* playerCharacter) override;
}; };