Help with a SpawnActor/Timer Problem?

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?

Not seeing RootComponent = <anything> in there.

Is it done in a parent class?

And probably the most important part, WHRE IS THE CODE THAT IS CALLING SpawnWall() ?

Its not ever going to spawn anything if it does not get called.

So an update since I posted:

-There was a typo, where the walls were invisible and without collision when SpawnWall() was called outside of BeginPlay() or in a timer.

-I’m new to Unreal. Does RootComponent have to always =<something> on an actor?

-SpawnWall() is right now being called in BeginPlay() of ObstacleSpawn.cpp in a timer.

I found out why the obstacle walls were invisible was because their own colliders would turn themselves off upon spawn. So, now the code looks like this:



void AObstacleWall::OnOverlapBegin(class UPrimitiveComponent* OverlappedComp, class AActor* OtherActor, class UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult)
{

    if (OtherActor && (OtherActor != this) && OtherComp )
    {

        if(OtherActor==DesVol)
        {
            Destroy();
        }
        else if(OtherActor==Main)
        {  
            OtherActor->SetActorEnableCollision(false);
            OtherActor->SetActorHiddenInGame(true);

            DesVol->SetActorEnableCollision(false);
        }
        else
        { }
    }
}

However, now, I can’t get these obstacles to collide with the main character (what Main refers to) for some reason. It’s as if the main character doesn’t have a capsule component, which it definitely has. I tried casting to it, but no luck. Anyone know anything about it?

Yes, to be able to change set the transform (location, rotation & scale) of an actor in a scene, its needs a scene component based root component.
If not, any actor you spawn will be located at 0,0,0 in the game world (origin).
An actor based of a Blueprints automatically creates a “DefaultRootComponent” or something like that.
C++ ones do not.

Just add something like this to you actor:



    USceneComponent* TransformComponent = CreateDefaultSubobject<USceneComponent>(TEXT("TransformComponent0"));
    TransformComponent->SetVisibility(false);
    RootComponent = TransformComponent;


And she’ll be right, as far as transform is concerned.

Be careful you don’t replace the root component with a component set to “Editor Only”, as this will be removed when your game is packaged, reverting its location to 0,0,0.

I applied a root component to both the obstacle wall actor and the spawn actor (I assume that the default capsule is a character’s default root component?), but I still seem to have issues where the collision still doesn’t seem to work between the obstacle wall actor and the main character.

Something to clarify: the obstacle wall actors collide fine with DesVol, another actor of a class called destroy volume that’s basically just a box component. However, even after trying to copy the settings for DesVol’s collision (OverallAllDynamic), there doesn’t seem to be collision between the wall actors and main.

It also used to work when I just call SpawnActor from BeginPlay() in the wall actors, but now not even that works. I assume it’s a code based reason or some setting I don’t know, but aside from a capsule component childed to the static mesh, main has no code on overlap, so whatever is happening is in the wall actors. I’ve been wracking my brain as to what happened.

Collision issues are a whole other kettle of fish, so to speak.

From memory, overlap events only trigger if both components have the overlap events option enabled.
So you need to make sure you call “SetGenerateOverlapEvents(true);” for all the components in question or set it in their Blueprints defaults.

Still no dice. I’ve made sure to set in both Blueprints and in code, but still nothing.

Other things I’ve tried:
-Moving the AddDynamic calls in obstacle wall actors to BeginPlay()
-Separated the colliding components to attach directly to the root component in the main character.
-Changed the non-default Capsule component on main character to a Box component.
-Created a new Blueprint derived from the Main Character class and only changed the transforms of the spring arm, camera, etc.
-Sent a debug message in else{} in the OnOverlapBegin() to see if it’s just not identifying the main character as Main.
-Breakpointed the latter. When it collides with DesVol, it does cast the main character. However, OnOverlapBegin() doesn’t seem to even fire when overlapping with the main character.

At this point, I’ll just add the Main Character class code, just in case.
MainCharacter.h


// Fill out your copyright notice in the Description page of Project Settings.

#pragma once

#include "CoreMinimal.h"
#include "GameFramework/Character.h"
#include "MainCharacter.generated.h"

class USpringArmComponent;
class UCameraComponent;
class UStaticMeshComponent;
class ARotatingStage;
class UCapsuleComponent;
class AObstacleSpawn;
class UBoxComponent;

UCLASS()
class RUNNER_API AMainCharacter : public ACharacter
{
    GENERATED_BODY()

public:
    // Sets default values for this character's properties
    AMainCharacter();

    UPROPERTY(EditAnywhere, BlueprintReadonly, Category="Camera")
        class USpringArmComponent* SpringArm;

    UPROPERTY(EditAnywhere, BlueprintReadonly, Category = "Camera")
        class UCameraComponent* Camera;

    UPROPERTY(VisibleAnywhere, BlueprintReadonly, Category = "Player")
        UStaticMeshComponent* CubeMesh;

    //UPROPERTY(VisibleAnywhere, BlueprintReadonly, Category = "Player")
        //UCapsuleComponent* Capsule;

    UPROPERTY(VisibleAnywhere, BlueprintReadonly, Category = "Player")
        UBoxComponent* BoxCollider;

