Download

Why is it so hard to get into c++ related to UE4?

Hello everyone,

I am literally trying now for 4 days to do one easy thing: Spawning an Actor into the world when the level begins, only by using c++.

The main reason for this might be that I don’t even understand where I should enter the code for this. I am working with Java, Java for Android, PHP etc. for several years but it doesn’t help at all.
Now my question:
When I create a default c++ vehicle project several cpp + h files are going to be created. There are also some C# files.

  1. What do the C# files like: Map1.Build.cs, Map1.Target.cs, Map1Editor.Target.cs ? Are they for compiling or do they call the c++ files in a way?
  2. Where is the entering point when the game starts within the c++ or C# files? For instance, Java has a main() - ok. I got files like: Map1.cpp, Map1GameMode.cpp, Map1Hud.cpp, Map1Pawn.cpp etc. (default c++ vehicle project). Where do I put my new cpp code which is then being executed when pressing the “play”-button?
  3. If it might be the case that my basic c++ knowledge is the problem, then please just tell me where to start. I watched several hours of tutorials which ended up using blueprints or drag and drop new actors into the world. I read parts of the documentation but I couldn’t find anything.

Example issue:
I created a new Actor c++ class and h file which looks like this:


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

#include "StreetActor1.h"
#include "Runtime/Engine/Classes/Engine/World.h"

// Sets default values
AStreetActor1::AStreetActor1()
{
     // 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;
}

// Called when the game starts or when spawned
void AStreetActor1::BeginPlay()
{
    Super::BeginPlay();
    FVector Location(0.0f, 0.0f, 0.0f);
    FRotator Rotation(0.0f, 0.0f, 0.0f);
    UWorld *world = GetWorld();
    FActorSpawnParameters SpawnInfo;
    world->SpawnActor<AStreetActor1>(Location, Rotation, SpawnInfo);
}

// Called every frame
void AStreetActor1::Tick(float DeltaTime)
{
    Super::Tick(DeltaTime);

}


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

#pragma once

#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "StreetActor1.generated.h"

UCLASS()
class MAP1_API AStreetActor1 : public AActor
{
    GENERATED_BODY()

public:    
    // Sets default values for this actor's properties
    AStreetActor1();

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

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

};

I know there is now Mesh or Material attached to, so nothing will spawn. However, in the world outliner the actor should be there. Of course, the actor won’t get created because there is no class call like “AStreetActor1->BeginPlay()”. Where do I put this class call into? Into the Map1.cpp?

I hope you understand what I am asking for. I did everything now to find the issue and I am asking myself if I am just to stupid to find the issue. I already commented on the last post of this question and it didn’t help. How to spawn actor in C++? Been trying for 9 hours now. - UE4 AnswerHub

Thank you for reading and I hope you have an idea of solving my confusion

They’re used for generating the game solution by the Unreal Build Tool.

There is a main, but it’s used by the engine to bootstrap everything. You likely want to override one of the callbacks in your GameMode:

Also take a look at the Actor Lifecycle to get a better understanding of what callbacks happen when:

Nah, you’re doing fine. It just takes time to learn the flow of the engine so you can manipulate things.

The problem with spawning your actor, is that u place the code in the BeginPlay method. As long this actor is not placed in the world (spawn by code or added in the editor), the BeginPlay will not be called. Another issue with your code is that whenever your StreetActor is spawned, it will call BeginPlay and spawn another StreetActor, which again will call BeginPlay … so u will end up in an endless loop.

If u dont want to place the actor in the world using the editor, but spawn it from code, move your spawn code into your gamemode class.


// Called when the game starts or when spawned

This comment might be a little misleading.
Called when the game starts, means that it’s called on existing actors in the world once the game start

See: http://api.unrealengine.com/INT/API/…lay/index.html
So when the game starts, BeginPlay will be called on all existing actors in the game world, after the game has started BeginPlay will be called to all actors being spawned.

Thank you very much for your help! After crawling 3 hours through your links I got an idea of what I have to do. However, a really strange error occurs.

  1. I implemented my spawning as followed:

Map1GameMode.cpp:



// Copyright 1998-2018 Epic Games, Inc. All Rights Reserved.

