Learning Unreal Engine 4: Conway’s Game of Life

So recently I’ve been learning Unreal Engine, both with blueprints and with C++. I decided to implement Conway’s Game of Life in Unreal Engine as a project, because I’ve wanted to do my own implementation for a while, and it seemed like a good way to get started with UE4.

While you’re reading this, you can download the executable and try it out for yourself. Note that the more cells you have in the grid, the more memory and render time it takes, so you might not want to spawn thousands of cells right away.

I’ve heard a lot about UE4 and people seem to like it a lot. It is also a strong competitor against Unity, and with good reason. Unreal Engine has been popular among AAA studios for a long time now, but a few years ago, with the release of UE4, it is gaining popularity among indie developers. Through the Blueprints visual scripting system, it is much more beginner-friendly for programmers as well as level designers than it was before.

I will be comparing UE4 a lot with Unity in this post, since I have more experience in Unity and that previous experience has helped me to migrate knowledge into UE4. The cube mesh used in the simulation is from the standard puzzle example.

Learning the engine structure

The first thing I started to look into was the engine structure differences between UE4 and Unity.

Unity’s base structure is comprised of modules, assets, game objects and components. Modules are how the engine divides it’s functionality into different categories (input, graphics, audio..), assets are files such as data files and textures, game objects are objects in the game world and components are basically what make game objects do something. Each game object must have a Transform component, which stores the object’s position relative to the object’s parent object, or global position if it does not have a parent. For someone who hasn’t used Unity, this structure may seem a bit complex, but it’s much more simple than that of UE4.

UE4 consists of modules, actors, components, controllers, pawns and game modes. Modules have the same idea as in Unity, they are used to categorize different parts of the engine. Your own C++ code will also reside in a module. Actors are all objects that reside in the game world. Unlike in Unity, actors are not only holders for components, they can be inherited, and they can have their own properties. An actor can have multiple components of the same type, and scene components can be stacked in a hierarchy, so they also function differently to that of Unity. Actors also need at least one scene component, but this component is created if it is not present. Controllers are an interface between pawns, players and AI. Pawns are controllable objects in the game world, either controlled by a player or by an AI. Game mode decides what object types to use as pawns, player controllers, spectator pawns, and so on.

So UE4 has a lot more parts in it that you need to get familiar with. But complexity in this case is rarely a bad thing. In Unity, you have to do a lot of things from scratch, or download/buy them from the Asset Store. The complexity of UE4 is, in most cases, useful, and makes your game more structured and easier to maintain.

Of course, the structure of both engines are not really as simple as stated above, that was just an oversimplified comparison. I suggest looking into the documentation of both engines if you want to find out more. I will probably talk more about those on game engine development posts.

Blueprints

blueprints

I was very skeptical about blueprints when I started learning UE4. I’ve always thought of visual scripting as a tool only useful for the basics of learning how to program, and as a useless gimmick in practical use. However, Unreal’s blueprints are a much more sophisticated visual scripting system than anything else I’ve seen before. So I decided to go with it for a while. I thought it was a good way to get to know how the native object types work and how actors interact with each other in the game world.

All was good for a while, although getting used to dragging nodes around was still a bit tedious. That was until I started making the Game of Life simulation logic. Here is a picture of it, in all it’s glory. (click to enlarge)

dontdothis

This is just the basic logic of counting the amount of living neighbors a cell has. Not only is it difficult to read without zooming and dragging all around the place, it was also slow in performance. The game froze even with a 20×20 grid.

So at this point I came to the conclusion that while blueprints are sophisticated and can do a lot of things, they still aren’t a good option for programming more complex functionality. They are slow and messy. They are more useful in adding some small level-specific features to an object, and also UMG, which I will talk about later.

Then I started migrating the project into C++.

C++

I have to say that UE developers made the right choice when sticking with C++ as an option, and adding features such as hot reloading to make rapid prototyping with C++ much more enjoyable. While I miss the simple clutter-less C# code, I don’t mind working with C++ at all, especially since Unreal has a lot of functionality ready to be used.

The first thing I wanted to figure out was how to use Qt Creator with UE. The reason for that is because I absolutely love Qt Creator, and think it’s a far better IDE for C++ than Visual Studio is. It’s faster, more lightweight, cross-platform, somewhat portable and I’ve used it a lot when developing Qt applications. Thankfully, I found a solution, and I didn’t need to use Visual Studio. Yay!

Reflection

cpp_pawn

The Unreal Property System is UE’s reflection system. A reflection system basically gives you the ability to inspect C++ objects, methods and fields during runtime. Like the program is looking at a reflection of itself. This system allows UE to instantiate objects into the game world when it reads the level assets or blueprints. It also allows Unreal Editor to see what kind of classes exist within the C++ codebase, so it can associate them with blueprints and levels.

So the property system is an integral part of UE development, but when creating new object types, you don’t need to do much to make it work. You simply include an automatically generated header file, add a couple of macros (UCLASS, UPROPERTY, UFUNCTION, etc) and let the Unreal Header Tool do the heavy lifting for you. C# has a built-in reflection system, but then again, C++ is a lower-level language.

Input system

The input system in UE is very different from Unity. In Unity, you access the Input static class to get the status of different inputs each frame, where as in UE you bind methods into an InputComponent that will detect changes to that specific input. Unity only has input axes which are used for both buttons and analog input, while UE splits these two into actions and axes, which makes more sense given the input binding mechanic. The pawn class even provides a useful overrideable method for binding input into the player’s pawn.