    class ARotatingStage* RotatingStage;

protected:
    // Called when the game starts or when spawned
    UFUNCTION(BluePrintCallable)
    virtual void BeginPlay() override;

    float MoveSpeed;

    float ClampRange;


public:    
    // Called every frame
    UFUNCTION(BluePrintCallable)
    virtual void Tick(float DeltaTime) override;

    // Called to bind functionality to input
    UFUNCTION(BluePrintCallable)
    virtual void SetupPlayerInputComponent(class UInputComponent* PlayerInputComponent) override;

    //FVector CurVelocty;
    UFUNCTION(BluePrintCallable)
        void MoveRight(float Value);

    UFUNCTION(BluePrintCallable)
        void RotateRight(float Value);

};


MainCharacter.cpp


// Fill out your copyright notice in the Description page of Project Settings.


#include "MainCharacter.h"
#include "Components/InputComponent.h"
#include "GameFramework/SpringArmComponent.h"
#include "Camera/CameraComponent.h"
#include "Components/StaticMeshComponent.h"
#include "Components/CapsuleComponent.h"
#include "GameFramework/Controller.h"
#include "GameFramework/CharacterMovementComponent.h"
#include "RotatingStage.h"
#include "EngineUtils.h"
#include "Components/BoxComponent.h"

// Sets default values
AMainCharacter::AMainCharacter()
{
     // Set this character to call Tick() every frame.  You can turn this off to improve performance if you don't need it.
    //Set to false unless Tick needed
    PrimaryActorTick.bCanEverTick = true;

    CubeMesh = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("CubeMesh"));
    CubeMesh->SetupAttachment(RootComponent);

    SpringArm = CreateDefaultSubobject<USpringArmComponent>(TEXT("SpringArm"));
    //SpringArm->SetupAttachment(RootComponent);
    Camera = CreateDefaultSubobject<UCameraComponent>(TEXT("Camera"));
    Camera->SetupAttachment(SpringArm);

    CubeMesh->SetEnableGravity(false);

    //Capsule = CreateDefaultSubobject<UCapsuleComponent>(TEXT("Capsule"));
    //Capsule->SetEnableGravity(false);
    //Capsule->SetGenerateOverlapEvents(true);
    //Capsule->SetupAttachment(CubeMesh);

    BoxCollider = CreateDefaultSubobject<UBoxComponent>(TEXT("Box Collider"));
    BoxCollider->SetEnableGravity(false);
    BoxCollider->SetGenerateOverlapEvents(true);
    BoxCollider->SetupAttachment(RootComponent);


    MoveSpeed = 15.f;
    ClampRange = 600.f;

    RotatingStage = nullptr; //Cast<ARotatingStage>();

}

// Called when the game starts or when spawned
void AMainCharacter::BeginPlay()
{
    for (TActorIterator<ARotatingStage> StageItr(GetWorld()); StageItr; ++StageItr) {
        RotatingStage = Cast<ARotatingStage>(*StageItr);
    }
    Super::BeginPlay();

}

// Called every frame
void AMainCharacter::Tick(float DeltaTime)
{
    Super::Tick(DeltaTime);
    /*if (!CurVelocty.IsZero())
    {
        FVector NewLocation = GetActorLocation() + (CurVelocty*DeltaTime);
        SetActorLocation(NewLocation);
    }*/

}

// Called to bind functionality to input
void AMainCharacter::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)
{
    Super::SetupPlayerInputComponent(PlayerInputComponent);

    PlayerInputComponent->BindAxis("MoveRight", this, &AMainCharacter::MoveRight);
    PlayerInputComponent->BindAxis("RotateRight", this, &AMainCharacter::RotateRight);

}

void AMainCharacter::MoveRight(float Value)
{

    if ((Controller != NULL) && (Value != 0.0f))
    {
        /*const FRotator Rotation = Controller->GetControlRotation();
        const FRotator YawRotation(0, Rotation.Yaw, 0);

        const FVector Direction = FRotationMatrix(YawRotation).GetUnitAxis(EAxis::Y);
          AddMovementInput(Direction, Value);*/
        //AddMovementInput(GetActorRightVector(), Value);
        FVector Direction=FVector(GetActorLocation().X, GetActorLocation().Y+Value*MoveSpeed, GetActorLocation().Z);
        if (Direction.Y > ClampRange||Direction.Y < -ClampRange)
        {
            Direction=FVector(GetActorLocation().X, GetActorLocation().Y, GetActorLocation().Z);
        }

        SetActorLocation(Direction);

    }

    //CurVelocty.X = FMath::Clamp(Value, -1.0f, 1.0f)*100.0f;
}

void AMainCharacter::RotateRight(float Value)
{
    if (RotatingStage)
    {
        RotatingStage->RotateRight(Value);
    }
}



And yes, I know my controls are strange. I just really can’t have the character move based on physics right now for this project.

I found a work around. I commented out all of the logic in the obstacle wall class referring to the main character, and added OnOverlapBegin() to the main character class, so that the main character would detect if it gets hit, rather than if the wall hit the main character. Still doesn’t really explain the initial problem, but it works?