#include "Map1GameMode.h"
#include "Map1Pawn.h"
#include "Map1Hud.h"
#include "Runtime/Engine/Classes/Engine/World.h"
#include "StreetActor1.h"

AMap1GameMode::AMap1GameMode()
{
    DefaultPawnClass = AMap1Pawn::StaticClass();
    HUDClass = AMap1Hud::StaticClass();

}

void AMap1GameMode::StartPlay() {
    Super();

    FVector Location(0.0f, 0.0f, 0.0f);
    FRotator Rotation(0.0f, 0.0f, 0.0f);
    FActorSpawnParameters SpawnInfo;
    GetWorld()->SpawnActor<AStreetActor1>(Location, Rotation, SpawnInfo);
}

Map1GameMode.h:



// Copyright 1998-2018 Epic Games, Inc. All Rights Reserved.
#pragma once
#include "GameFramework/GameModeBase.h"
#include "Map1GameMode.generated.h"

UCLASS(minimalapi)
class AMap1GameMode : public AGameModeBase
{
    GENERATED_BODY()

public:
    AMap1GameMode();

    void StartPlay() override;

};

When I put the “super();” in the method call, UE4 editor crashes. When I delete “super();” my actor is shown in the world outliner but I can’t drive my car anymore and my HUD is not there anymore, do you have some suggestions?

Thank you for you effort!


Super::StartPlay();

Thank you very much that helped! “Super::StartPlay();”. Eventhough it doesn’t work properly, my actor spawns and the game still works! Thank you very much!

When the editor crashes, go to YourProject\Saved\Logs\YourProject.log and look at the crash stacktrace at the very end, preferably before restarting the editor (or you’ll have to find the proper log file in the -backups). Sharing the stacktrace with us would be helpful in understanding your error.

As for the super call, it’s fully expected that not calling it on most engine classes overrides would lead to unreliable behaviour. StartPlay is a prime example of that, as it is not an overridable method that’s just there as a hook for your game logic (like some virtual methods are). Sure, it may not crash when you do not call the super, but that’s only because you essentially prevented your game from fully starting :D.

I’m not 100% sure about what’s causing the error that you’re seeing here, but my best guess would be that you’re confusing StartPlay and BeginPlay. StartPlay is a method that is (as far as I know) unique to the GameMode and is called relatively early in your map lifecycle, while BeginPlay is the method that is called on every single actor when the game is ready to start, or when they finish spawning while the game is already running.

By trying to spawn things in StartPlay, you’re probably interacting with the level before it is fully ready, leading to a crash. Even if I’m wrong, it’s very likely that there is no reason to keep your Spawn in StartPlay, as it’s way too early to interact with the spawned actor anyway. So I’d move it to BeginPlay anyway :stuck_out_tongue:

Edit: Right, there was also the issue that the super call is not supposed to be “Super();”, but “Super::StartPlay();”

So Altrue, you would suggest to put the code in another method? BeginPlay() is a method of the Actor itself, not of the GameModeBase class. However I am not able to see my actor eventhough it is shown in the world outliner and have set a mesh and a material. So it might be the case that the StartPlay() method is not the correct one in this case.

As mentioned here: How to spawn actor in C++? Been trying for 9 hours now. - UE4 AnswerHub
I set “bReplicates = true” in the actor class


AStreetActor1::AStreetActor1()
{
     // 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;
    bReplicates = true;

}

My StartPlay() method now looks like this: (Map1GameMode.cpp)


void AMap1GameMode::StartPlay() {
    Super::StartPlay();

    FVector Location(0.0f, 2900.0f, 196.0f);
    FRotator Rotation(0.0f, 0.0f, 0.0f);
    FActorSpawnParameters SpawnInfo;
    SpawnInfo.Owner = this;
    SpawnInfo.Instigator = Instigator;

    AStreetActor1 *obj = GetWorld()->SpawnActor<AStreetActor1>(AStreetActor1::StaticClass(), Location, Rotation, SpawnInfo);
    UStaticMeshComponent *MyMeshComponent = NewObject<UStaticMeshComponent>(obj, UStaticMeshComponent::StaticClass(), TEXT("Mesh"));
    UStaticMesh* MeshAsset = Cast<UStaticMesh>(StaticLoadObject(UStaticMesh::StaticClass(), NULL, TEXT("StaticMesh'/Game/Geometry/Meshes/1M_Cube_Chamfer.uasset'")));
    UMaterial* MaterialAsset = Cast<UMaterial>(StaticLoadObject(UMaterial::StaticClass(), NULL, TEXT("Material'/Game/Geometry/Meshes/CubeMaterial.uasset'")));
    MyMeshComponent->SetStaticMesh(MeshAsset);
    MyMeshComponent->SetMaterial(0, MaterialAsset);
}

