feat: add grenade pickup, throw, and drop mechanics

Implemented basic grenade interaction:
- Player can pick up, throw, and drop grenades.

To-do:
- Refine throwing mechanics (e.g. direction, force).
- Clean up and refactor related code.
This commit is contained in:
Foxim 2025-04-08 18:22:33 +02:00
parent c372d9e8d5
commit 2b6bdf9f80
11 changed files with 224 additions and 2 deletions

Binary file not shown.

Binary file not shown.

View File

@ -3,6 +3,8 @@
#include "Characters/Components/ShootingComponent.h"
#include "Items/ThrowableBase.h"
// Sets default values for this component's properties
UShootingComponent::UShootingComponent()
{
@ -96,7 +98,7 @@ void UShootingComponent::Reload()
CurrentGun->CurrentAmmo = CurrentGun->MaxAmmo;
PlayerCharacter->GetWorldTimerManager().SetTimer(ReloadTimer, this, &UShootingComponent::ReloadCompleted, CurrentGun->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"), CurrentGun->CurrentAmmo, CurrentGun->MaxAmmo); // Docelowo tutaj wywo<77>anie UI aktualizuj<75>ce stan ammo
}
void UShootingComponent::PickUpGun(AGunBase* gunItem)
@ -121,6 +123,38 @@ void UShootingComponent::PickUpGun(AGunBase* gunItem)
CurrentGun = NewGun;
}
bool UShootingComponent::PickUpThrow(AThrowableBase* ThrowItem)
{
AThrowableBase* NewThrowable = NewObject<AThrowableBase>();
if (IsValid(ThrowableItem))
{
if (ThrowableItem->GetClass() == NewThrowable->GetClass())
{
if (ThrowableItem->Quantity + ThrowItem->Quantity >= ThrowableItem->MaxQuantity)
{
ThrowableItem->Quantity = ThrowableItem->MaxQuantity;
return false;
}
ThrowableItem->Quantity += ThrowItem->Quantity;
return true;
}
DropThrow();
}
NewThrowable->Damage = ThrowItem->Damage;
NewThrowable->Quantity = ThrowItem->Quantity;
NewThrowable->Radius = ThrowItem->Radius;
NewThrowable->ThrowableType = ThrowItem->ThrowableType;
NewThrowable->SceneItemClass = ThrowItem->GetClass();
ThrowableItem = NewThrowable;
return true;
}
void UShootingComponent::DropGun()
{
if (!IsValid(CurrentGun)) return;
@ -151,6 +185,30 @@ void UShootingComponent::DropGun()
}
}
void UShootingComponent::DropThrow()
{
if (!IsValid(ThrowableItem)) return;
FVector ForwardVector = PlayerCharacter->GetActorForwardVector();
FVector DroppedPos = PlayerCharacter->GetActorLocation() + (ForwardVector * 100.f);
FTransform DroppedTransform = PlayerCharacter->GetActorTransform();
DroppedTransform.SetLocation(DroppedPos);
AThrowableBase* DropedItem = GetWorld()->SpawnActor<AThrowableBase>(ThrowableItem->SceneItemClass, DroppedTransform);
if (DropedItem)
{
ThrowableItem->Quantity--;
DropedItem->Damage = ThrowableItem->Damage;
DropedItem->Radius = ThrowableItem->Radius;
DropedItem->Quantity = ThrowableItem->Quantity;
DropedItem->ThrowableType = ThrowableItem->ThrowableType;
}
}
void UShootingComponent::ResetFireCooldown()
{
bCanShoot = true;
@ -206,6 +264,40 @@ void UShootingComponent::StopAiming()
TargetFOV = DefaultFOV;
}
void UShootingComponent::Throw() // Wymaga dopracowania
{
if (IsValid(ThrowableItem))
{
FVector ForwardVector = PlayerCharacter->GetActorForwardVector();
FVector DroppedPos = PlayerCharacter->GetActorLocation() + (ForwardVector * 100.f);
FTransform DroppedTransform = PlayerCharacter->GetActorTransform();
DroppedTransform.SetLocation(DroppedPos);
AThrowableBase* ThrowedItem = GetWorld()->SpawnActor<AThrowableBase>(ThrowableItem->SceneItemClass, DroppedTransform);
if (ThrowedItem)
{
ThrowableItem->Quantity--;
ThrowedItem->Damage = ThrowableItem->Damage;
ThrowedItem->Radius = ThrowableItem->Radius;
ThrowedItem->Quantity = 1;
ThrowedItem->ThrowableType = ThrowableItem->ThrowableType;
FVector ThrowVector = (ThrowedItem->GetActorLocation() - PlayerCharacter->GetActorLocation()).GetSafeNormal();
ThrowedItem->Mesh->SetPhysicsLinearVelocity(ThrowVector * 800.f);
if (ThrowableItem->Quantity <= 0)
{
ThrowableItem->Destroy();
ThrowableItem = nullptr;
}
}
}
}
// Called every frame
void UShootingComponent::TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction)
{

View File

@ -0,0 +1,45 @@
// Fill out your copyright notice in the Description page of Project Settings.
#include "Items/ThrowableBase.h"
#include "Characters/Components/ShootingComponent.h"
AThrowableBase::AThrowableBase()
{
PrimaryActorTick.bCanEverTick = true;
RootSceneComponent = CreateDefaultSubobject<USceneComponent>(TEXT("Root"));
SetRootComponent(RootSceneComponent);
Mesh = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("Mesh"));
Mesh->SetupAttachment(GetRootComponent());
Mesh->SetSimulatePhysics(true);
}
void AThrowableBase::Interact_Implementation(AExoPlayerCharacter* PlayerCharacter)
{
if (PlayerCharacter->ShootingComponent->PickUpThrow(this))
{
Destroy();
}
}
void AThrowableBase::EffectActivate()
{
if (ThrowableType == ThrowableType::Hit)
{
UE_LOG(LogTemp, Display, TEXT("Throwable Hit"));
}
if (ThrowableType == ThrowableType::Explode)
{
UE_LOG(LogTemp, Display, TEXT("Throwable Explode"));
}
if (ThrowableType == ThrowableType::Area)
{
UE_LOG(LogTemp, Display, TEXT("Throwable Area"));
}
}

