Hi, so I’m relevantly new to Unreal (coming from a mostly Unity background with some C++), and I’m trying to learn by making a prototype. At the moment, I’m trying to make a spawner that spawns a moving obstacle every couple of seconds. However, whenever I try to call the SpawnWall() function anywhere outside of BeginPlay() and not in a timer, the obstacles seem to be invisible and be missing collision. I’ve tried looking up various topics and tutorials, but I haven’t been able to figure out an answer, so I’m wondering if anyone knows?
Here’s my code:
ObstacleSpawn.h
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "ObstacleSpawn.generated.h"
class AObstacleWall;
UCLASS()
class RUNNER_API AObstacleSpawn : public AActor
{
GENERATED_BODY()
public:
// Sets default values for this actor's properties
AObstacleSpawn();
UPROPERTY(EditAnywhere, Category = "Spawn")
TSubclassOf<class AObstacleWall> SWall;
//class AObstacleWall* SpawnedWall;
protected:
// Called when the game starts or when spawned
virtual void BeginPlay() override;
public:
// Called every frame
virtual void Tick(float DeltaTime) override;
UFUNCTION(BlueprintCallable)
void SpawnWall();
float SpawnInterval;
FTimerHandle THandle;
};
ObstacleSpawn.cpp
// Fill out your copyright notice in the Description page of Project Settings.
#include "ObstacleSpawn.h"
#include "ObstacleWall.h"
#include "Engine/World.h"
#include "TimerManager.h"
// Sets default values
AObstacleSpawn::AObstacleSpawn()
{
// 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;
SpawnInterval = 2.f;
}
// Called when the game starts or when spawned
void AObstacleSpawn::BeginPlay()
{
//SpawnWall();
GetWorldTimerManager().SetTimer(THandle, this, &AObstacleSpawn::SpawnWall, SpawnInterval, true, 1.0f);
Super::BeginPlay();
}
// Called every frame
void AObstacleSpawn::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
}
void AObstacleSpawn::SpawnWall()
{
if (SWall) {
FActorSpawnParameters SpawnParams;
SpawnParams.Owner = this;
//SpawnParams.Instigator = Instigator;
int offset = rand() % 4;
offset *= 90;
FRotator StartRot = FRotator(GetActorRotation().Pitch, GetActorRotation().Yaw, offset);
FVector StartLoc = GetActorLocation();
GetWorld()->SpawnActor<AObstacleWall>(SWall, StartLoc, StartRot, SpawnParams);
}
}
ObstacleWall.h
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "ObstacleWall.generated.h"
class ARotatingStage;
class UBoxComponent;
class ADestroyVolume;
UCLASS()
class RUNNER_API AObstacleWall : public AActor
{
GENERATED_BODY()
public:
// Sets default values for this actor's properties
AObstacleWall();
float RushSpeed;
UPROPERTY(VisibleAnywhere, BlueprintReadonly)
UStaticMeshComponent* RootEmpty;
UPROPERTY(VisibleAnywhere, BlueprintReadonly, Category = "Wall")
UStaticMeshComponent* FirstWall;
UPROPERTY(VisibleAnywhere, BlueprintReadonly, Category = "Wall")
UStaticMeshComponent* SecondWall;
UPROPERTY(VisibleAnywhere, BlueprintReadonly)
UBoxComponent* FirstBox;
UPROPERTY(VisibleAnywhere, BlueprintReadonly)
UBoxComponent* SecondBox;
float RollVal;
float InputVal;
protected:
// Called when the game starts or when spawned
virtual void BeginPlay() override;
class ARotatingStage* RotStage;
class ADestroyVolume* DesVol;
public:
// Called every frame
virtual void Tick(float DeltaTime) override;
UFUNCTION()
void OnOverlapBegin(class UPrimitiveComponent* OverlappedComp, class AActor* OtherActor, class UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult);
};
ObstacleWall.cpp
// Fill out your copyright notice in the Description page of Project Settings.
#include "ObstacleWall.h"
#include "Components/BoxComponent.h"
#include "Components/StaticMeshComponent.h"
#include "EngineUtils.h"
#include "RotatingStage.h"
#include "DestroyVolume.h"
// Sets default values
AObstacleWall::AObstacleWall()
{
// 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;
RootEmpty = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("RootEmpty"));
FirstWall = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("FirstWall"));
SecondWall = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("SecondWall"));
RootEmpty->SetupAttachment(RootComponent);
FirstWall->SetupAttachment(RootEmpty);
SecondWall->SetupAttachment(RootEmpty);
RootEmpty->SetVisibility(true);
FirstWall->SetVisibility(true);
SecondWall->SetVisibility(true);
RootEmpty->SetEnableGravity(false);
FirstWall->SetEnableGravity(false);
SecondWall->SetEnableGravity(false);
FirstBox = CreateDefaultSubobject<UBoxComponent>(TEXT("FirstBox"));
FirstBox->SetupAttachment(FirstWall);
FirstBox->OnComponentBeginOverlap.AddDynamic(this, &AObstacleWall::OnOverlapBegin);
SecondBox = CreateDefaultSubobject<UBoxComponent>(TEXT("SecondBox"));
SecondBox->SetupAttachment(SecondWall);
SecondBox->OnComponentBeginOverlap.AddDynamic(this, &AObstacleWall::OnOverlapBegin);
RushSpeed = 20.f;
}
// Called when the game starts or when spawned
void AObstacleWall::BeginPlay()
{
for (TActorIterator<ARotatingStage> StageItr(GetWorld()); StageItr; ++StageItr) {
RotStage = Cast<ARotatingStage>(*StageItr);
}
RollVal = RotStage->RollValue;
for (TActorIterator<ADestroyVolume> StageItr(GetWorld()); StageItr; ++StageItr) {
DesVol = Cast<ADestroyVolume>(*StageItr);
}
Super::BeginPlay();
}
// Called every frame
void AObstacleWall::Tick(float DeltaTime)
{
FVector Direction = FVector(GetActorLocation().X - RushSpeed, GetActorLocation().Y, GetActorLocation().Z);
SetActorLocation(Direction);
InputVal = RotStage->InputValue;
FRotator NewRotation = FRotator(GetActorRotation().Pitch, GetActorRotation().Yaw, GetActorRotation().Roll + InputVal * RollVal);
FQuat QuatRotation = FQuat(NewRotation);
SetActorRotation(QuatRotation);
Super::Tick(DeltaTime);
}
void AObstacleWall::OnOverlapBegin(class UPrimitiveComponent* OverlappedComp, class AActor* OtherActor, class UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult)
{
if (OtherActor && (OtherActor != this) && OtherComp && (OtherActor==DesVol))
{
Destroy();
}
else
{
//OtherActor->Destroy();
//RushSpeed *= -1.f;
OtherActor->SetActorEnableCollision(false);
OtherActor->SetActorHiddenInGame(true);
DesVol->SetActorEnableCollision(false);
}
}
Note: There’s a small possibility that it may have something to do with the RootEmpty object in ObstacleWall, the object the other box actors are childed to and that I need to center them together. If so, what’s a better object type to use/alternative?