So, I have a spawner object, whose job it is to spawn Fence obstacles that move toward the player along the X axis. Through debug messages, I have confirmed that the Fence objects do indeed spawn, but I have yet to see one. Debug messages assure me that given their coordinates, they should pass through the view. I would assume that the meshes, even if they don’t have a material applied, would at least be somewhat visible in my window as a translucent figure, right?
Below is my code for the Spawner’s “tick”, as well as the Fence’s constructor and AssignMesh script. And for reference, the spawner’s state is automatically set to SPAWN_PLAY in its constructor.
AObstacleSpawner::Tick
// Called every frame
void AObstacleSpawner::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
switch (state) {
case SPAWN_PLAY:
//Get a reference to the game world so we can spawn new objects as needed
UWorld* World = GetWorld();
if (World) {
FActorSpawnParameters SpawnParams;
SpawnParams.Owner = this;
SpawnParams.Instigator = GetInstigator();
//Increment/Decrement timers, spawn objects
if (SpawnTimer <= 0.0f) {
//Spawn a new fence obstacle with appropriate height
int FenceHeight = FMath::RandRange(1, 8);
AFenceObstacle* NewFence = World->SpawnActor<AFenceObstacle>(SpawnParams);
FVector SpawnLocation = FVector(300.0f, 0.0f, 0.0f);
NewFence->SetActorLocation(SpawnLocation);
NewFence->ObstacleSpeed = ObstacleSpeed;
NewFence->HeightMarker = FenceHeight;
NewFence->AssignMesh();
//DEBUG: Show a prompt indicating that a new fence has been created
FVector loc = NewFence->GetActorLocation();
auto const debug_msg = FString::Printf(TEXT("New fence %i spawned. Height = %i. Spd = %f. X = %f. Y = %f. Z = %f"), FenceIndex, FenceHeight, ObstacleSpeed, loc.X, loc.Y, loc.Z);
GEngine->AddOnScreenDebugMessage(-1, 5.0f, FColor::Red, debug_msg);
//GUBED
Fences[FenceIndex] = NewFence;
FenceIndex++;
if (FenceIndex >= 4) {
FenceIndex = 0;
}
SpawnTimer = 5.0f; //<-- This will be modified later to depend on TimeElapsed
}
else {
SpawnTimer -= DeltaTime;
}
int ii;
FVector CurrentLocation;
if (TimeToAccel <= 0.0f) {
ObstacleSpeed += ObstacleAccel;
//Set the speeds of the fences
for (ii = 0; ii < 4; ii++) {
if (Fences[ii] != nullptr) {
Fences[ii]->ObstacleSpeed = ObstacleSpeed;
CurrentLocation = Fences[ii]->GetActorLocation();
if (CurrentLocation.X < -1000.0) {
Fences[ii]->Destroy();
Fences[ii] = nullptr;
}
}
}
TimeToAccel = 5.0f; //<-- This will be modified later to depend on TimeElapsed
}
else {
TimeToAccel -= DeltaTime;
}
TimeElapsed += DeltaTime;
}
}
}
AFenceObstacle::AFenceObstacle()
AFenceObstacle::AFenceObstacle()
{
// 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;
//Create the box component for collisions
BoxComponent = CreateDefaultSubobject<UBoxComponent>(TEXT("CollisionBox"));
check(BoxComponent != nullptr);
//Create a visible mesh for the fence. CreateDefaultSubobject cannot be called outside of constructors
if (!VisibleMesh) {
VisibleMesh = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("VisibleMesh"));
}
}
AFenceObstacle::AssignMesh()
//Assigns the appropriate mesh after creation (called externally)
void AFenceObstacle::AssignMesh() {
if (HeightMarker > 0 && HeightMarker < 9) {
//Assign the appropriate mesh to the fence based on the assigned HeightMarker
auto const Path = FString::Printf(TEXT("/Game/Meshes/sm_obstacle_fence_size_%i.sm_obstacle_fence_size_%i"), HeightMarker, HeightMarker);
auto Mesh = LoadObject<UStaticMesh>(nullptr, TEXT("FenceMesh"), *Path);
if (Mesh) {
VisibleMesh->SetStaticMesh(Mesh);
}
}
else {
//If an erroneous HeightMarker value was assigned, show that in a debug message
check(GEngine != nullptr);
//Display a debug message for five seconds
//The -1 "Key" vale argument prevents the message from being updated or refreshed.
auto const err_msg = FString::Printf(TEXT("AssignMesh: Invalid fence height value: %i"), HeightMarker);
GEngine->AddOnScreenDebugMessage(-1, 10.0f, FColor::Red, err_msg);
}
}