View File

@ -57,6 +57,7 @@ void AExoPlayerController::SetupInputComponent()
EnhancedInputComponent->BindAction(SprintAction, ETriggerEvent::Started, this, &AExoPlayerController::PlayerStartSprint);
EnhancedInputComponent->BindAction(SprintAction, ETriggerEvent::Completed, this, &AExoPlayerController::PlayerStopSprint);
EnhancedInputComponent->BindAction(ShootAction, ETriggerEvent::Started, this, &AExoPlayerController::PlayerShoot);
EnhancedInputComponent->BindAction(ThrowAction, ETriggerEvent::Completed, this, &AExoPlayerController::PlayerThrow);
EnhancedInputComponent->BindAction(AimAction, ETriggerEvent::Started, this, &AExoPlayerController::PlayerStartAim);
EnhancedInputComponent->BindAction(AimAction, ETriggerEvent::Completed, this, &AExoPlayerController::PlayerStopAim);
EnhancedInputComponent->BindAction(MeleAction, ETriggerEvent::Started, this, &AExoPlayerController::PlayerMeleAttack);
@ -166,6 +167,11 @@ void AExoPlayerController::PlayerShoot()
ShootingComponent->Shoot();
}
void AExoPlayerController::PlayerThrow()
{
ShootingComponent->Throw();
}
void AExoPlayerController::PlayerStartAim()
{
ShootingComponent->StartAiming();

View File

@ -11,6 +11,8 @@
#include "ShootingComponent.generated.h"
class AThrowableBase;
UCLASS( ClassGroup=(Custom), meta=(BlueprintSpawnableComponent) )
class EXO_API UShootingComponent : public UActorComponent
{
@ -49,9 +51,15 @@ public:
UFUNCTION(BlueprintCallable, Category = "Shooting")
void PickUpGun(AGunBase* gunItem);
UFUNCTION(BlueprintCallable, Category = "Shooting")
bool PickUpThrow(AThrowableBase* ThrowItem);
UFUNCTION(Category = "Shooting")
void DropGun();
UFUNCTION(Category = "Shooting")
void DropThrow();
UFUNCTION(Category = "Shooting")
void SwitchGun();
@ -64,6 +72,9 @@ public:
UFUNCTION(Category = "Shooting")
void StopAiming();
UFUNCTION(Category = "Shooting")
void Throw();
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Aiming")
float AimingAnimationSpeed = 30.0f;
@ -76,6 +87,9 @@ public:
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Mele Attack")
float MeleRange = 75.f;
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Shooting")
AThrowableBase* ThrowableItem = nullptr;
AGunBase* CurrentGun = nullptr;
AGunBase* SecondaryGun = nullptr;

View File

@ -0,0 +1,59 @@
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "Interfaces/Interactable.h"
#include "ThrowableBase.generated.h"
UENUM()
enum class ThrowableType : uint8
{
Hit UMETA(DisplayName = "Hit"),
Explode UMETA(DisplayName = "Explode"),
Area UMETA(DisplayName = "Area")
};
UCLASS()
class EXO_API AThrowableBase : public AActor, public IInteractable
{
GENERATED_BODY()
public:
AThrowableBase();
virtual void Interact_Implementation(AExoPlayerCharacter* PlayerCharacter) override;
TSubclassOf<AActor> SceneItemClass;
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Throw")
TObjectPtr<USceneComponent> RootSceneComponent;
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Throw")
TObjectPtr<UStaticMeshComponent> Mesh;
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Throw Details")
int Quantity = 1;
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Throw Details")
int MaxQuantity = 6;
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Throw Details")
int NumberOfBounces = 0;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Effect")
ThrowableType ThrowableType = ThrowableType::Hit;
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Effect")
float Damage = 100.f;
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Effect")
float Radius = 0.f;
protected:
UFUNCTION(BlueprintCallable, Category = "Effect")
virtual void EffectActivate();
};

View File

@ -62,6 +62,9 @@ protected:
UFUNCTION(BlueprintCallable, Category = "Input")
void PlayerShoot(); // LPM
UFUNCTION(BlueprintCallable, Category = "Input")
void PlayerThrow(); // G
UFUNCTION(BlueprintCallable, Category = "Input")
void PlayerStartAim(); // PPM (pressed)
@ -119,7 +122,10 @@ protected:
UPROPERTY(EditAnywhere, Category = "Input")
TObjectPtr<UInputAction> ShootAction;
UPROPERTY(EditAnywhere, Category = "Input")
TObjectPtr<UInputAction> ThrowAction;
UPROPERTY(EditAnywhere, Category = "Input")
TObjectPtr<UInputAction> ReloadAction;