SimpleShooter Mega Challenge - Help me I implement Ammo - Problem Solved

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.

https://gdev.tv/projectupload

==============================================================================
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.

You’re missing includes in the cpp

Thanks, now I can compile with no errors

Do you have other issues? It’s not clear from the OP whether that was just the issue

Yes.

There are no compile errors but when the PlayerShooterCharacter touches the capsule component of the Pickup Actor (ammo pickup) the

CapsuleComp->OnComponentHit.AddDynamic(this, &APickup::OnHit);

does not seem to work as the APickup::OnHit function does not run.

Maybe you can see my problem by downloading my game?

Where are is that line of code? Does it work if you put it in BeginPlay?


If you want to do that you can do that by following link

https://gdev.tv/projectupload

Please use File > Package Project > Zip Up Project within Unreal as this will ensure only required files are zipped up and things like the Binaries are excluded.

Done, thanks

You’re using OnHit but using it as OnOverlap. Use OnComponentBeginOverlap.

This topic was automatically closed 20 days after the last reply. New replies are no longer allowed.

Privacy & Terms