Create A Side Scroller C++ Game In Unreal Engine Part 2: Creating Obstacles And Level Parts For Our Game

Table of Contents

Create A Side Scroller C++ Game In Unreal Engine Part 2: Creating Obstacles And Level Parts For Our Game

Reading Time: 14 minutes
Level: Beginner
Version: Unreal Engine 4.27

Help Others Learn Game Development

In part 1 of this tutorial series we created the RunnerCharacter C++ class and we created the movement behaviour for our player actor.

We also created the BP_RunnerCharacter blueprint out of the RunnerCharacter class and we created a game mode blueprint which allowed us to set the Default Pawn Class to BP_RunnerCharacter.

In this part, we are going to create the Spike and BaseLevel classes and use them to create obstacles and level parts for our game.

Creating The Spike Class

In the C++ Classes-> SideRunner folder, Right Click -> New C++ Class. From the pop up window select the Actor as the parent class and click on Next:

Img 1

Name the new class Spike and click on Create Class button:

Img 2
We are going to use this class to create spike obstacles for our game, we can also do this with blueprints, but since the major focus is on C++ in this tutorial I am going to stick with classes.
 
Note that every time you create a new C++ class you will be prompted to reload the project in Visual Studio, just press the Reload All button in the File Modification window:
 
Img 3
When you do that, the new classes you created will appear in the Source folder in the project structure in Visual Studio:
 
Img 4


Creating Spike Obstacle Blueprint

Now that we have the class for the spike obstacle, we can create blueprints out of it. In the Content -> Blueprints folder, Right Click -> New Folder and name the new folder Obstacles.

Inside the Obstacles folder create a blueprint and name it BP_Spike, make sure that you inherit the Spike class that we just created.

Open the BP_Spike blueprint in the editor and in the Components tab click on the Add Component button and search for static mesh:

Img 5 fixed

Change the name of the Static Mesh component to Spike, and in the Details tab for the Static Mesh settings, click on the drop down list and filter for Shape_QuadPyramid model and select it:

Img 6

In the Material settings, which is under the Static Mesh Settings, click on the drop down list and filter for M_Brick_Clay_New material and apply it to the pyramid model:

Img 7

In the Transform settings set the Z value of the Location property to -50:

Img 8

Don’t forget to press Compile and Save. This is how the spike looks like when we are finished:

Img 9


Creating Custom Collision Presets

The collision presets define how the actor will collide with other actors, or there will be collision at all.
 
If you don’t know what are collision presets, you can learn about them by clicking here.
 
Now, there are default collision presets that we can choose from, but we can also use custom collision presets and combine them with custom object channels that we create.
 
For that we need to open the Project Settings under Edit -> Project settings, and click on the Collision tab located under the Engine category:
 
Img 11

Under Object Channels, click on New Object Channel button:

Img 12

In the New Channel window, set the name for the channel to Spike and Default Response to Overlap:

Img 14
So what does Overlap Default Response do?
 
The Overlap Default Response will act like a non solid body and it will allow the player actor to pass through it, but we will not notice that since we will kill the player as soon as he touches the spike obstacle.
 

Actors And Custom Object Channels

Open the BP_Spike blueprint in the editor, select the Spike Static Mesh component in the Components window and under Details tab locate the Collision settings:

Img 15

Click on the drop down list for the Collision Presets and select Custom from the list.

For the Collision Enabled list, select Query Only (No Physics Collision) and for the Object Type list, select Spike:

Img 16

The Collision Enabled option allows us to specify if the actor will be able to collide and register the collision with other actors.

For the Spike obstacle we specified that we only want to register the collision but we don’t want the physics collision to happen e.g. the Spike will not be counted as a solid body.

For the Collision Responses we are going to make the Spike overlap everything except for the Camera where we are going to set Ignore:

Img 17

This will make the Spike blueprint overlap with blueprints that are using the specified Collision Presets, except for the Camera as it will ignore the collision with the camera actor.