void AGOLPawn::SetupPlayerInputComponent(UInputComponent* InputComponent)
{
    Super::SetupPlayerInputComponent(InputComponent);

    InputComponent->BindAction("ChangeCameraMode", IE_Pressed, this, &AGOLPawn::ChangeCameraMode);

    InputComponent->BindAction("MoveCamera", IE_Pressed, this, &AGOLPawn::MoveCameraBegin);
    InputComponent->BindAction("MoveCamera", IE_Released, this, &AGOLPawn::MoveCameraEnd);

    InputComponent->BindAction("MoveCameraFurther", IE_Pressed, this, &AGOLPawn::MoveCameraFurther);
    InputComponent->BindAction("MoveCameraCloser", IE_Pressed, this, &AGOLPawn::MoveCameraCloser);

    InputComponent->BindAction("MoveFaster", IE_Pressed, this, &AGOLPawn::MoveFaster);
    InputComponent->BindAction("MoveFaster", IE_Released, this, &AGOLPawn::MoveSlower);

    InputComponent->BindAction("ToggleSimulation", IE_Pressed, this, &AGOLPawn::ToggleSimulation);

    InputComponent->BindAction("ActivateCell", IE_Pressed, this, &AGOLPawn::ActivateCell);
    InputComponent->BindAction("ActivateCell", IE_Released, this, &AGOLPawn::ClearActivationStatus);
    InputComponent->BindAction("DeactivateCell", IE_Pressed, this, &AGOLPawn::DeactivateCell);
    InputComponent->BindAction("DeactivateCell", IE_Released, this, &AGOLPawn::ClearActivationStatus);

    InputComponent->BindAxis("MoveCameraVertical", this, &AGOLPawn::MoveVertical);
    InputComponent->BindAxis("MoveCameraHorizontal", this, &AGOLPawn::MoveHorizontal);

    InputComponent->BindAxis("RotateCameraVertical", this, &AGOLPawn::RotateVertical);
    InputComponent->BindAxis("RotateCameraHorizontal", this, &AGOLPawn::RotateHorizontal);
}

I personally like UE’s input system over Unity. It’s much better structured and you don’t have to put all your input logic on a tick function. You also don’t have to think about where to put your input logic; pawn is your character, so you process input for that character. I reckon that makes multiplayer game code much easier to manage.

Game of Life functionality

Creating a custom pawn for camera controls was a trivial task; bind input, add speed properties, add lock/unlock logic and that was pretty much it. Simple and functional.

The Game of Life functionality could’ve been more simpler but I wanted to focus more on performance. Whenever I needed to query the value of a cell by it’s X and Y position, I could’ve made a method such as this:

bool ALifeSimulator::GetCellValue(int32 gridX, int32 gridY)
{
    for(int32 i = 0; i < mCells.Num(); i++) {
        ALifeCell *cell = mCells[i];
        if(cell->GridX == gridX && cell->GridY == gridY)
            return cell->Value;
    }
    return false;
}

However, this is slower than getting the cell value from a two-dimensional boolean array, like this:

bool ALifeSimulator::GetCellValue(int32 gridX, int32 gridY)
{
    if (!mGrid) return false;
    if (gridX > mSize || gridY > mSize ||
        gridX < 1 || gridY < 1)
        return false;

    return mGrid[gridX - 1][gridY - 1];
}

Not only that, but having cell values in an array makes it faster to copy it during a simulation tick to apply the next state as the current state

for (int x = 1; x <= mSize; x++)
    for (int y = 1; y <= mSize; y++) {
        mGrid[x - 1][y - 1] = mGridNextState[x - 1][y - 1];
    }

Only after the next grid state is ready, we apply the values to the real cell actors

for(int i = 0; i < mCells.Num(); i++) {
    ALifeCell *cell = mCells[i];
    bool value = mGrid[cell->GridX - 1][cell->GridY - 1];
    cell->SetHighlighted(value);
}

So now the only performance limitation we have is how much pretty lights and cube meshes UE4 and the hardware can render, as the two-dimensional array can process thousands of cells with a single tick.

Unreal Motion Graphics

Now that the base game was complete, I wanted to create a nice little user interface for the simulation. Unreal Motion Graphics (UMG) is the de facto standard for creating UI in UE4. UMG is done a bit better than the UI system in Unity. Because of Unity’s simpler engine model, the UI also exists within the game world, which can become a bit tedious when working with it. UMG is not part of the game world; it is added to the local player’s viewport. It can interact with the game world, but it’s not part of it. And this is fine, because it makes little sense to have the UI system within the game world, right?

I used blueprint scripting in UMG, since the logic wasn’t very complex, and it was mostly used for calling C++ functions on actors anyway. Here’s an example:

umg_blueprint

You can code UI elements with C++ as well, so same as actors, don’t stay on blueprints if your logic gets too complex.

Creating standalone releases

After finishing the UI, it was time to pack up the game into a standalone executable. This worked without problems for exporting to Windows 32-bit and 64-bit, but you need to setup a cross-compiler for packaging to any other platform. This is because you need to compile your C++ code to that specific target platform’s native binary format. This probably wouldn’t take more than an hour to set up, but I got lazy at this point, so I only compiled my project for Windows.

Conclusion

After a couple days of intensive jamming, the game simulator was complete! It’s a pretty basic example of an Unreal Engine project, but it was a useful learning experience.

If you missed it, you can download the executable on the top of the post, or just get it here. 🙂

Also, if you want to read more about Conway’s Game of Life, it has an entire wiki dedicated to it.

Leave a Reply

Your email address will not be published.

Time limit is exhausted. Please reload CAPTCHA.