Chasers of TimeA Mid-Core mobile endless runner
(This game is currently in development, so everything is subject to change)
The technical implementation
(This section will describe some technical challenges and decisions I made)
Level and world generation
In order to generate the level, the tiles and the obstacles for the player I wanted to implement a level generation manager class.
Since I had a strong C# background I was used to implement manager classes via the singleton pattern for example, so that the class could be easily accessible from from other classes and to make sure that there would always and only be 1.
C++ supports singleton classes, but I ran into problems with this in Unreal engine 4. In a build, everything would work fine. But while testing the game in the engine, you get problems because unreal does not clean up static variables between play sessions in the editor.
Because of this the variables you use for accessing the single-instance objects become invalid during the second run and you’ll have to restart the editor to make it work again.
The way to clearly integrate single-instance objects into Unreal is to put them either into the GameMode or the GameInstance class.
But I did not wat to do this because from an OOP point of view this did not seem right to me.
So I went on to search for a solution and found out that I could make it work when I instantiate my manager class via the GameInstance class (which is globally accessible) and use a getter which is a bit more complex but makes sure that it always returns a valid instance of my manager class. So when I want to access the single-instance class while playing in the editor (PIE), I would get it via the GameInstance, where when playing the game in a stand alone build, I could use the normal singleton getter (this can be checked by conditional compilation in order to safe performance with redundant validity checks). Because in a stand alone build, when the application closes, everything will be destroyed. So this won’t give problems for a second play though of the game.
At the moment I am doing the platform check by getting the platform name for the UGameplayStatics class.
Later on I will change this by using conditional compilation like #if_UE_EDITOR
Workflow for adding new worlds to the game
Because there are a lot of different world for the player to run in, I had to come up with a workflow which would make it easy for me to integrate new worlds into the game.
I started off with designing the system on paper to see if it would work. With some iteration I came up with the following system:
There is a struct called FWorldData, this struct contains all the data that a world has:
– int ID
– ELevelNames Name (An enumerator containing all the names of the worlds in the game)
– int UnlockCost
– UTexture2D WorldIcon
– TArray Tiles (all the tiles that belong to this world and which can be used by the level generator)
And many more.
Unreal has class in it’s framework of which always, only 1 exists and which is easily accessible by other classes. This is the GameInstance class. This class has an FWorldData array, and so, all the worlds. If I want to add a new world to the game, all that is needed to be done is add it into this array via the editor and create a special world button in the UI with the given world name and ID.
Then the world has to be physically added via sub levels. Each world is a sub level which can be loaded into the game. This is done to keep everything separated and as clean as possible.
Then the only thing left to do is designing tiles for each world and adding the correct mesh to the base obstacle classes.
From there, the level generation manager and the obstacles make sure that the correct tiles are spawned and the correct obstacle meshes are being renderend.