Don’t forget to press Compile and Save so that the changes we made to BP_Spike will take effect on the blueprint.

Base Level Class

The next step is to create the level parts that will be spawned using a method called procedural level generation.

We will start with creating a new class. In the C++ Classes -> SideRunner folder, Right Click -> New C++ Class.

Select Actor as the parent class and click on Next:

Img 19

In the next window, name the class BaseLevel and click on Create Class button:

Img 20
Open the BaseLevel.h file in Visual Studio and at the top, above the class declaration and below the last #include statement add the following line:
 
				
					class UBoxComponent;
				
			
We talked about forward declaration in the previous part of this tutorial series. When it comes to forward declaration, we don’t have to do it every time we declare a variable of a specific type, instead we can forward declare the class type at the beginning of the file e.g. below the last #include line and it will be enough for every declaration of that variable type in the whole class.
 
The variable type we declared is a UBoxComponent which represents a box that we will use as a means to detect collision between the BaseLevel actor and other actors.
 
Moving forward, at the bottom of the class file below the Tick function declaration, add the following lines:
 
				
					protected:

	UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "My Triggers")
		UBoxComponent* Trigger;

	UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "My Triggers")
		UBoxComponent* SpawnLocation;
				
			

We created two UBoxComponents variables with Trigger and SpawnLocation as their names.

We will use the Trigger UBoxComponent to detect when the player actor collides with the BaseLevel actor and then we will spawn a new level part, and we will use the SpawnLocation UBoxComponent and access its world location, and use that value as the spawn location for the new level part.

This will be more clear when we create the level and you see where we will position the Trigger, and where we will position the SpawnLocation UBoxComponent.

In the previous part of this tutorial series, we introduced the UPROPERTY C++ macro and we said that it helps expose variables in the blueprint instance.

The BlueprintReadWrite parameter, will allow us to read e.g. get the value of this variable in the blueprint, and it will allow us to write e.g. change the value of this variable in the blueprint.

Basically it will allow us to access and manipulate this variable in the blueprint of this class and in other blueprints.

The EditAnywhere parameter indicates that this property can be edited by property windows, basically it will allow us to edit the value of this variable directly in the Details tab in the blueprint editor.

And the Category parameter specifies the category of the property when displayed in blueprint editing tools. This means when we Right Click to open the All Actions For This Blueprint window we can find the variable under the custom category we created which in this case is My Triggers.
 
Moving forward, we are going to create two functions that will return the values of the two UBoxComponents we created:
 
				
					public:

	UBoxComponent* GetTrigger();
	UBoxComponent* GetSpawnLocation();
				
			

Before we proceed to the BaseLevel.cpp file, I am going to leave the full BaseLevel.h file class below as a reference in case you want to copy paste it in your project:

				
					#pragma once

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

class UBoxComponent;

UCLASS()
class SIDERUNNER_API ABaseLevel : public AActor
{
	GENERATED_BODY()

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

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

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

protected:

	UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "My Triggers")
		UBoxComponent* Trigger;

	UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "My Triggers")
		UBoxComponent* SpawnLocation;

public:

	UBoxComponent* GetTrigger();
	UBoxComponent* GetSpawnLocation();

};
				
			
In the BaseLevel.cpp file, at the top, above the class declaration and below the #include line, add the following line:
 
				
					#include "Components/BoxComponent.h"
				
			

As we already mentioned, we use forward declaration in the .h file and then we use the #include to include the class we want to use.

At the bottom of the file, add the following lines of code:

				
					UBoxComponent* ABaseLevel::GetTrigger()
{
	return Trigger;
}

UBoxComponent* ABaseLevel::GetSpawnLocation()
{
	return SpawnLocation;
}
				
			

These lines are going to implement the two functions we declared in the .h file, you can also implement the functions by Right Click -> Quick Actions And Refactorings -> Create Declaration / Definition on the function name.

