Good evening.
I am currently working on Section Five of the Unreal Engine 4 and C++ course, having reached video lecture 146: Spawning Actors. I believe I have followed along with the code as precisely as I could (aside from a couple of refactoring omissions (apparent on PawnTank.h) in addition to creating and setting up the various actors (the Tank and Turret) as displayed to the greatest of my ability. However, even with everything, once I arrived at the end of the lecture and, consequently, my following along, when I compiled my project and played her, I found that the projectiles that spawn from the Tank and the Turret correctly spawn, yet immediately fall to the floor if not colliding with another object such as the actors or another projectile. That problem appeared soon after I fixed a previous problem where I was receiving error C4458 for a hidden class member involving InitialLifeSpan (I searched through the forums and thankfully found a solution that involved
#pragma warning(push)
#pragma warning(disable 4458)
as apparently that is a Visual Studio issue rather than an actual user typo or logic issue. Now, however, I am faced with the issue I have above. May there be any assistance that may be graciously offered, please? I would be most appreciative for any help.
ProjectileBase.cpp
// Fill out your copyright notice in the Description page of Project Settings.
#include "ProjectileBase.h"
#include "Components/StaticMeshComponent.h"
#include "GameFramework/ProjectileMovementComponent.h"
#include "Kismet/GameplayStatics.h"
#include "Particles/ParticleSystemComponent.h"
#pragma warning(push)
#pragma warning(disable: 4458)
// Sets default values
AProjectileBase::AProjectileBase()
{
// 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;
ProjectileMesh = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("Projectile Mesh"));
RootComponent = ProjectileMesh;
ProjectileMovement = CreateDefaultSubobject<UProjectileMovementComponent>(TEXT("Projectile Movement"));
}
// Called when the game starts or when spawned
void AProjectileBase::BeginPlay()
{
Super::BeginPlay();
ProjectileMovement->InitialSpeed = MovementSpeed;
ProjectileMovement->MaxSpeed = MovementSpeed;
float InitialLifeSpan = 3.0f;
}
ProjectileBase.h
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "ProjectileBase.generated.h"
class UProjectileMovementComponent;
UCLASS()
class TOONTANKS_API AProjectileBase : public AActor
{
GENERATED_BODY()
private:
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Components", meta = (AllowPrivateAccess = "true"))
UProjectileMovementComponent* ProjectileMovement;
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Components", meta = (AllowPrivateAccess = "true"))
UStaticMeshComponent* ProjectileMesh;
UPROPERTY(EditDefaultsOnly, Category = "Damage")
TSubclassOf<UDamageType> DamageType;
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Move", meta = (AllowPrivateAccess = "true"))
float MovementSpeed = 1300;
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Damage", meta = (AllowPrivateAccess = "true"))
float Damage = 50;
public:
// Sets default values for this actor's properties
AProjectileBase();
protected:
// Called when the game starts or when spawned
virtual void BeginPlay() override;
};
PawnBase.cpp
// Fill out your copyright notice in the Description page of Project Settings.
#include "PawnBase.h"
#include "Components/CapsuleComponent.h"
#include "ToonTanks/Actors/ProjectileBase.h"
// Sets default values
APawnBase::APawnBase()
{
// 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(RootComponent);
TurretMesh = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("Turret Mesh"));
TurretMesh->SetupAttachment(BaseMesh);
ProjectileSpawnPoint = CreateDefaultSubobject<USceneComponent>(TEXT("Projectile Spawn Point"));
ProjectileSpawnPoint->SetupAttachment(TurretMesh);
}
void APawnBase::RotateTurret(FVector LookAtTarget)
{
FVector LookAtTargetCleaned = FVector(LookAtTarget.X, LookAtTarget.Y, TurretMesh->GetComponentLocation().Z);
FVector StartLocation = TurretMesh->GetComponentLocation();
FRotator TurretRotation = FVector(LookAtTargetCleaned - StartLocation).Rotation();
TurretMesh->SetWorldRotation(TurretRotation);
}
void APawnBase::Fire()
{
// Get ProjectileSpawnPoint Location && Rotation -> Spawn Projectile class at Location firing towards Rotation
UE_LOG(LogTemp, Warning, TEXT("Fire Condition Checked"));
if (ProjectileClass)
{
FVector SpawnLocation = ProjectileSpawnPoint->GetComponentLocation();
FRotator SpawnRotation = ProjectileSpawnPoint->GetComponentRotation();
AProjectileBase* TempProjectile = GetWorld()->SpawnActor<AProjectileBase>(ProjectileClass, SpawnLocation, SpawnRotation);
TempProjectile->SetOwner(this);
}
}
void APawnBase::HandleDestruction()
{
// --- Universal functionality ---
// Play death effects particle, sound and camera shake
// --- Then do Child overrides ---
// -- PawnTurret - Inform GameMode Turret died -> Then Destroy() self
// -- PawnTank - Inform GameMode Player died -> Then Hide() all components && stop movement input
}
PawnBase.h
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Pawn.h"
#include "PawnBase.generated.h"
class UCapsuleComponent;
class AProjectileBase;
UCLASS()
class TOONTANKS_API APawnBase : public APawn
{
GENERATED_BODY()
private:
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"))
UStaticMeshComponent* TurretMesh;
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Components", meta = (AllowPrivateAccess = "true"))
USceneComponent* ProjectileSpawnPoint;
// VARIABLES
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Projectile Type", meta = (AllowPrivateAccess = "true"))
TSubclassOf<AProjectileBase> ProjectileClass;
public:
// Sets default values for this pawn's properties
APawnBase();
protected:
void RotateTurret(FVector LookAtTarget);
void Fire();
virtual void HandleDestruction();
};
PawnTank.cpp
// Fill out your copyright notice in the Description page of Project Settings.
#include "PawnTank.h"
#include "GameFramework/SpringArmComponent.h"
#include "Camera/CameraComponent.h"
APawnTank::APawnTank()
{
SpringArm = CreateDefaultSubobject<USpringArmComponent>(TEXT("Spring Arm"));
SpringArm->SetupAttachment(RootComponent);
Camera = CreateDefaultSubobject<UCameraComponent>(TEXT("Camera"));
Camera->SetupAttachment(SpringArm);
}
// Called when the game starts or when spawned
void APawnTank::BeginPlay()
{
Super::BeginPlay();
PlayerControllerRef = Cast<APlayerController>(GetController());
}
void APawnTank::HandleDestruction()
{
Super::HandleDestruction();
// Hide Player, TODO - Create new function to handle this
}
// Called every frame
void APawnTank::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
Rotate();
Move();
if (PlayerControllerRef)
{
FHitResult TraceHitResult;
PlayerControllerRef->GetHitResultUnderCursor(ECC_Visibility, false, TraceHitResult);
FVector HitLocation = TraceHitResult.ImpactPoint;
RotateTurret(HitLocation);
}
}
// Called to bind functionality to input
void APawnTank::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)
{
Super::SetupPlayerInputComponent(PlayerInputComponent);
PlayerInputComponent->BindAxis("MoveForward", this, &APawnTank::CalculateMoveInput);
PlayerInputComponent->BindAxis("Turn", this, &APawnTank::CalculateRotateInput);
PlayerInputComponent->BindAction("Fire", IE_Pressed, this, &APawnTank::Fire);
}
void APawnTank::CalculateMoveInput(float Value)
{
MoveDirection = FVector(Value * MoveSpeed * GetWorld()->DeltaTimeSeconds, 0, 0);
}
void APawnTank::CalculateRotateInput(float Value)
{
float RotateAmount = Value * RotateSpeed * GetWorld()->DeltaTimeSeconds;
FRotator Rotation = FRotator(0, RotateAmount, 0);
RotationDirection = FQuat(Rotation);
}
void APawnTank::Move()
{
AddActorLocalOffset(MoveDirection, true);
}
void APawnTank::Rotate()
{
AddActorLocalRotation(RotationDirection, true);
}
PawnTank.h
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "PawnBase.h"
#include "PawnTank.generated.h"
class USpringArmComponent;
class UCameraComponent;
UCLASS()
class TOONTANKS_API APawnTank : public APawnBase
{
GENERATED_BODY()
private:
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Components", meta = (AllowPrivateAccess = "true"))
USpringArmComponent* SpringArm;
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Components", meta = (AllowPrivateAccess = "true"))
UCameraComponent* Camera;
FVector MoveDirection;
FQuat RotationDirection;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Movement", meta = (AllowPrivateAccess = "true"))
float MoveSpeed = 100.0f;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Movement", meta = (AllowPrivateAccess = "true"))
float RotateSpeed = 100.0f;
APlayerController* PlayerControllerRef;
void CalculateMoveInput(float Value);
void CalculateRotateInput(float Value);
void Move();
void Rotate();
public:
APawnTank();
// Called every frame
virtual void Tick(float DeltaTime) override;
// Called to bind functionality to input
virtual void SetupPlayerInputComponent(class UInputComponent* PlayerInputComponent) override;
protected:
// Called when the game starts or when spawned
virtual void BeginPlay() override;
virtual void HandleDestruction() override;
};
PawnTurret.cpp
// Fill out your copyright notice in the Description page of Project Settings.
#include "PawnTurret.h"
#include "Kismet/GameplayStatics.h"
#include "PawnTank.h"
// Called when the game starts or when spawned
void APawnTurret::BeginPlay()
{
Super::BeginPlay();
GetWorld()->GetTimerManager().SetTimer(FireRateTimerHandle, this, &APawnTurret::CheckFireCondition, FireRate, true);
PlayerPawn = Cast<APawnTank>(UGameplayStatics::GetPlayerPawn(this, 0));
}
void APawnTurret::HandleDestruction()
{
Super::HandleDestruction();
Destroy();
}
// Called every frame
void APawnTurret::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
if (!PlayerPawn || ReturnDistanceToPlayer() > FireRange)
{
return;
}
RotateTurret(PlayerPawn->GetActorLocation());
}
void APawnTurret::CheckFireCondition()
{
// If Player == null || is Dead THEN BAIL!!
if (!PlayerPawn)
{
return;
}
// If Player IS in range THEN FIRE!!
if(ReturnDistanceToPlayer() <= FireRange)
{
Fire();
}
}
float APawnTurret::ReturnDistanceToPlayer()
{
if (!PlayerPawn)
{
return 0.0f;
}
return FVector::Dist(PlayerPawn->GetActorLocation(), GetActorLocation());
}
PawnTurret.h
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "PawnBase.h"
#include "PawnTurret.generated.h"
class APawnTank;
UCLASS()
class TOONTANKS_API APawnTurret : public APawnBase
{
GENERATED_BODY()
private:
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Combat", meta = (AllowPrivateAccess = "true"))
float FireRate = 2.0f;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Combat", meta = (AllowPrivateAccess = "true"))
float FireRange = 500.0f;
void CheckFireCondition();
FTimerHandle FireRateTimerHandle;
APawnTank* PlayerPawn;
float ReturnDistanceToPlayer();
public:
// Called every frame
virtual void Tick(float DeltaTime) override;
protected:
// Called when the game starts or when spawned
virtual void BeginPlay() override;
virtual void HandleDestruction() override;
};
Once again, thank you all so very much for any and all assistance that may be offered.
(I can offer specific screenshots of the BP_PawnTank and BP_PawnTurret though I will not be including them in the original post as there are many different tabs and pages that would need to be screenshotted and I already code dumped above.)
Thank you all so very much.