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"
// Sets default values for this component's properties
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;
// ...
}
// Called when the game starts
void UShootingComponent::BeginPlay()
{
Super::BeginPlay();
@ -30,27 +24,27 @@ void UShootingComponent::BeginPlay()
void UShootingComponent::Shoot()
{
if (!IsValid(CurrentGun) || CurrentGun->CurrentAmmo == 0 || bIsReloading || !bCanShoot) return;
if (!IsValid(CurrentGunBase) || CurrentGunBase->CurrentAmmo == 0 || bIsReloading || !bCanShoot) return;
bCanShoot = false;
CurrentGun->CurrentAmmo--;
CurrentGunBase->CurrentAmmo--;
AActor* HitActor = ExecuteLineTrace(CurrentGun->MaxRange);
AActor* HitActor = ExecuteLineTrace(CurrentGunBase->MaxRange);
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
float RecoilPitch = FMath::RandRange(-1.0f, -0.5f);
float RecoilYaw = FMath::RandRange(-0.5f, 0.5f);
PlayerCharacter->AddControllerPitchInput(RecoilPitch * CurrentGun->RecoilForceMultiplier);
PlayerCharacter->AddControllerYawInput(RecoilYaw * CurrentGun->RecoilForceMultiplier);
PlayerCharacter->AddControllerPitchInput(RecoilPitch * CurrentGunBase->RecoilForceMultiplier);
PlayerCharacter->AddControllerYawInput(RecoilYaw * CurrentGunBase->RecoilForceMultiplier);
}
void UShootingComponent::MeleAttack()
@ -88,30 +82,30 @@ AActor* UShootingComponent::ExecuteLineTrace(float LineRange)
void UShootingComponent::Reload()
{
if (!IsValid(CurrentGun) || bIsReloading) return;
if (!IsValid(CurrentGunBase) || bIsReloading) return;
bIsReloading = true;
StopAiming();
CurrentGun->CurrentAmmo = CurrentGun->MaxAmmo;
PlayerCharacter->GetWorldTimerManager().SetTimer(ReloadTimer, this, &UShootingComponent::ReloadCompleted, CurrentGun->ReloadTime, false);
CurrentGunBase->CurrentAmmo = CurrentGunBase->MaxAmmo;
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)
{
if (IsValid(CurrentGun))
if (IsValid(CurrentGunBase))
{
// Adding ammo only if type is compatible
if (CurrentGun->AmmoType == CheckingAmmo)
if (CurrentGunBase->AmmoType == CheckingAmmo)
{
CurrentGun->AddAmmo(AddValue);
CurrentGunBase->AddAmmo(AddValue);
return true;
}
if (SecondaryGun->AmmoType == CheckingAmmo)
if (SecondaryGunBase->AmmoType == CheckingAmmo)
{
SecondaryGun->AddAmmo(AddValue);
SecondaryGunBase->AddAmmo(AddValue);
return true;
}
}
@ -119,27 +113,31 @@ bool UShootingComponent::AddAmmo(EAmmoType CheckingAmmo, int AddValue)
return false;
}
void UShootingComponent::PickUpGun(AGunBase* gunItem)
void UShootingComponent::PickUpGun(UStaticMeshComponent* PickedGunMesh)
{
if (IsValid(CurrentGun) && IsValid(SecondaryGun))
if (IsValid(CurrentGunBase) && IsValid(SecondaryGunBase))
DropGun();
AGunBase* NewGun = NewObject<AGunBase>();
NewGun->MaxRange = gunItem->MaxRange;
NewGun->DamageValue = gunItem->DamageValue;
NewGun->FireRateCooldown = gunItem->FireRateCooldown;
NewGun->RecoilForceMultiplier = gunItem->RecoilForceMultiplier;
NewGun->ReloadTime = gunItem->ReloadTime;
NewGun->AmmoType = gunItem->AmmoType;
NewGun->CurrentAmmo = gunItem->CurrentAmmo;
NewGun->MaxAmmo = gunItem->MaxAmmo;
NewGun->SceneItemClass = gunItem->GetClass();
FAttachmentTransformRules AttachmentRules(EAttachmentRule::SnapToTarget, EAttachmentRule::SnapToTarget,
EAttachmentRule::KeepRelative, true);
if (IsValid(CurrentGun))
SecondaryGun = NewGun;
PickedGunMesh->SetSimulatePhysics(false);
PickedGunMesh->SetCastShadow(false);
PickedGunMesh->AttachToComponent(PlayerCharacter->Weapon, AttachmentRules, FName(TEXT("GripPoint")));
AGunBase* PickedGunBase = Cast<AGunBase>(PickedGunMesh->GetOwner());
if (IsValid(CurrentGunBase))
{
SecondaryGunBase = PickedGunBase;
SecondaryGunMesh = PickedGunMesh;
SecondaryGunMesh->SetVisibility(false);
}
else
CurrentGun = NewGun;
{
CurrentGunBase = PickedGunBase;
CurrentGunMesh = PickedGunMesh;
}
}
bool UShootingComponent::PickUpThrow(AThrowableBase* ThrowItem)
@ -176,33 +174,20 @@ bool UShootingComponent::PickUpThrow(AThrowableBase* ThrowItem)
void UShootingComponent::DropGun()
{
if (!IsValid(CurrentGun)) return;
if (!IsValid(CurrentGunBase)) return;
StopAiming();
FVector ForwardVector = PlayerCharacter->GetActorForwardVector();
FVector PlayerPos = PlayerCharacter->GetActorLocation();
FVector DroppedGunPos = PlayerPos + (ForwardVector * DropGunRange);
FTransform DroppedGunTransform = PlayerCharacter->GetActorTransform();
DroppedGunTransform.SetLocation(DroppedGunPos);
FDetachmentTransformRules DetachmentRules(EDetachmentRule::KeepRelative, EDetachmentRule::KeepRelative,
EDetachmentRule::KeepRelative, false);
AGunBase* DroppedGun = GetWorld()->SpawnActor<AGunBase>(CurrentGun->SceneItemClass, DroppedGunTransform);
CurrentGunMesh->DetachFromComponent(DetachmentRules);
CurrentGunMesh->SetSimulatePhysics(true);
CurrentGunMesh->bCastDynamicShadow = true;
CurrentGunMesh->CastShadow = true;
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;
}
CurrentGunMesh = nullptr;
CurrentGunBase = nullptr;
}
void UShootingComponent::DropThrow()
@ -247,9 +232,19 @@ void UShootingComponent::SelectGun(bool bSelectFirstSlot)
void UShootingComponent::SwitchGun()
{
AGunBase* temp = CurrentGun;
CurrentGun = SecondaryGun;
SecondaryGun = temp;
AGunBase* tempBase = CurrentGunBase;
CurrentGunBase = SecondaryGunBase;
SecondaryGunBase = tempBase;
UStaticMeshComponent* tempMesh = CurrentGunMesh;
CurrentGunMesh = SecondaryGunMesh;
SecondaryGunMesh = tempMesh;
if (IsValid(SecondaryGunMesh))
SecondaryGunMesh->SetVisibility(false);
if (IsValid(CurrentGunMesh))
CurrentGunMesh->SetVisibility(true);
bIsFirstGunSelected = !bIsFirstGunSelected;
StopAiming();
@ -267,12 +262,12 @@ void UShootingComponent::SwitchGun()
void UShootingComponent::StartAiming()
{
if (IsValid(CurrentGun))
if (IsValid(CurrentGunBase))
{
PlayerCharacter->bIsAimingMode = true;
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)
{
Super::TickComponent(DeltaTime, TickType, ThisTickFunction);

View File

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

View File

@ -13,9 +13,15 @@ AExoPlayerCharacter::AExoPlayerCharacter()
GetCharacterMovement()->bSnapToPlaneAtStart = true;
InteractionComponent = CreateDefaultSubobject<UInteractionComponent>(TEXT("Interaction 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;
bUseControllerRotationYaw = true;
bUseControllerRotationRoll = false;

View File

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

View File

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

View File

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

View File

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

View File

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