The GetTrigger function will return the Trigger variable and the GetSpawnLocation will return the SpawnLocation variable that we declared.

Make sure that you compile the code by holding CTRL + SHIFT + B or going to Build -> Build Solution, or you can do it from Unreal Engine editor as well.

Before we move forward, here is the full BaseLevel.cpp class for your reference:
 
				
					#include "BaseLevel.h"

#include "Components/BoxComponent.h"

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

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

}

UBoxComponent* ABaseLevel::GetTrigger()
{
	return Trigger;
}

UBoxComponent* ABaseLevel::GetSpawnLocation()
{
	return SpawnLocation;
}
				
			


Creating The Base Level Blueprint

In the Content -> Blueprints folder, Right Click -> New Folder and name it Levels. In the Levels folder Right Click -> Blueprint Class.

From the search bar for all classes filter for base level and select it as the parent class:

Img 21

Name the blueprint BP_Level1. As you can already assume because of the number, we are going to have multiple level parts, from 1 to 10 to be more precise, and the one we just created is the first level part.

First, we are going to add a Static Mesh component. In the Components tab click on the Add Component button and filter for static mesh:

Img 22
Rename the Static Mesh to Floor, create a duplicate of it by holding CTRL + C and then CTRL + V, or you can also use CTRL + W, and name the new Static Mesh to Cube.
 
For MacOS, you would use CMD + the same keys I mentioned above.
 
Click on the Add Component button and in the search filter for Box Collision and add it as a component. Rename the Box Collision to Trigger Box and create a copy out of it and name the copy Spawn Location Box:
 
Img 23
Moving forward, select the Floor Static Mesh component and in the Details tab under Static Mesh filter for Cube model and select it, and for the Material filter for BasicShapeMaterial and select it:
 
Img 24

Next, we are going to resize the Floor mesh by changing its scale. Make sure that you unlock the Lock icon on the right side of the scale values before you change them, because the values are going to be different for all 3 axis:

Img 25

If you are wondering why we need to make sure that the Lock icon is unlocked when we are changing the scale values of an actor, then click here.

Now select the Cube Static Mesh, and in the Details tab under Static Mesh and Materials select the same Cube model and the same BasicShapeMaterial as we did for the Floor mesh.

We are not going to change the scale values for the Cube mesh but we are going to change the Z Location to 90:

Img 26

Moving on the Box Collision components, select the Trigger Box component and set the following values for its Location and Scale in the Transform property:

Img 27
And last but not least, select the Spawn Location Box component, and set the following values for the Location in the Transform property:
 
Img 28
When we are done, this is how our BP_Level1 looks like:
 
Img 29

As I already mentioned, we are going to use the Trigger Box component to detect when the player actor collides with it, when that happens, we will spawn a new level actor using the Spawn Location Box as the position where the new level actor will be spawned.

That is why we had to resize the Trigger Box to make sure that the player actor will collide with it, and that is why had to move the Spawn Location Box so that we spawn the next level actor away from the current level actor.
 

Setting The Class Variable Values In The Blueprint Editor

Before we proceed to create more level parts, we need to set the values for the Trigger and SpawnLocation variables that we declared in BaseLevel.h.

To do this, go in the Event Graph tab in the BP_Level1 blueprint, and Right Click in the graph and search for spawn location:

Img 30

You will notice that the category under which you can find Set Spawn Location is called My Triggers, does this sound familiar?

We declared that in the UPROPERTY when we declared the SpawnLocation variable:

				
					UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "My Triggers")
		UBoxComponent* SpawnLocation;
				
			

The last parameter in UPROPERTY is the Category which takes a string value and we set that value to My Triggers, and we just saw where does this reflect.

You can always use the Category property to define a category for a specific variable(s) and then you will be able to locate that variable(s) under the specified category when you are searching for it in the blueprint editor.

Speaking of UPROPERTY parameters, we are only able to access the SpawnLocation variable in the blueprint editor because we added the BlueprintReadWrite parameter in UPROPERTY.