I just used the meshes from the default project. I still need to learn about meshes, but for now, I just want to spawn one actor. It can’t be so difficult?!

Have you seen the First person c++ template? The spawn of the projectiles is quite clear.

Also, hard coding references is a really bad idea.

Do it right: BP + C++.

Anyway here is the solution:



AStreetActor1::AStreetActor1()
{
     // 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;

    UStaticMeshComponent *MyMeshComponent = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("Mesh"));
    UStaticMesh* MeshAsset = Cast<UStaticMesh>(StaticLoadObject(UStaticMesh::StaticClass(), NULL, TEXT("StaticMesh'/Game/Geometry/Meshes/1M_Cube_Chamfer.uasset'")));
    MyMeshComponent->SetStaticMesh(MeshAsset);
}





void AMap1GameMode::StartPlay() {
    Super::StartPlay();

    FVector Location(0.0f, 2900.0f, 196.0f);
    FRotator Rotation(0.0f, 0.0f, 0.0f);
    FActorSpawnParameters SpawnInfo;
    SpawnInfo.Owner = this;
    SpawnInfo.Instigator = Instigator;

    GetWorld()->SpawnActor<AStreetActor1>(AStreetActor1::StaticClass(), Location, Rotation, SpawnInfo);
}


It still doen’t work. The actor “spawns” only in the world outliner. I can choose the meshes in the editor and the actor appears. However, the mashes won’t get set by c++. The suggestion of EvilCleric didn’t work either. Thank you guys for your answers and effort, I will work on a solution the next days. I plan to make a tutorial after solving this, so I will let you know. Last state of my files. (yes Map1GameMode::StartPlay() does the same as StreetActor1::StreetActor1() does, just to be safe)

StreetActor1.h:



#pragma once

#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "StreetActor1.generated.h"

UCLASS()
class MAP1_API AStreetActor1 : public AActor
{
    GENERATED_BODY()

public:    
    // Sets default values for this actor's properties
    AStreetActor1();

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

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

public:

    UPROPERTY(EditAnywhere)
    UStaticMeshComponent *MeshComponent;

    UPROPERTY(EditAnywhere)
    UStaticMesh *Mesh;

    UPROPERTY(EditAnywhere)
    UMaterial *Material;

    FVector Location;

    FRotator Rotation;

};

StreetActor1.cpp:



#include "StreetActor1.h"
#include "Runtime/Engine/Classes/Components/StaticMeshComponent.h"
#include "Runtime/Engine/Classes/Engine/StaticMesh.h"
#include "Runtime/Engine/Classes/Materials/Material.h"

// Sets default values
AStreetActor1::AStreetActor1()
{
    // 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;
    bReplicates = true;

    AStreetActor1::MeshComponent = NewObject<UStaticMeshComponent>(this, UStaticMeshComponent::StaticClass(), TEXT("StaticMesh"));
    AStreetActor1::Mesh = Cast<UStaticMesh>(StaticLoadObject(UStaticMesh::StaticClass(), NULL, TEXT("StaticMesh'/Game/Geometry/Meshes/1M_Cube_Chamfer.uasset'")));
    AStreetActor1::Material = Cast<UMaterial>(StaticLoadObject(UMaterial::StaticClass(), NULL, TEXT("Material'/Game/Geometry/Meshes/CubeMaterial.uasset'")));
    MeshComponent->SetStaticMesh(Mesh);
    MeshComponent->SetMaterial(0, Material);
    MeshComponent->SetWorldLocation(AStreetActor1::Location);
    MeshComponent->RegisterComponent();
    this->AddOwnedComponent(MeshComponent);
    this->SetRootComponent(MeshComponent);
}

