C++ Game Mode Tutorial
Contents
C++ Game Mode Tutorial
This tutorial will teach you how to make a simple Gun Game mode for UT using C++. We'll be making a UE4 plugin that defines a child class of AUTGameMode. I recommend reading the C++ Mutator Tutorial, Blueprint_Mutator_Tutorial_-_Instagib and Blueprint_Mutator_Tutorial_-_Low_Grav first. I will be glossing over things covered in depth in other tutorials.
This tutorial is out of date and does not compile in 4.14, working version can be found within Plugins/SampleGameMode
Requirements
- Engine version: 4.3
- Skill level: intermediate C++ and blueprint knowledge
Directory Structure
- If UnrealTournament\Plugins does not already exist, create it
- Create a directory inside of UnrealTournament\Plugins to hold our game mode, in this case it would be "SampleGameMode"
- Create "Source" and "Content" directories inside of the SampleGameMode directory
- Create a "Public" directory and a "Private" directory inside of the last "Source" directory that we made
UPlugin File
- We need to create SampleGameMode.uplugin to let UE4 know we have a plugin it should load
- It should live at the same level that the Source directory does
- The key difference between this .uplugin and the one in the mutator sample is that EnabledByDefault and CanContainContent are set
SampleGameMode.uplugin - Plugin defintion
1 {
2 "FileVersion" : 3,
3
4 "FriendlyName" : "Sample Game Mode",
5 "Version" : 1,
6 "VersionName" : "1.0",
7 "CreatedBy" : "Epic Games, Inc.",
8 "CreatedByURL" : "http://epicgames.com",
9 "EngineVersion" : "4.3.0",
10 "Description" : "Sample Game Mode",
11 "Category" : "UnrealTournament.GameMode",
12 "EnabledByDefault" : true,
13 "CanContainContent" : true,
14
15 "Modules" :
16 [
17 {
18 "Name" : "SampleGameMode",
19 "Type" : "Runtime",
20 "WhitelistPlatforms" : [ "Win32", "Win64" ]
21 }
22 ]
23 }
Build.cs File
- We need to create SampleGameMode.Build.cs so that Unreal Build tool knows how to generate our dll file
- It should live at the same level that the Public and Private directories do
- We'll use the PrivateIncludesPaths array to make #include cleaner throughout our source files
SampleGameMode.Build.cs - Unreal Build Tool definitions
1 namespace UnrealBuildTool.Rules
2 {
3 public class SampleGameMode : ModuleRules
4 {
5 public SampleGameMode(TargetInfo Target)
6 {
7 PrivateIncludePaths.Add("SampleGameMode/Private");
8
9 PublicDependencyModuleNames.AddRange(
10 new string[]
11 {
12 "Core",
13 "CoreUObject",
14 "Engine",
15 "UnrealTournament",
16 }
17 );
18 }
19 }
20 }
Header File
- Unreal Build Tool requires that we have a shared header file as the first include in every cpp source file in the plugin.
- We'll put the SampleGameMode.h header file inside of the Public directory
- include "SampleGameMode.generated.h" is required by Unreal Header Tool
- Our header is a bit more complex than the mutator one. We've defined two custom structs that we've made BlueprintType so they'll be visible when we're in the editor.
- You'll notice that each of the structs is just a TArray and their usage in the ASampleGameMode is just a TArray. UE4 does not currently support TArrays of TArrays so we work around that by housing the inner array inside of a structure.
- We've exposed the StartingInventories and ScoringDamageTypes TArrays to blueprints so they can be filled with content references later. In order for the editor to see them, we made them UPROPERTYs with BlueprintReadWrite markup
SampleGameMode.h - Sample Mutator class definition
1 // Copyright 1998-2014 Epic Games, Inc. All Rights Reserved.
2 #pragma once
3
4 #include "Core.h"
5 #include "Engine.h"
6 #include "UTWeapon.h"
7 #include "UTGameMode.h"
8
9 #include "SampleGameMode.generated.h"
10
11 USTRUCT(BlueprintType)
12 struct FStartingInventory
13 {
14 GENERATED_USTRUCT_BODY()
15
16 UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="GameMode")
17 TArray<TSubclassOf<AUTInventory>> Inventory;
18 };
19
20 USTRUCT(BlueprintType)
21 struct FDamageTypeToProgess
22 {
23 GENERATED_USTRUCT_BODY()
24
25 UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "GameMode")
26 TArray<TSubclassOf<UDamageType>> DamageType;
27 };
28
29 UCLASS(Blueprintable, Meta = (ChildCanTick))
30 class ASampleGameMode : public AUTGameMode
31 {
32 GENERATED_UCLASS_BODY()
33
34 UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="GameMode")
35 TArray<FStartingInventory> StartingInventories;
36
37 UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "GameMode")
38 TArray<FDamageTypeToProgess> ScoringDamageTypes;
39
40 virtual void GiveDefaultInventory(APawn* PlayerPawn) override;
41 virtual void ScoreKill(AController* Killer, AController* Other, TSubclassOf<UDamageType> DamageType) override;
42 virtual void GiveNewGun(AUTCharacter *UTCharacter);
43 };
Plugin Module Interface
- Very similar to the mutator sample here, just some name changes
SampleGameModePlugin.cpp - Plugin Module Interface
1 // Copyright 1998-2014 Epic Games, Inc. All Rights Reserved.
2
3 #include "SampleGameMode.h"
4
5 #include "Core.h"
6 #include "Engine.h"
7 #include "ModuleManager.h"
8 #include "ModuleInterface.h"
9
10 class FSampleGameModePlugin : public IModuleInterface
11 {
12 /** IModuleInterface implementation */
13 virtual void StartupModule() override;
14 virtual void ShutdownModule() override;
15 };
16
17 IMPLEMENT_MODULE( FSampleGameModePlugin, SampleGameMode )
18
19 void FSampleGameModePlugin::StartupModule()
20 {
21
22 }
23
24
25 void FSampleGameModePlugin::ShutdownModule()
26 {
27
28 }
The Game Mode (C++)
- Our aim with this tutorial is to make a game mode that functions like "Gun Game", you start with a weapon according to your score and going up in score gives you the next gun in the sequence. Pickups are disabled and so are weapon drops.
- We'll use C++ to handle giving players the right guns on spawn and giving them the right guns after scoring.
- We'll use Blueprints to handle disabling weapon drops and pickups.
- Blueprints will also be used to get content references to weapons and damagetypes as hard coding them in C++ can be brittle.
- ASampleGameMode::GiveDefaultInventory reads the content reference from StartingInventories that we'll set later in blueprints and spawns that inventory for the spawning player
- ASampleGameMode::ScoreKill verifies that the killing damage type is equal to the content reference from ScoringDamageTypes and then calls GiveNewGun on the killer
- ASampleGameMode::GiveNewGun will discard the character's current inventory and give them the proper gun by reading a content reference from StartingInventories
SampleGameMode.cpp
1 // Copyright 1998-2014 Epic Games, Inc. All Rights Reserved.
2
3 #include "SampleGameMode.h"
4
5 ASampleGameMode::ASampleGameMode(const FPostConstructInitializeProperties& PCIP)
6 : Super(PCIP)
7 {
8 }
9
10 void ASampleGameMode::GiveDefaultInventory(APawn* PlayerPawn)
11 {
12 // Don't call Super
13
14 AUTCharacter* UTCharacter = Cast<AUTCharacter>(PlayerPawn);
15 if (UTCharacter != nullptr && UTCharacter->GetInventory() == nullptr)
16 {
17 int32 InventoryIndex = PlayerPawn->PlayerState->Score;
18 if (InventoryIndex >= StartingInventories.Num())
19 {
20 InventoryIndex = StartingInventories.Num() - 1;
21 }
22
23 if (StartingInventories.IsValidIndex(InventoryIndex))
24 {
25 for (int32 i = 0; i < StartingInventories[InventoryIndex].Inventory.Num(); i++)
26 {
27 if (StartingInventories[InventoryIndex].Inventory[i] != nullptr)
28 {
29 UTCharacter->AddInventory(GetWorld()->SpawnActor<AUTInventory>(StartingInventories[InventoryIndex].Inventory[i], FVector(0.0f), FRotator(0, 0, 0)), true);
30 }
31 }
32 }
33 }
34 }
35
36 void ASampleGameMode::ScoreKill(AController* Killer, AController* Other, TSubclassOf<UDamageType> DamageType)
37 {
38 // Just a suicide, pass it through
39 if (Killer == Other || Killer == nullptr)
40 {
41 Super::ScoreKill(Killer, Other, DamageType);
42 return;
43 }
44
45 APlayerState* KillerPlayerState = Killer->PlayerState;
46 if (KillerPlayerState)
47 {
48 int32 DamageIndex = KillerPlayerState->Score;
49 if (DamageIndex >= ScoringDamageTypes.Num())
50 {
51 DamageIndex = ScoringDamageTypes.Num() - 1;
52 }
53
54 for (int32 i = 0; i < ScoringDamageTypes[DamageIndex].DamageType.Num(); i++)
55 {
56 if (DamageType == ScoringDamageTypes[DamageIndex].DamageType[i])
57 {
58 Super::ScoreKill(Killer, Other, DamageType);
59
60 // If we changed score, give player a new gun
61 if (DamageIndex != KillerPlayerState->Score)
62 {
63 AUTCharacter* UTCharacter = Cast<AUTCharacter>(Killer->GetPawn());
64 if (UTCharacter != nullptr)
65 {
66 GiveNewGun(UTCharacter);
67 }
68 }
69
70 break;
71 }
72 }
73 }
74 }
75
76 void ASampleGameMode::GiveNewGun(AUTCharacter *UTCharacter)
77 {
78 UTCharacter->DiscardAllInventory();
79
80 int32 InventoryIndex = UTCharacter->PlayerState->Score;
81 if (InventoryIndex >= StartingInventories.Num())
82 {
83 InventoryIndex = StartingInventories.Num() - 1;
84 }
85
86 if (StartingInventories.IsValidIndex(InventoryIndex))
87 {
88 for (int32 i = 0; i < StartingInventories[InventoryIndex].Inventory.Num(); i++)
89 {
90 if (StartingInventories[InventoryIndex].Inventory[i] != nullptr)
91 {
92 UTCharacter->AddInventory(GetWorld()->SpawnActor<AUTInventory>(StartingInventories[InventoryIndex].Inventory[i], FVector(0.0f), FRotator(0, 0, 0)), true);
93 }
94 }
95 }
96 }
Editor Setup
- You'll need to enable "Show Plugin Content" in the Content Browser's "View Options" menu
- Inside the SampleGameMode folder, make two new blueprints: BP_SampleGameBuiltinMutator with parent UTMutator and BP_SampleGameMode with parent SampleGameMode. Make sure to check out Blueprint_Mutator_Tutorial_-_Instagib and Blueprint_Mutator_Tutorial_-_Low_Grav for more information on blueprints.
Mutator Blueprint
- We want to use a mutator to destroy all map pickups and all dropped pickups similar to Blueprint_Mutator_Tutorial_-_Instagib
- We'll override the Check Relevance function as seen below
Game Mode Blueprint
- Open up BP_SampleGameMode and go to the default properties tab
- First, we'll set the Built in Mutators array to reference BP_SampleGameBuiltinMutator
- Next, we'll set the HUD Class to UTHUD_DM
- Third, we'll set up the content references to our guns in the StartingInventories array
- Finally, we'll set up the content references to our damage types in the ScoringDamageTypes array
Testing
- To make typing our command line easier, you should add a game mode alias to DefaultGame.ini
- Under [/Script/Engine.GameMode] put +GameModeClassAliases=(ShortName="Sample",GameClassName="/SampleGameMode/BP_SampleGameMode.BP_SampleGameMode_C")
- The game type can now be tested by using ?game=sample instead of ?game=/SampleGameMode/BP_SampleGameMode.BP_SampleGameMode_C
- Make sure to restart the editor after putting that in the ini
- I prefer to test in PIE by setting the number of clients to 2, but you can also test via commandline by running a server with "ue4editor unrealtournament -game example_map?game=sample?listen" and a client with "ue4editor unrealtournament -game 127.0.0.1"