I just completed the toon tanks section, and I am trying to make some changes in the code to make the game better.
The Change I want to make:-
I want to let the enemy tower fire, only when the tank is in its range and no object is between tower and tank so that when the tower fires the projectile, it doesn’t collide with any wall.
The problem:-
- Actually I am not getting the correct location of the UStaticMeshComponent* Turret.
- And even If I manually set the location somewhat up it is not detecting my tank as a Word_Dynamic.
My Code
// 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:
// Called every frame
virtual void Tick(float DeltaTime) override;
void HandleDestruction();
protected:
// Called when the game starts or when spawned
virtual void BeginPlay() override;
private:
// class APawn* TankRef = nullptr;
class ATank* TankRef = nullptr;
FTimerHandle FireRateTimeHandle;
UPROPERTY(EditDefaultsOnly, Category = "Combat")
float FireRange = 700.f;
UPROPERTY(EditDefaultsOnly, Category = "Combat")
float FireRate = 1.0f;
bool CheckTankRange();
void CheckFireCondition();
UStaticMeshComponent* TurretRef;
void SettingUp();
};
Turret.cpp:-
// Fill out your copyright notice in the Description page of Project Settings.
#include "Tower.h"
#include "Kismet/GameplayStatics.h"
#include "Tank.h"
#include "TimerManager.h"
#include "DrawDebugHelpers.h"
#define OUT
void ATower::BeginPlay()
{
Super::BeginPlay();
SettingUp();
}
void ATower::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
if(CheckTankRange()) //Rotating the tower's turret every frame if the tank is in range
{
RotateTurret(TankRef->GetActorLocation()); //Using RotateTurret Function derived form BasePawn class and passing the TankLocation as target location
}
}
void ATower::CheckFireCondition() //Checks the condition for firing
{
if (!TankRef) {return;}
FHitResult HitResult;
FVector TurretLocation = TurretRef->GetComponentTransform().GetLocation();
FVector TurretRangeRocation = TurretRef->GetComponentTransform().GetRotation().Vector() * FireRange;
bool HasHit = GetWorld()->LineTraceSingleByChannel(OUT HitResult, TurretLocation, TurretRangeRocation, ECC_WorldDynamic);
DrawDebugLine(GetWorld(), TurretLocation, TurretRangeRocation, FColor::Red, true, 0.f, 0.f, 5.f);
if(HasHit)
{
UE_LOG(LogTemp, Warning, TEXT("%s"), *(HitResult.GetActor())->GetName());
}
if (Cast<ATank>(HitResult.GetActor()) == TankRef)
{
if (CheckTankRange() & TankRef->bTankAlive) //Checks if the tank is in range or not and tank is alive or not. If teh tank would be dead then tower will not fire any more
{
FireProjectile(); //after checking the above condition tower fires the projectile by using this function which is derived from BasePawn class
}
}
}
bool ATower::CheckTankRange() //This function checks if the tank is in range or not
{
if(!TankRef) {return false;}
float Distance = FVector::Dist(GetActorLocation(), TankRef->GetActorLocation()); //stores the distance between tower and tank
if(Distance <= FireRange) //If the distance is equal or less than the Fire Range then this function will return true
{
return true;
}
return false;
}
void ATower::HandleDestruction() //HandleDestruction for tower
{
Super::HandleDestruction(); //Calls every part of the fuction named same as this in parent class
Destroy(); //Destroy the tower from the level
}
void ATower::SettingUp()
{
// TankRef = UGameplayStatics::GetPlayerPawn(this, 0);
TankRef = Cast<ATank>(UGameplayStatics::GetPlayerPawn(this, 0)); //Getting pointer to Pawn by casting it to ATank
GetWorldTimerManager().SetTimer(FireRateTimeHandle, this, &ATower::CheckFireCondition, FireRate, true); //Setting to check the fire condition every FireRate Seconds
TArray<UActorComponent*> StaticMeshComponents = GetComponentsByClass(UStaticMeshComponent::StaticClass());
for (UActorComponent* StaticMesh : StaticMeshComponents)
{
if (StaticMesh->GetName() == TEXT("Turret") & StaticMesh->ComponentHasTag(TEXT("Turret")))
{
TurretRef = Cast<UStaticMeshComponent>(StaticMesh);
UE_LOG(LogTemp, Warning, TEXT("%s"), *TurretRef->GetName());
}
}
}
NOTE:- I am not providing the code of other files because according to me it won’t help you solve the problem.
Turret Blueprint:-
The visual representation of my ray tracing, which I am getting right now:-
Some important points about my code:-
- I assigned a tag to my turret component to verify in the code that we are taking correct 100% correct reference of the turret
- I Log out at every step to check the result of the code, and I am getting the correct reference to the turret.
- Even if you somehow solve to get the correct location of the turret, remember that ray tracing is not detecting tank as a world dynamic.