// Called when the game starts or when spawned
void AStreetActor1::BeginPlay()
{
    Super::BeginPlay();

}

// Called every frame
void AStreetActor1::Tick(float DeltaTime)
{
    Super::Tick(DeltaTime);

}

Map1GameMode.h:



#pragma once
#include "GameFramework/GameModeBase.h"
#include "Map1GameMode.generated.h"

UCLASS(minimalapi)
class AMap1GameMode : public AGameModeBase
{
    GENERATED_BODY()

public:
    AMap1GameMode();

    void StartPlay() override;

};

Map1GameMode1.cpp:



#include "Map1GameMode.h"
#include "Map1Pawn.h"
#include "Map1Hud.h"
#include "Runtime/Engine/Classes/Engine/World.h"
#include "StreetActor1.h"
#include "Runtime/Engine/Classes/Components/StaticMeshComponent.h"
#include "Runtime/Engine/Classes/Engine/StaticMesh.h"
#include "Runtime/Engine/Classes/Materials/Material.h"

AMap1GameMode::AMap1GameMode()
{
    DefaultPawnClass = AMap1Pawn::StaticClass();
    HUDClass = AMap1Hud::StaticClass();

}

void AMap1GameMode::StartPlay() {
    Super::StartPlay();

    FVector Location(0.0f, 2988.0f, 196.0f);
    FRotator Rotation(0.0f, 0.0f, 0.0f);
    FActorSpawnParameters SpawnInfo;
    SpawnInfo.Owner = this;
    SpawnInfo.Instigator = Instigator;

    AStreetActor1 *obj = GetWorld()->SpawnActor<AStreetActor1>(AStreetActor1::StaticClass(), Location, Rotation, SpawnInfo);

    UStaticMeshComponent *MeshComponent = NewObject<UStaticMeshComponent>(obj, UStaticMeshComponent::StaticClass(), TEXT("StaticMesh"));
    UStaticMesh *Mesh = Cast<UStaticMesh>(StaticLoadObject(UStaticMesh::StaticClass(), NULL, TEXT("StaticMesh'/Game/Geometry/Meshes/1M_Cube_Chamfer.uasset'")));
    UMaterial *Material = Cast<UMaterial>(StaticLoadObject(UMaterial::StaticClass(), NULL, TEXT("Material'/Game/Geometry/Meshes/CubeMaterial.uasset'")));
    MeshComponent->SetStaticMesh(Mesh);
    MeshComponent->SetMaterial(0, Material);
    MeshComponent->SetWorldLocation(Location);
    MeshComponent->RegisterComponent();
    obj->AddOwnedComponent(MeshComponent);
    obj->SetRootComponent(MeshComponent);
}

Here is the entire code for both classes:

StreetActor1.h


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

#pragma once

#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "StreetActor1.generated.h"

UCLASS()
class MAP1_API AStreetActor1 : public AActor
{
    GENERATED_BODY()

public:    
    // Sets default values for this actor's properties
    StreetActor1();

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

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

};

StreetActor1.cpp


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

#include "StreetActor1.h"
#include "Components/StaticMeshComponent.h"


// Sets default values
AStreetActor1::StreetActor1()
{
     // 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;

    UStaticMeshComponent *MyMeshComponent = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("Mesh"));
    UStaticMesh* MeshAsset = Cast<UStaticMesh>(StaticLoadObject(UStaticMesh::StaticClass(), NULL, TEXT("StaticMesh'/Game/StarterContent/Props/SM_Chair.SM_Chair'")));
    MyMeshComponent->SetStaticMesh(MeshAsset);
}

// Called when the game starts or when spawned
void AStreetActor1::BeginPlay()
{
    Super::BeginPlay();  
}

// Called every frame
void AStreetActor1::Tick(float DeltaTime)
{
    Super::Tick(DeltaTime);
}



Map1GameMode.h


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

#pragma once

#include "CoreMinimal.h"
#include "GameFramework/GameModeBase.h"
#include "Map1GameMode.generated.h"

/**
 *
 */
UCLASS()
class MAP1_API AMap1GameMode : public AGameModeBase
{
    GENERATED_BODY()

protected:
    virtual void StartPlay() override;    

};



AMap1GameMode.cpp


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

#include "Map1GameMode.h"

#include "StreetActor1.h"
#include "Runtime/Engine/Classes/Engine/World.h"

void AMap1GameMode::StartPlay() {
    Super::StartPlay();

    FVector Location(0.0f, 2988.0f, 196.0f);
    FRotator Rotation(0.0f, 0.0f, 0.0f);
    FActorSpawnParameters SpawnInfo;
    SpawnInfo.Owner = this;
    SpawnInfo.Instigator = Instigator;

    GetWorld()->SpawnActor<AStreetActor1>(AStreetActor1::StaticClass(), Location, Rotation, SpawnInfo);
}



There is a C++ beginners tutorial in Epic’s youtube channel. She goes step by step on a simple game. Start there.

Yes, even if using StartPlay was intentional and not a mistake as I thought, I’d definitely recommend trying to move everything in BeginPlay instead.
The fact that it is an Actor method is not an issue, this BeginPlay behaviour may be common to all actors, but it doesn’t make it less relevant for the problem at hand: Because your GameMode class is necessarily present at the start of the game, you can be certain that anything spawning at BeginPlay inside your GameMode, will spawn at the very start of the game. It’s virtually the same end-result than StartPlay, except that you’re overriding the proper function for that intent.

In my opinion, overriding StartPlay should be reserved to highly technical cases where you want to do something at this very step of the game lifecycle, whereas BeginPlay is your well-known catch-all “do it ASAP” function. I mean, just look at any GameMode blueprint, the suggested events are “Tick” and “BeginPlay”, not “Tick” and “StartPlay” (by the way, tick is also widely-used even though it does not come from AGameMode but from AActor (or even UObject, whatever)). Even if this doesn’t solve your problem, at least you would be doing it properly :slight_smile:

EDIT: Could we please get a summary of what your current problem is? So far we’ve been working on the asumption that the spawn function was the issue, but your method of asserting that the spawn did work may be at fault too. If I understood properly, you could only assert that the spawn did work through the world outlier, because the projectile didn’t have any mesh. Is it still the case? Using the world outlier is tricky in multiplayer because you can have up to three parallel versions of that outlier running at the same time: Editor, Client, and Server (if dedicated). Transient actors may not show up in the editor world outlier, for instance.

Thank you Altrue for the long reply. I am going to put the code into the OnBegin() method. I already figuered out the problem I had. The name of the mesh wasn’t correct. It has to be like ‘…/1M_Cube.1M_Cube’.
The problem I have with VisualStudio and UE-Editor is that I just get too few responses what goes wrong when testing/compiling code. Crashes of the UE-Editor are the worst in my opinion. However, the log does help at some point. I will try to work with CLion in the future but currently 4.19 seems not to be compatible in a way.
Does the naming of the Meshes/Material fit always?
Thanks a lot for the help from all of you.

As far as I know, it should always be 1M_Cube.1M_Cube. But that may not be the most ideal way to reference assets.

This is highly subjective territory, but I’m not a fan of direct path references in the code. If you try to reorganize your content files in any way, everything always breaks…

I’d recommend either using UPROPERTIES on asset pointers with the proper UPROPERTIES specifiers, then using the BP version of the class, in which you can set your asset references directly through the editor (this way, if you move or rename them, the references are automatically modified as well). That would be the easy way, even though you will need to learn how to get the BP class itself in C++ to spawn it. (Unless we’re talking about a default class referenced in the Game Mode (i.e PlayerController, GameState, PlayerState, etc…), in which case the engine can spawn that for you.)

The second option would be to use the Asset Manager, there is lots of recent documentation available on the UE4 docs since it was added in 4.17 or 4.18.

To be honest, I haven’t found a fully satisfying solution yet myself, and my attempts to start threads on the subject haven’t been very successful. Nonetheless, directly referencing assets seems like an accident waiting to happen.

Try putting Super::BeginPlay(); after you spawn the actor.



void AStreetActor1::BeginPlay()
{
   FActorSpawnParameters SpawnInfo;
   GetWorld()->SpawnActor<AStreetActor1>(this->GetActorLocation(), FRotator(0.0f), SpawnInfo);
  Super::BeginPlay();
}