but it still does not fix the original problem, this is all the code in my current project:
BasePawn.h:
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include “CoreMinimal.h”
#include “GameFramework/Pawn.h”
#include “BasePawn.generated.h”
UCLASS()
class TOONTANKS_API ABasePawn : public APawn
{
GENERATED_BODY()
public:
// Sets default values for this pawn's properties
ABasePawn();
protected:
void RotateTurret(FVector LookAtTarget);
void Fire();
private:
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Components", meta = (AllowPrivateAccess = "true"))
class UCapsuleComponent* CapsuleComp;
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Components", meta = (AllowPrivateAccess = "true"))
UStaticMeshComponent* BaseMesh;
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Components", meta = (AllowPrivateAccess = "true"))
UStaticMeshComponent* TurretMesh;
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Components", meta = (AllowPrivateAccess = "true"))
USceneComponent* ProjectileSpawnPoint;
UPROPERTY(EditDefaultsOnly, Category = "Combat")
TSubclassOf<class AProjectile> ProjectileClass;
};
BasePawn.cpp:
// Fill out your copyright notice in the Description page of Project Settings.
#include “BasePawn.h”
#include “Components/CapsuleComponent.h”
#include “Kismet/GameplayStatics.h”
#include “Projectile.h”
// Sets default values
ABasePawn::ABasePawn()
{
// Set this pawn 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"));
RootComponent = CapsuleComp;
BaseMesh = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("Base Mesh"));
BaseMesh->SetupAttachment(CapsuleComp);
TurretMesh = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("Turret Mesh"));
TurretMesh->SetupAttachment(BaseMesh);
ProjectileSpawnPoint = CreateDefaultSubobject<USceneComponent>(TEXT("Spawn Point"));
ProjectileSpawnPoint->SetupAttachment(TurretMesh);
}
// Called when the game starts or when spawned
void ABasePawn::RotateTurret(FVector LookAtTarget)
{
FVector ToTarget = LookAtTarget - TurretMesh->GetComponentLocation();
FRotator LookAtRotation = FRotator(0.f, ToTarget.Rotation().Yaw, 0.f);
TurretMesh->SetWorldRotation(FMath::RInterpTo(TurretMesh->GetComponentRotation(),
LookAtRotation, UGameplayStatics::GetWorldDeltaSeconds(this), 2.f));
}
void ABasePawn::Fire()
{
FVector Location = ProjectileSpawnPoint->GetComponentLocation();
FRotator Rotation = ProjectileSpawnPoint->GetComponentRotation();
auto Projectile = GetWorld()->SpawnActor<AProjectile>(ProjectileClass, Location, Rotation);
Projectile->SetOwner(this);
}
Tank.h:
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include “CoreMinimal.h”
#include “BasePawn.h”
#include “Tank.generated.h”
/**
*/
UCLASS()
class TOONTANKS_API ATank : public ABasePawn
{
GENERATED_BODY()
public:
ATank();
// Called to bind functionality to input
virtual void SetupPlayerInputComponent(class UInputComponent* PlayerInputComponent) override;
// Called every frame
virtual void Tick(float DeltaTime) override;
protected:
// Called when the game starts or when spawned
virtual void BeginPlay() override;
private:
UPROPERTY(VisibleAnywhere, Category = "Components")
class USpringArmComponent* SpringArm;
UPROPERTY(VisibleAnywhere, Category = "Components")
class UCameraComponent* Camera;
UPROPERTY(EditAnywhere, Category = "Movement")
float Speed = 200.f;
UPROPERTY(EditAnywhere, Category = "Movement")
float TurnRate = 45.f;
void Move(float Value);
void Turn(float Value);
APlayerController* PlayerControllerRef;
};
Tank.cpp:
// Fill out your copyright notice in the Description page of Project Settings.
#include “Tank.h”
#include “GameFramework/SpringArmComponent.h”
#include “Camera/CameraComponent.h”
#include “Components/InputComponent.h”
#include “Kismet/GameplayStatics.h”
ATank::ATank()
{
SpringArm = CreateDefaultSubobject<USpringArmComponent>(TEXT("Spring Arm"));
SpringArm->SetupAttachment(RootComponent);
Camera = CreateDefaultSubobject<UCameraComponent>(TEXT("Camera"));
Camera->SetupAttachment(SpringArm);
}
// Called to bind functionality to input
void ATank::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)
{
Super::SetupPlayerInputComponent(PlayerInputComponent);
PlayerInputComponent->BindAxis(TEXT("MoveForward"), this, &ATank::Move);
PlayerInputComponent->BindAxis(TEXT("Turn"), this, &ATank::Turn);
PlayerInputComponent->BindAction(TEXT("Fire"), IE_Pressed, this, &ATank::Fire);
}
// Called every frame
void ATank::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
FHitResult HitResult;
if (PlayerControllerRef)
{
PlayerControllerRef->GetHitResultUnderCursor(ECollisionChannel::ECC_Visibility, false, HitResult);
//DrawDebugSphere(GetWorld(), HitResult.ImpactPoint, 25.f, 12, FColor::Red, false, -1.f);
RotateTurret(HitResult.ImpactPoint);
}
}
void ATank::BeginPlay()
{
Super::BeginPlay();
PlayerControllerRef = Cast<APlayerController>(GetController());
}
void ATank::Move(float Value)
{
FVector DeltaLocation = FVector::ZeroVector;
DeltaLocation.X = Value * Speed * UGameplayStatics::GetWorldDeltaSeconds(this);
AddActorLocalOffset(DeltaLocation, true);
}
void ATank::Turn(float Value)
{
FRotator DeltaRotation = FRotator::ZeroRotator;
DeltaRotation.Yaw = Value * TurnRate * UGameplayStatics::GetWorldDeltaSeconds(this);
AddActorLocalRotation(DeltaRotation, true);
}
Tower.h:
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include “CoreMinimal.h”
#include “BasePawn.h”
#include “Tower.generated.h”
/**
*/
UCLASS()
class TOONTANKS_API ATower : public ABasePawn
{
GENERATED_BODY()
public:
virtual void Tick(float DeltaTime) override;
protected:
// Called when the game starts or when spawned
virtual void BeginPlay() override;
private:
class ATank* Tank;
UPROPERTY(EditDefaultsOnly, Category = "Combat")
float FireRange = 300.f;
FTimerHandle FireRateTimerHandle;
float FireRate = 2.f;
void CheckFireCondition();
bool InFireRange();
};
Tower.cpp:
// Fill out your copyright notice in the Description page of Project Settings.
#include “Tower.h”
#include “Tank.h”
#include “Kismet/GameplayStatics.h”
#include “TimerManager.h”
// Called every frame
void ATower::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
if(InFireRange())
{
RotateTurret(Tank->GetActorLocation());
}
}
void ATower::BeginPlay()
{
Super::BeginPlay();
Tank = Cast<ATank>(UGameplayStatics::GetPlayerPawn(this, 0));
GetWorldTimerManager().SetTimer(FireRateTimerHandle, this, &ATower::CheckFireCondition, FireRate, true);
}
void ATower::CheckFireCondition()
{
if(InFireRange())
{
Fire();
}
}
bool ATower::InFireRange()
{
if(Tank)
{
float Distance = FVector::Dist(GetActorLocation(), Tank->GetActorLocation());
if(Distance <= FireRange)
{
return true;
}
}
return false;
}
Projectile.cpp:
// Fill out your copyright notice in the Description page of Project Settings.
#include “Projectile.h”
#include “GameFramework/ProjectileMovementComponent.h”
#include “GameFramework/DamageType.h”
#include “Kismet/GameplayStatics.h”
// Sets default values
AProjectile::AProjectile()
{
// 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;
ProjectileMesh = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("Projectile Mesh"));
RootComponent = ProjectileMesh;
ProjectileMovementComponent = CreateDefaultSubobject<UProjectileMovementComponent>(TEXT("Projectile Movement Component"));
ProjectileMovementComponent->MaxSpeed = 1300.f;
ProjectileMovementComponent->InitialSpeed = 1300.f;
}
// Called when the game starts or when spawned
void AProjectile::BeginPlay()
{
Super::BeginPlay();
ProjectileMesh->OnComponentHit.AddDynamic(this, &AProjectile::OnHit);
}
// Called every frame
void AProjectile::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
}
void AProjectile::OnHit(UPrimitiveComponent *HitComp,
AActor *OtherActor,
UPrimitiveComponent *OtherComp,
FVector NormalImpulse,
const FHitResult& Hit)
{
auto MyOwner = GetOwner();
UE_LOG(LogTemp, Warning, TEXT("Hit"));
if (MyOwner == nullptr) return;
auto MyOwnerInstigator = MyOwner->GetInstigatorController();
auto DamageTypeClass = UDamageType::StaticClass();
if (OtherActor && OtherActor != this && OtherActor != MyOwner)
{
UGameplayStatics::ApplyDamage(OtherActor, Damage, MyOwnerInstigator, this, DamageTypeClass);
}
}
Projectile.h:
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include “CoreMinimal.h”
#include “GameFramework/Actor.h”
#include “Projectile.generated.h”
UCLASS()
class TOONTANKS_API AProjectile : public AActor
{
GENERATED_BODY()
public:
// Sets default values for this actor's properties
AProjectile();
protected:
// Called when the game starts or when spawned
virtual void BeginPlay() override;
private:
UPROPERTY(EditDefaultsOnly, Category = "Combat")
UStaticMeshComponent* ProjectileMesh;
UPROPERTY(VisibleAnywhere, Category = "Combat")
class UProjectileMovementComponent* ProjectileMovementComponent;
UFUNCTION()
void OnHit(UPrimitiveComponent*
HitComp, AActor* OtherActor,
UPrimitiveComponent* OtherComp,
FVector NormalImpulse,
const FHitResult& Hit);
UPROPERTY(EditAnywhere, Category = "Combat")
float Damage = 50.f;
public:
// Called every frame
virtual void Tick(float DeltaTime) override;
};
HealthComponent:
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include “CoreMinimal.h”
#include “GameFramework/Actor.h”
#include “Projectile.generated.h”
UCLASS()
class TOONTANKS_API AProjectile : public AActor
{
GENERATED_BODY()
public:
// Sets default values for this actor's properties
AProjectile();
protected:
// Called when the game starts or when spawned
virtual void BeginPlay() override;
private:
UPROPERTY(EditDefaultsOnly, Category = "Combat")
UStaticMeshComponent* ProjectileMesh;
UPROPERTY(VisibleAnywhere, Category = "Combat")
class UProjectileMovementComponent* ProjectileMovementComponent;
UFUNCTION()
void OnHit(UPrimitiveComponent*
HitComp, AActor* OtherActor,
UPrimitiveComponent* OtherComp,
FVector NormalImpulse,
const FHitResult& Hit);
UPROPERTY(EditAnywhere, Category = "Combat")
float Damage = 50.f;
public:
// Called every frame
virtual void Tick(float DeltaTime) override;
};
HealthComponent.cpp:
// Fill out your copyright notice in the Description page of Project Settings.
#include “GameFramework/Actor.h”
#include “HealthComponent.h”
// Sets default values for this component’s properties
UHealthComponent::UHealthComponent()
{
// 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 UHealthComponent::BeginPlay()
{
Super::BeginPlay();
Health = MaxHealth;
GetOwner()->OnTakeAnyDamage.AddDynamic(this, &UHealthComponent::DamageTaken);
}
// Called every frame
void UHealthComponent::TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction)
{
Super::TickComponent(DeltaTime, TickType, ThisTickFunction);
// ...
}
void UHealthComponent::DamageTaken(AActor *DamagedActor, float Damage, const UDamageType *DamageType, class AController *Instigator,
AActor *DamageCauser)
{
UE_LOG(LogTemp, Warning, TEXT("You Have Been Hit"));
if (Damage <= 0.f) return;
Health -= Damage;
UE_LOG(LogTemp, Warning, TEXT("Health: %f"), Health);
}