I am currently trying to make pickup boxes that when one is touched my a ShooterCharacter (the player or the enemy), the pickup box will disappear and give the ShooterCharacter Ammo for its weapons. My difficulty now is getting the weapons that the ShooterCharacter has.
Pickup.h
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "Pickup.generated.h"
class UCapsuleComponent;
class AShooterCharacter;
class AGun;
UCLASS()
class SIMPLESHOOTER_API APickup : public AActor
{
GENERATED_BODY()
public:
// Sets default values for this actor's properties
APickup();
// protected:
// // Called when the game starts or when spawned
// virtual void BeginPlay() override;
// public:
// // Called every frame
// virtual void Tick(float DeltaTime) override;
private:
// COMPONENTS
UPROPERTY(VisibleAnywhere)
UCapsuleComponent* CapsuleComp;
UPROPERTY(VisibleAnywhere)
UStaticMeshComponent* Mesh;
// FUNCTIONS
UFUNCTION()
void OnHit(UPrimitiveComponent* HitComp, AActor* OtherActor, UPrimitiveComponent* OtherComp, FVector NormalImpulse, const FHitResult& Hit);
// VARIABLES
UPROPERTY(EditAnywhere)
int32 Ammo = 10;
// UPROPERTY(EditDefaultsOnly)
// TSubclassOf<AShooterCharacter> ShooterCharacterClass;
// UPROPERTY()
// AShooterCharacter* ShooterCharacter;
// UPROPERTY()
// AGun* Gun;
};
Pickup.cpp
// Fill out your copyright notice in the Description page of Project Settings.
#include "Pickup.h"
#include "Components/CapsuleComponent.h"
#include "Containers/Array.h"
#define OUT
// Sets default values
APickup::APickup()
{
// 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;
CapsuleComp = CreateDefaultSubobject<UCapsuleComponent>(TEXT("Capsule Collider"));
CapsuleComp->OnComponentHit.AddDynamic(this, &APickup::OnHit);
RootComponent = CapsuleComp;
Mesh = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("Base Mesh"));
Mesh->SetupAttachment(RootComponent);
}
void APickup::OnHit(UPrimitiveComponent* HitComp, AActor* OtherActor, UPrimitiveComponent* OtherComp, FVector NormalImpulse, const FHitResult& Hit)
{
// If the other actor is ShooterCharacter...
if (OtherActor && OtherActor != this && OtherActor->IsA<AShooterCharacter>())
{
for (int32 Index = 0; Index < 3; Index++)
{
AShooterCharacter* ShooterCharacter = Cast<AShooterCharacter>(GetOwner());
// AGun* ActiveGun = Cast<AShooterCharacter>(GetGunActor(Index));
AGun* ActiveGun = ShooterCharacter->GetGunActor(Index);
// If the Pickup has no more ammo, then do early return. Thus, no more ammo is given
if (Ammo <= 0)
return;
// Give one ammo to first weapon until it has enough ammo, then repeat for the following weapons
while (!ActiveGun->EnoughAmmo() || Ammo > 0)
{
ActiveGun->ChangeAmmo(1);
Ammo--;
}
}
}
// Disable();
Destroy();
}
// void Disable();
// If Ammo < MaxAmmo
// Disable Collison and hide Mesh
// Start a timer
// When timer is up renable Collison and show Mesh
// // Called when the game starts or when spawned
// void APickup::BeginPlay()
// {
// Super::BeginPlay();
// }
// // Called every frame
// void APickup::Tick(float DeltaTime)
// {
// Super::Tick(DeltaTime);
// }
ShooterCharacter.h
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Character.h"
#include "ShooterCharacter.generated.h"
class AGun;
UCLASS()
class SIMPLESHOOTER_API AShooterCharacter : public ACharacter
{
GENERATED_BODY()
public:
// Sets default values for this character's properties
AShooterCharacter();
protected:
// Called when the game starts or when spawned
virtual void BeginPlay() override;
public:
UFUNCTION(BlueprintPure)
bool IsDead() const;
UFUNCTION(BlueprintPure)
float GetHealthPercent() const;
// Called every frame
virtual void Tick(float DeltaTime) override;
// Called to bind functionality to input
virtual void SetupPlayerInputComponent(class UInputComponent* PlayerInputComponent) override;
virtual float TakeDamage(float DamageAmount, struct FDamageEvent const &DamageEvent, class AController *EventInstigator, AActor *DamageCauser) override;
void Shoot();
AGun* GetGunActor(int32 GunIndex);
private:
void MoveForward(float AxisValue);
void MoveRight(float AxisValue);
void LookUpRate(float AxisValue);
void LookRightRate(float AxisValue);
void SpawnWeapons();
void SwitchWeapons(float Slot);
UPROPERTY(EditAnywhere)
float RotationRate = 10;
UPROPERTY(EditAnywhere)
float MaxHealth = 100;
UPROPERTY(VisibleAnywhere)
float Health;
UPROPERTY(EditAnywhere)
int32 ActiveIndex;
UPROPERTY(EditDefaultsOnly)
TSubclassOf<AGun> GunClass[3];
UPROPERTY()
AGun* Gun[3];
};
ShooterCharacter.cpp
// Fill out your copyright notice in the Description page of Project Settings.
#include "ShooterCharacter.h"
#include "Gun.h"
#include "Components/CapsuleComponent.h"
#include "SimpleShooterGameModeBase.h"
#include "Math/UnrealMathUtility.h"
// Sets default values
AShooterCharacter::AShooterCharacter()
{
// Set this character 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 AShooterCharacter::BeginPlay()
{
Super::BeginPlay();
Health = MaxHealth;
SpawnWeapons();
}
bool AShooterCharacter::IsDead() const
{
return Health <= 0;
}
float AShooterCharacter::GetHealthPercent() const
{
return Health / MaxHealth;
}
// Called every frame
void AShooterCharacter::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
}
// Called to bind functionality to input
void AShooterCharacter::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)
{
Super::SetupPlayerInputComponent(PlayerInputComponent);
// Keyboard Inputs
PlayerInputComponent->BindAction(TEXT("Jump"), EInputEvent::IE_Pressed, this, &ACharacter::Jump);
PlayerInputComponent->BindAction(TEXT("Attack"), EInputEvent::IE_Pressed, this, &AShooterCharacter::Shoot);
PlayerInputComponent->BindAxis(TEXT("MoveForward"), this, &AShooterCharacter::MoveForward);
PlayerInputComponent->BindAxis(TEXT("MoveRight"), this, &AShooterCharacter::MoveRight);
PlayerInputComponent->BindAxis(TEXT("LookUp"), this, &APawn::AddControllerPitchInput);
PlayerInputComponent->BindAxis(TEXT("LookRight"), this, &APawn::AddControllerYawInput);
PlayerInputComponent->BindAxis(TEXT("SwitchWeapons"), this, &AShooterCharacter::SwitchWeapons);
// Controller Inputs
PlayerInputComponent->BindAxis(TEXT("LookUpRate"), this, &AShooterCharacter::LookUpRate);
PlayerInputComponent->BindAxis(TEXT("LookRightRate"), this, &AShooterCharacter::LookRightRate);
}
float AShooterCharacter::TakeDamage(float DamageAmount, struct FDamageEvent const& DamageEvent, class AController* EventInstigator, AActor* DamageCauser)
{
float DamageToApply = Super::TakeDamage(DamageAmount, DamageEvent, EventInstigator, DamageCauser);
DamageToApply = FMath::Min(Health, DamageToApply);
Health -= DamageToApply;
UE_LOG(LogTemp, Warning, TEXT("Health = %f"), Health);
if (IsDead())
{
ASimpleShooterGameModeBase* GameMode = GetWorld()->GetAuthGameMode<ASimpleShooterGameModeBase>();
if (GameMode != nullptr)
{
GameMode->PawnKilled(this);
}
DetachFromControllerPendingDestroy();
GetCapsuleComponent()->SetCollisionEnabled(ECollisionEnabled::NoCollision);
}
return DamageToApply;
}
void AShooterCharacter::MoveForward(float AxisValue)
{
AddMovementInput(GetActorForwardVector() * AxisValue);
}
void AShooterCharacter::MoveRight(float AxisValue)
{
AddMovementInput(GetActorRightVector() * AxisValue);
}
void AShooterCharacter::LookUpRate(float AxisValue)
{
AddControllerPitchInput(AxisValue * RotationRate * GetWorld()->GetDeltaSeconds());
}
void AShooterCharacter::LookRightRate(float AxisValue)
{
AddControllerYawInput(AxisValue * RotationRate * GetWorld()->GetDeltaSeconds());
}
void AShooterCharacter::SpawnWeapons()
{
GetMesh()->HideBoneByName(TEXT("weapon_r"), EPhysBodyOp::PBO_None);
ActiveIndex = 0;
UE_LOG(LogTemp, Warning, TEXT("ActiveIndex = %d"), ActiveIndex);
for (int32 Index = 0; Index < 3; Index++)
{
Gun[Index] = GetWorld()->SpawnActor<AGun>(GunClass[Index]);
if (Gun[Index])
{
Gun[Index]->AttachToComponent(GetMesh(), FAttachmentTransformRules::KeepRelativeTransform, TEXT("WeaponSocket"));
Gun[Index]->SetOwner(this);
UE_LOG(LogTemp, Warning, TEXT("Attached mesh number %d"), Index);
if (Index != ActiveIndex)
{
Gun[Index]->SetActorHiddenInGame(true);
}
}
else
{
UE_LOG(LogTemp, Warning, TEXT("No gun attached on %d"), Index);
}
}
}
void AShooterCharacter::SwitchWeapons(float Slot)
{
int32 SlotIndex = static_cast<int32>(Slot);
Gun[ActiveIndex]->SetActorHiddenInGame(true);
ActiveIndex += SlotIndex;
ActiveIndex = FMath::Clamp(ActiveIndex, 0, 2);
Gun[ActiveIndex]->SetActorHiddenInGame(false);
}
void AShooterCharacter::Shoot()
{
Gun[ActiveIndex]->PullTrigger();
}
AGun* AShooterCharacter::GetGunActor(int32 GunIndex)
{
int32 Index = GunIndex;
return Gun[Index];
}
The complier log when I try to compile the code above:
Building SimpleShooterEditor...
Using Visual Studio 2019 14.28.29336 toolchain (C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Tools\MSVC\14.28.29333) and Windows 10.0.18362.0 SDK (C:\Program Files (x86)\Windows Kits\10).
Building 4 actions with 12 processes...
[1/4] Pickup.cpp
D:\Coding Projects\Unreal games\SimpleShooter Mega Challenge\SimpleShooter\Source\SimpleShooter\Pickup.cpp(34) : error C2027: use of undefined type 'AShooterCharacter'
D:\Coding Projects\Unreal games\SimpleShooter Mega Challenge\SimpleShooter\Source\SimpleShooter\Pickup.h(10): note: see declaration of 'AShooterCharacter'
D:\Coding Projects\Unreal games\SimpleShooter Mega Challenge\SimpleShooter\Source\SimpleShooter\Pickup.cpp(40) : error C2027: use of undefined type 'AGun'
D:\Coding Projects\Unreal games\SimpleShooter Mega Challenge\SimpleShooter\Source\SimpleShooter\Pickup.h(11): note: see declaration of 'AGun'
D:\Coding Projects\Unreal games\SimpleShooter Mega Challenge\SimpleShooter\Source\SimpleShooter\Pickup.cpp(42) : error C2027: use of undefined type 'AGun'
D:\Coding Projects\Unreal games\SimpleShooter Mega Challenge\SimpleShooter\Source\SimpleShooter\Pickup.h(11): note: see declaration of 'AGun'
==============================================================================
Update 2021-03-29:
I have decided to use trigger volumes. BP_ShooterCharacter now has a Trigger Capsule Component that detects whether it is overlapping with a trigger volume. I also used “OnComponentBeginOverlap” instead of “OnHit” in my ShooterCharacter.cpp file.
I followed this tutorial here:
" Unreal Engine 4 C++ Tutorial: Character Overlap Events" by Harrison McGuire
The enemies in the level when entering and exiting the trigger volume successfully give out a UE_LOG , but the Player does not. The player is a blueprint child of BP_ShooterCharacter, which in turn is the blueprint child of ShooterCharacter C++ Class.
How do I make the Player get detected?!!
ShooterCharacter.h:
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Character.h"
#include "ShooterCharacter.generated.h"
class AGun;
UCLASS()
class SIMPLESHOOTER_API AShooterCharacter : public ACharacter
{
GENERATED_BODY()
public:
// Sets default values for this character's properties
AShooterCharacter();
protected:
// Called when the game starts or when spawned
virtual void BeginPlay() override;
public:
UFUNCTION(BlueprintPure)
bool IsDead() const;
UFUNCTION(BlueprintPure)
float GetHealthPercent() const;
// Called every frame
virtual void Tick(float DeltaTime) override;
// Called to bind functionality to input
virtual void SetupPlayerInputComponent(class UInputComponent* PlayerInputComponent) override;
virtual float TakeDamage(float DamageAmount, struct FDamageEvent const &DamageEvent, class AController *EventInstigator, AActor *DamageCauser) override;
void Shoot();
AGun* GetGunActor(int32 GunIndex);
UFUNCTION()
void OnOverlapBegin(class UPrimitiveComponent* OverlappedComp, class AActor* OtherActor, class UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult);
UFUNCTION()
void OnOverlapEnd(class UPrimitiveComponent* OverlappedComp, class AActor* OtherActor, class UPrimitiveComponent* OtherComp, int32 OtherBodyIndex);
private:
// COMPONENTS
UPROPERTY(VisibleAnywhere, Category = "Trigger Capsule")
class UCapsuleComponent* TriggerCapsule;
// FUNCTIONS
void MoveForward(float AxisValue);
void MoveRight(float AxisValue);
void LookUpRate(float AxisValue);
void LookRightRate(float AxisValue);
void SpawnWeapons();
void SwitchWeapons(float Slot);
// VARIABLES
UPROPERTY(EditAnywhere)
float RotationRate = 10;
UPROPERTY(EditAnywhere)
float MaxHealth = 100;
UPROPERTY(VisibleAnywhere)
float Health;
UPROPERTY(EditAnywhere)
int32 ActiveIndex;
UPROPERTY(EditDefaultsOnly)
TSubclassOf<AGun> GunClass[3];
UPROPERTY()
AGun* Gun[3];
};
ShooterCharacter.cpp:
// Fill out your copyright notice in the Description page of Project Settings.
#include "ShooterCharacter.h"
#include "Gun.h"
#include "Components/CapsuleComponent.h"
#include "SimpleShooterGameModeBase.h"
#include "Math/UnrealMathUtility.h"
// Sets default values
AShooterCharacter::AShooterCharacter()
{
// Set this character to call Tick() every frame. You can turn this off to improve performance if you don't need it.
PrimaryActorTick.bCanEverTick = true;
TriggerCapsule = CreateDefaultSubobject<UCapsuleComponent>(TEXT("Trigger Capsule"));
TriggerCapsule->SetCollisionProfileName(TEXT("Trigger"));
TriggerCapsule->SetupAttachment(RootComponent);
TriggerCapsule->OnComponentBeginOverlap.AddDynamic(this, &AShooterCharacter::OnOverlapBegin);
TriggerCapsule->OnComponentEndOverlap.AddDynamic(this, &AShooterCharacter::OnOverlapEnd);
}
// Called when the game starts or when spawned
void AShooterCharacter::BeginPlay()
{
Super::BeginPlay();
Health = MaxHealth;
SpawnWeapons();
}
bool AShooterCharacter::IsDead() const
{
return Health <= 0;
}
float AShooterCharacter::GetHealthPercent() const
{
return Health / MaxHealth;
}
// Called every frame
void AShooterCharacter::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
}
// Called to bind functionality to input
void AShooterCharacter::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)
{
Super::SetupPlayerInputComponent(PlayerInputComponent);
// Keyboard Inputs
PlayerInputComponent->BindAction(TEXT("Jump"), EInputEvent::IE_Pressed, this, &ACharacter::Jump);
PlayerInputComponent->BindAction(TEXT("Attack"), EInputEvent::IE_Pressed, this, &AShooterCharacter::Shoot);
PlayerInputComponent->BindAxis(TEXT("MoveForward"), this, &AShooterCharacter::MoveForward);
PlayerInputComponent->BindAxis(TEXT("MoveRight"), this, &AShooterCharacter::MoveRight);
PlayerInputComponent->BindAxis(TEXT("LookUp"), this, &APawn::AddControllerPitchInput);
PlayerInputComponent->BindAxis(TEXT("LookRight"), this, &APawn::AddControllerYawInput);
PlayerInputComponent->BindAxis(TEXT("SwitchWeapons"), this, &AShooterCharacter::SwitchWeapons);
// Controller Inputs
PlayerInputComponent->BindAxis(TEXT("LookUpRate"), this, &AShooterCharacter::LookUpRate);
PlayerInputComponent->BindAxis(TEXT("LookRightRate"), this, &AShooterCharacter::LookRightRate);
}
float AShooterCharacter::TakeDamage(float DamageAmount, struct FDamageEvent const& DamageEvent, class AController* EventInstigator, AActor* DamageCauser)
{
float DamageToApply = Super::TakeDamage(DamageAmount, DamageEvent, EventInstigator, DamageCauser);
DamageToApply = FMath::Min(Health, DamageToApply);
Health -= DamageToApply;
UE_LOG(LogTemp, Warning, TEXT("Health = %f"), Health);
if (IsDead())
{
ASimpleShooterGameModeBase* GameMode = GetWorld()->GetAuthGameMode<ASimpleShooterGameModeBase>();
if (GameMode != nullptr)
{
GameMode->PawnKilled(this);
}
DetachFromControllerPendingDestroy();
GetCapsuleComponent()->SetCollisionEnabled(ECollisionEnabled::NoCollision);
}
return DamageToApply;
}
void AShooterCharacter::MoveForward(float AxisValue)
{
AddMovementInput(GetActorForwardVector() * AxisValue);
}
void AShooterCharacter::MoveRight(float AxisValue)
{
AddMovementInput(GetActorRightVector() * AxisValue);
}
void AShooterCharacter::LookUpRate(float AxisValue)
{
AddControllerPitchInput(AxisValue * RotationRate * GetWorld()->GetDeltaSeconds());
}
void AShooterCharacter::LookRightRate(float AxisValue)
{
AddControllerYawInput(AxisValue * RotationRate * GetWorld()->GetDeltaSeconds());
}
void AShooterCharacter::SpawnWeapons()
{
GetMesh()->HideBoneByName(TEXT("weapon_r"), EPhysBodyOp::PBO_None);
ActiveIndex = 0;
UE_LOG(LogTemp, Warning, TEXT("ActiveIndex = %d"), ActiveIndex);
for (int32 Index = 0; Index < 3; Index++)
{
Gun[Index] = GetWorld()->SpawnActor<AGun>(GunClass[Index]);
if (Gun[Index])
{
Gun[Index]->AttachToComponent(GetMesh(), FAttachmentTransformRules::KeepRelativeTransform, TEXT("WeaponSocket"));
Gun[Index]->SetOwner(this);
UE_LOG(LogTemp, Warning, TEXT("Attached mesh number %d"), Index);
if (Index != ActiveIndex)
{
Gun[Index]->SetActorHiddenInGame(true);
}
}
else
{
UE_LOG(LogTemp, Warning, TEXT("No gun attached on %d"), Index);
}
}
}
void AShooterCharacter::SwitchWeapons(float Slot)
{
int32 SlotIndex = static_cast<int32>(Slot);
Gun[ActiveIndex]->SetActorHiddenInGame(true);
ActiveIndex += SlotIndex;
ActiveIndex = FMath::Clamp(ActiveIndex, 0, 2);
Gun[ActiveIndex]->SetActorHiddenInGame(false);
}
void AShooterCharacter::Shoot()
{
Gun[ActiveIndex]->PullTrigger();
}
AGun* AShooterCharacter::GetGunActor(int32 GunIndex)
{
int32 Index = GunIndex;
return Gun[Index];
}
void AShooterCharacter::OnOverlapBegin(class UPrimitiveComponent* OverlappedComp, class AActor* OtherActor, class UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult)
{
// (OtherActor != this)
if (OtherActor && OtherComp)
{
UE_LOG(LogTemp, Warning, TEXT("Overlap Begin"));
}
}
void AShooterCharacter::OnOverlapEnd(class UPrimitiveComponent* OverlappedComp, class AActor* OtherActor, class UPrimitiveComponent* OtherComp, int32 OtherBodyIndex)
{
UE_LOG(LogTemp, Warning, TEXT("Overlap End"));
}
==============================================================================
Update 2021-03-30:
By deleting the 2nd capsule component on the player and doing the video tutorial on the Pickup Actor, the player can now be detected.
==============================================================================
Update 2021-03-30 number 2:
I decided its too much of a hassle to try and access the gun actor and edit its variables in it from the Pickup Actor, using blueprints or code. Thus, I decided to try to make the BP_ShooterCharacter itself call the gun to do something when it touches the Pickup Actor (just like in the video tutorial). However, I cannot get the BP_PlayerShooterCharacter to activate the UE_LOG which says “Overlap Begin” but I can for the BP_ShooterCharacter. @DanM, if you are reading this, can you take a look at my game? I’ll use the link again.
==============================================================================
Update 2021-03-31:
I have made a new Blueprint class derived from the ShooterCharacter C++ Class itself. I named it BP_Player and now it can give a UE_LOG if it touches the Trigger Capsule component of the Pickup Actor!! Problem solved… for now.
I would still like it if I could find out why BP_PlayerShooterCharacter could not make the UE_LOG.
==============================================================================
Update 2021-04-02:
I have finally finished implementing Ammo and Pickups to SimpleShooter.