I have finished all the lessons for ToonTanks in this course:
Now i am trying to make a new pawn class that is a land mine. The idea is that if the tank touches it, it will deal damage to the tank and explode. But it is not doing anything when the tank touches it. Can someone help me?
PawnLandMine.h:
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Pawn.h"
#include "PawnLandMine.generated.h"
class UCapsuleComponent;
class UHealthComponent;
UCLASS()
class TOONTANKS_API APawnLandMine : public APawn
{
GENERATED_BODY()
private:
// COMPONENTS
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Components", meta = (AllowPrivateAccess = "true"))
UCapsuleComponent* CapsuleComp;
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Components", meta = (AllowPrivateAccess = "true"))
UStaticMeshComponent* BaseMesh;
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Components", meta = (AllowPrivateAccess = "true"))
UHealthComponent* HealthComponent;
// VARIABLES
UPROPERTY(EditAnywhere, Category = "Effects")
UParticleSystem* DeathParticle;
UPROPERTY(EditAnywhere, Category = "Effects")
USoundBase* DeathSound;
UPROPERTY(EditAnywhere, Category = "Effects")
TSubclassOf<UMatineeCameraShake> DeathShake;
UPROPERTY(EditDefaultsOnly, Category = "Damage")
TSubclassOf<UDamageType> DamageType;
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Damage", meta = (AllowPrivateAccess = "true"))
float Damage = 50;
// FUNCTIONS
UFUNCTION()
void OnHit(UPrimitiveComponent* HitComp, AActor* OtherActor, UPrimitiveComponent* OtherComp, FVector NormalImpulse, const FHitResult& Hit);
public:
// Sets default values for this pawn's properties
APawnLandMine();
void PawnDestroyed();
virtual void HandleDestruction();
protected:
// Called when the game starts or when spawned
virtual void BeginPlay() override;
};
PawnLandMine.cpp:
// Fill out your copyright notice in the Description page of Project Settings.
#include "PawnLandMine.h"
#include "Components/CapsuleComponent.h"
#include "ToonTanks/Components/HealthComponent.h"
#include "Kismet/GameplayStatics.h"
// Sets default values
APawnLandMine::APawnLandMine()
{
// Set this pawn to call Tick() every frame. You can turn this off to improve performance if you don't need it.
PrimaryActorTick.bCanEverTick = false;
CapsuleComp = CreateDefaultSubobject<UCapsuleComponent>(TEXT("Capsule Collider"));
CapsuleComp->OnComponentHit.AddDynamic(this, &APawnLandMine::OnHit);
RootComponent = CapsuleComp;
BaseMesh = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("Base Mesh"));
BaseMesh->SetupAttachment(RootComponent);
HealthComponent = CreateDefaultSubobject<UHealthComponent>(TEXT("Health Component"));
UE_LOG(LogTemp, Warning, TEXT("APawnLandMine::APawnLandMine() has been run"));
}
// Called when the game starts or when spawned
void APawnLandMine::BeginPlay()
{
Super::BeginPlay();
}
void APawnLandMine::HandleDestruction()
{
// Play death effects particle, sound and camera shake.
UGameplayStatics::SpawnEmitterAtLocation(this, DeathParticle, GetActorLocation());
UGameplayStatics::SpawnSoundAtLocation(this, DeathSound, GetActorLocation());
GetWorld()->GetFirstPlayerController()->ClientPlayCameraShake(DeathShake);
}
void APawnLandMine::PawnDestroyed()
{
HandleDestruction();
Destroy();
}
void APawnLandMine::OnHit(UPrimitiveComponent* HitComp, AActor* OtherActor, UPrimitiveComponent* OtherComp, FVector NormalImpulse, const FHitResult& Hit)
{
UE_LOG(LogTemp, Warning, TEXT("APawnLandMine::OnHit has been run"));
// If the other actor ISN'T self AND exists, then apply damage
if (OtherActor && OtherActor != this)
{
UGameplayStatics::ApplyDamage(OtherActor, Damage, GetOwner()->GetInstigatorController(), this, DamageType);
PawnDestroyed();
}
}
===========================================================================
Update 2021-03-07:
DanM said:
- Do you intend to being able to control this land mine? If the answer is no then this should be derived from AActor not APawn.
No, I do not need to control the land mine as a player. I have made an Actor for the Land Mine before this making this Pawn. It caused the game to crash due to the code:
GetOwner()->GetInstigatorController()
Here’s its code:
LandMineBase.h:
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "LandMineBase.generated.h"
UCLASS()
class TOONTANKS_API ALandMineBase : public AActor
{
GENERATED_BODY()
private:
// COMPONENTS
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Components", meta = (AllowPrivateAccess = "true"))
UStaticMeshComponent* LandMineMesh;
// VARIABLES
UPROPERTY(EditDefaultsOnly, Category = "Damage")
TSubclassOf<UDamageType> DamageType;
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Damage", meta = (AllowPrivateAccess = "true"))
float Damage = 50;
UPROPERTY(EditAnywhere, Category = "Effects")
UParticleSystem* ExplodeParticle;
UPROPERTY(EditAnywhere, Category = "Effects")
USoundBase* ExplodeSound;
UPROPERTY(EditAnywhere, Category = "Effects")
TSubclassOf<UMatineeCameraShake> HitShake;
// FUNCTIONS
UFUNCTION()
void OnHit(UPrimitiveComponent* HitComp, AActor* OtherActor, UPrimitiveComponent* OtherComp, FVector NormalImpulse, const FHitResult& Hit);
public:
// Sets default values for this actor's properties
ALandMineBase();
protected:
// Called when the game starts or when spawned
virtual void BeginPlay() override;
};
LandMineBase.cpp:
// Fill out your copyright notice in the Description page of Project Settings.
#include "LandMineBase.h"
#include "Components/StaticMeshComponent.h"
#include "Kismet/GameplayStatics.h"
#include "Particles/ParticleSystemComponent.h"
// Sets default values
ALandMineBase::ALandMineBase()
{
// Set this actor to call Tick() every frame. You can turn this off to improve performance if you don't need it.
PrimaryActorTick.bCanEverTick = false;
LandMineMesh = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("Land Mine Mesh"));
LandMineMesh->OnComponentHit.AddDynamic(this, &ALandMineBase::OnHit);
RootComponent = LandMineMesh;
}
// Called when the game starts or when spawned
void ALandMineBase::BeginPlay()
{
Super::BeginPlay();
// DEBUG
// GetOwner()->GetInstigatorController() seems to always make the game crash!
if (GetOwner()->GetInstigatorController() == nullptr)
{
UE_LOG(LogTemp, Error, TEXT("GetOwner()->GetInstigatorController() is invalid or has a nullptr!"));
return;
}
}
void ALandMineBase::OnHit(UPrimitiveComponent* HitComp, AActor* OtherActor, UPrimitiveComponent* OtherComp, FVector NormalImpulse, const FHitResult& Hit)
{
// Try to get a reference to the owning class.
AActor* MyOwner = GetOwner();
// If for some reason we can't get a valid reference, return as we need to check against the owner.
if (!MyOwner)
{
return;
}
// If the other actor ISN'T self OR Owner AND exists, then apply damage
if (OtherActor && OtherActor != this/* && OtherActor != MyOwner*/)
{
UGameplayStatics::ApplyDamage(OtherActor, Damage, MyOwner->GetInstigatorController(), this, DamageType);
UGameplayStatics::SpawnEmitterAtLocation(this, ExplodeParticle, GetActorLocation());
UGameplayStatics::PlaySoundAtLocation(this, ExplodeSound, GetActorLocation());
GetWorld()->GetFirstPlayerController()->ClientPlayCameraShake(HitShake);
Destroy();
}
}
Here’s Blueprints:
BP_LandMineBase: