Part 1

Diving into the code

Now we open Visual Studio by going to “File” -> “Open Visual Studio” in the editor.

We have two folders “Engine” and “Games” in the solution. You can check out the engine code in the “Engine” folder, but we are more interested in the “Games” folder because that is where our code lives. The Unreal Engine uses the project name for most files in our Visual Studio project. We already have a couple files. First right click the project and select “Set as StartUp Project”.

A GameMode and a main class for our game. You don’t have to touch that one. Our GameMode is empty for now. The C# files are the build configuration files for our project. The Unreal Engine uses its own build tool which reads these files.

Start the Game in the Editor

But let’s see what we have for now before we start writing any additional code. We have 4 important buttons in the top bar for that.

Build builds the map data. That means the light maps, and navigational data is computed. You generally only have to do this if you have changed the lighting or any static meshes in the scene. Compile compiles the C++ code, but you can also use the compile function directly from within Visual Studio. Play starts the game in the editor viewport and Launch opens its own window. There are more options there but for now just hit “Play”. We have a free floating first person camera and can move around the scene. But wait we did not see any PlayerController class in our solution and the GameMode is empty. To solve that mystery we can go to the “World Settings”. If there is no “World Settings” tab next to to the “Details” go to “Window” -> “World Settings” to open them. We see that we have not set GameMode Override so right now we are using Unreal’s default GameMode, PlayerController and Pawn.

Using our GameMode

First we need to go to our GameMode and start implementing it. The GameMode sets which PlayerController and Pawns should be used.

GameModeBase.h

/**
 * The GameMode for the test project
 */
UCLASS()
class TESTPROJECT_API ATestProjectGameModeBase : public AGameModeBase
{
    GENERATED_BODY()

public:

    ATestProjectGameModeBase();
};

GameModeBase.cpp

ATestProjectGameModeBase::ATestProjectGameModeBase()
{
    // use our custom PlayerController class
    PlayerControllerClass = APlayerController::StaticClass();
}

Unreal uses the constructor to get the default values for a class. In the case of the GameMode that includes the PlayerController class. We just use the default PlayerController for now. But first we must tell Unreal to use our GameMode. For that we go back to the “WorldSettings” and select our GameMode. Here you can also see why it is a good idea to use a project specific handle as a Prefix for every class we create. The Unreal selection menus show the name and basic type but without a specific handle it can be pretty difficult to distinguish between Unreal’s classes and our own.

If we hit “Play” now nothing has changed, because we have set the default PlayerController in our GameMode. So let’s create our own.

Creating a PlayerController

We can create a class with the editor by right clicking in the “Content Browser” and selecting “New C++ Class …”.

First we want to toggle “Show All Classes” in the top right corner so we can see the actual class names. Unreal uses inheritance to extend the base classes. Select “PlayerController” and click “Next”.

I will be using Test as the project handle since TestProject is a bit too long for my taste. You should decide what handle you want when creating the project. Unreal will compile the code and we get the empty TestPlayerController.h and .cpp. First we set our new PlayerController class to be used.

GameModeBase.cpp

#include "TestProject.h"
#include "TestProjectGameModeBase.h"

// we include our PlayerController
#include "TestPlayerController.h"

ATestProjectGameModeBase::ATestProjectGameModeBase()
{
    // use our custom PlayerController class
    PlayerControllerClass = ATestPlayerController::StaticClass();
}

Using the PlayerController

For now we will let every static mesh in the scene spin to make sure Unreal is actually using our PlayerController. So lets start implementing our PlayerController

PlayerController.h

/**
 * The default player Controller in the test project
 */
UCLASS()
class TESTPROJECT_API ATestPlayerController : public APlayerController
{
    GENERATED_BODY()

public:

    ATestPlayerController();

    // we override the BeginPlay function which is called
    // once the PlayerController has been spawned in the scene and is ready
    virtual void BeginPlay() override;

    // we override the Tick function which is called every frame
    virtual void Tick(float deltaTime) override;

private:

    // we use an array( the Unreal version of a dynamic array, like std::vector)
    // to hold the pointers to the static meshes in the world
    UPROPERTY()
    TArray<class AStaticMeshActor*> m_staticActors;

    float m_rotationSpeed;
};

The UPROPERTY() is used to make Unreal aware of the member. That means that the garbage collector will not free the memory as long as our PlayerController exists. UPROPERTY is used for much more especially for Blueprint and C++ communication. It also important to note that the TArray is the only container that the garbage collector will take into consideration when freeing memory.

PlayerController.cpp

ATestPlayerController::ATestPlayerController()
    : m_rotationSpeed(100.f)
{
    // we want the Tick function to be called for our PlayerController
    PrimaryActorTick.bCanEverTick = true;

    // we reserve some memory in our array
    m_staticActors.Reserve(10);
}

void ATestPlayerController::BeginPlay()
{
    // we also call the BeginPlay for the parent class
    Super::BeginPlay();

    // we get every static mesh actor in the world
    int32 numActors = 0;
    UWorld* world = GetWorld();
    for (TActorIterator<AStaticMeshActor> itr(world); itr; ++itr)
    {
        m_staticActors.Add(*itr);
        ++numActors;
    }

    // generally display is the more fitting log level here
    // but we use warning for now because it is highlighted in the console output
    UE_LOG(LogTemp, Warning, TEXT("We found %d Static Mesh Actors"), numActors)
}

void ATestPlayerController::Tick(float deltaTime)
{
    // we also call the tick for the parent class
    Super::Tick(deltaTime);

    // we prepare our delta rotator
    const FRotator rotation(0.0f, m_rotationSpeed * deltaTime, 0.0f);

    // now we iterate through the actors and add a rotation
    const int32 numActors = m_staticActors.Num();
    for (int32 i = 0; i < numActors; ++i)
    {
        // we add a rotation in world space
        m_staticActors[i]->AddActorWorldRotation(rotation);
    }
}

If we press “Play” now nothing will happen and we will get a ton of warnings. That is because every Static Mesh Actor in the map has a mobility option. Static, Stationary and Movable.

Every Static Mesh Actor in the map is set to Static since they did not move before so we have to switch them to Movable. We can multi-select all of them and make the switch.

And now everything rotates, including the floor. We don’t want that so we have to filter out the floor. There are many ways to do that but we will use the Unreal Tag system. Every Actor can be given any number of tags and we can check whether an actor has a specific tag. We add the “Rotate” tag to everything we want to rotate. Now we can also set the mobility on the floor back to “Static”. It is important to set the Actor Tag and not the Component Tag. The “Tags” section is for the Component Tags. We have to scroll down a but further for the Actor Tags.

Now we have to filter out any actors that do not have our tag. We can either do that with the AActor::ActorHasTag() or use the UGameplayStatics::GetAllActorsWithTag() to get all actors with the tag. The TActorIterator only iterates over actors with a specific class whereas GetAllActorsWithTag iterates over all actors. Use them depending on your situation. If we hit “Play” now the floor no longer rotates. With that we have our finished code.

PlayerController.h

/**
 * The default player Controller in the test project
 */
UCLASS()
class TESTPROJECT_API ATestPlayerController : public APlayerController
{
    GENERATED_BODY()

public:

    ATestPlayerController();

    // we override the BeginPlay function which is called
    // once the PlayerController has been spawned in the scene and is ready
    virtual void BeginPlay() override;

    // we override the Tick function which is called every frame
    virtual void Tick(float deltaTime) override;

private:

    // we use an array( the Unreal version of a dynamic array, like std::vector)
    // to hold the pointers to the static meshes in the world
    UPROPERTY()
    TArray<class AActor*> m_actors;

    FName m_rotationTag;
    float m_rotationSpeed;
};

We changed the Array from AStaticMeshActor to AActor

PlayerController.cpp

ATestPlayerController::ATestPlayerController()
    : m_rotationSpeed(100.f)
    , m_rotationTag(TEXT("Rotate"))
{
    // we want the Tuck function to be called for our PlayerController
    PrimaryActorTick.bCanEverTick = true;

    // we reserve some memory in our array
    m_actors.Reserve(10);
}

void ATestPlayerController::BeginPlay()
{
    // we also call the BeginPlay for the parent class
    Super::BeginPlay();

    // we get every actor with our rotate tag
    UGameplayStatics::GetAllActorsWithTag(GetWorld(), m_rotationTag, m_actors);

    // generally display is the more fitting log level here
    // but we use warning for now because it is highlighted in the console output
    UE_LOG(LogTemp, Warning, TEXT("We found %d Actors with the %s tag"), m_actors.Num(), *(m_rotationTag.ToString()))
}

void ATestPlayerController::Tick(float deltaTime)
{
    // we also call the tick for the parent class
    Super::Tick(deltaTime);

    // we prepare our delta rotator
    const FRotator rotation(0.0f, m_rotationSpeed * deltaTime, 0.0f);

    // now we iterate through the actors and add a rotation
    const int32 numActors = m_actors.Num();
    for (int32 i = 0; i < numActors; ++i)
    {
        // we add a rotation in world space
        m_actors[i]->AddActorWorldRotation(rotation);
    }
}

The logging code for the tag is a bit confusing but it works like that. First we get the FName as a FString and then we use the * operator to get a TCHAR* from the FString, which we can then log. The big difference between FName and FString is that FName represents an immutable string. Unreal keeps a table for the used FNames to avoid unnecessary allocations when the same FName is used in multiple locations. FString is a dynamic string and behaves more like the std::string. The Hungarian Notation Unreal uses will be explained at a later point.

Creating our own Pawn(next time)

We now have created our own PlayerController and are using it to affect objects in the map. It is important to note that nothing we have done in the PlayerController is specific to that class. The code for the rotation could be in any other Class and it would still work. I put it there to show that we were actually using our own PlayerController. The next step is to create and use our own pawn. That will also include getting the Input events in C++.

Part 3

comments powered by Disqus