If you remove BlueprintReadWrite from UPROPERTY you will not be able to locate the SpawnLocation variable. So, if you want to expose the variables you declare in a C++ class, use the UPROPERTY macro and add BlueprintReadWrite parameter inside.

Now to set the Spawn Location variable, drag the Spawn Location Box collision from the Components tab in the graph and connect it to the Set Spawn Location value:

Img 31

Repeat the same process but this time set the Trigger variable that we declared in the BaseLevel.h file. You can locate the Set Trigger node in My Triggers category, the same category where Set Spawn Location node is located:

Img 32
You can copy the nodes from here:
 


Creating More Level Parts

Moving forward, we are going to create more level parts. Inside the Content -> Blueprints -> Levels, Right Click -> New Blueprint class.

This time we are not going to inherit from the BaseClass, instead we are going to inherit from the BP_Level1 blueprint that we created:

Img 33

Name the new blueprint BP_Level2 and open it in the editor. You will notice that BP_Level2 looks exactly like BP_Level1:

Img 34

This is because we have inherited BP_Level1 which means we inherit all of its variables and components and any code that we wrote in the Event Graph as well.

You can easily see this by going in the Event Graph window:

Img 35
From the BeginPlay node we call the BeginPlay of the parent object. We know that the parent object is BP_Level1 and we know what code we put in the BeginPlay of that blueprint.
 
To create a different level part, we can move around the existing parts of the level that we inherited from BP_Level1. For example we can change the location of the Cube Mesh component:
 
Img 36

We can also duplicate the Cube Mesh component, rename the duplicate to Cube 2 and we can change its location and scale values:

Now duplicate Cube 2, rename it to Cube 3 and set the following values in the Transform property:

Img 37
And with that change, we are done with BP_Level2 blueprint. This is how the final result looks like:
 
Img 38

This is how we are going to create all other level parts that will go up to 10. So the next blueprint that you will create will be BP_Level3, and you will inherit from BP_Level1 again, and then add modifications to create a new level part and you will repeat this process until you have 10 level part blueprints.

I am not going to put a guide here to do that because its basically the same what we did for BP_Level2, it’s just duplicating components, renaming them and changing their values in the Transform component.

Instead I am going to leave images of every level part so that you can see how they look like and replicate their look.

I will mention here that it is not necessary that you create the exact same level parts as I did, experiment with your own imagination and create your own level parts, you can always change them if the game is difficult because we can’t pass some level parts.

Here is the list of all remaining level parts.

BP_Level3:

Img 39

BP_Level4:

Img 40

For BP_Level5, we are going to add two spike obstacles as part of the blueprint to make the game more challenging for the user who is playing.

To do that, we are going to add a new Child Actor component. Click on the Add Component button, filter for child actor and select it:

Img 41

Rename the Child Actor component to Spike 1, after that select it, and under the Details tab locate the Child Actor component property where you have a field called Child Actor Class. Click on the drop down list and select BP_Spike as the class:

Img 42

Using the Child Actor component we can use other blueprints we created in another blueprint. The blueprint we use, in this case BP_Spike, will have all of its properties the same way we defined them when we created that blueprint.

You can duplicate Spike 1 same as any other component to create copies of it. This is how the final version of BP_Level5 looks like:

Img 43

Moving forward with other level blueprints, we have BP_Level6:

Img 44

BP_Level7:

Img 45

BP_Level8:

Img 46

BP_Level9:

Img 47

BP_Level10:

Img 48


Where To Go From Here

In this tutorial we created the Spike and BaseLevel class. We also created an obstacle blueprint and 10 level parts that we will use to procedurally generate the level while the player actor is moving.

In the next part titled Creating The LevelSpawner Class And Generating The Game Level Using Procedural Level Generation you will learn how to dynamically generate levels using a technique called procedural level generation.

Leave a Comment