This sample game project is deprecated, and is not supported for any version of the Unreal Engine after 4.24. Please make sure you have an appropriate engine version installed before loading this project. For more information on updating a project to the latest engine version, refer to Updating to the Latest Changes from Epic.
This document refers to a sample game project called Strategy Game. You can find this project by doing the following:
- Click the Learn tab in the Epic Launcher and scroll down to the Legacy Samples section.
- Click the image for Strategy Game to see a description of the project. Click the yellow button named Free.
- After a short time loading, the button will change to Create Project. Click this button and the launcher will prompt you to choose a project name and location.
- Click Create and the project will download to your designated folder.
The Tower Defense sample is an example of a RTS/Tower Defense game.
A complete list of the featured concepts:
- Simple AI Logic
- Automated Pawns
- Top-view Camera
- Building Construction
- Main Menu
- In-Game HUD with Combination of Canvas Drawing and Slate Widgets
- In-Game Menu
In the Tower Defense, the player must defend their brewery by building arbalest, auto-arbalest, and flamethrower turrets, which can be supplemented with minion Pawns. The Pawns can be outfitted with hammers and shields if upgrades are purchased for their brewery. Turrets, minions, and upgrades all cost gold, which can be harvested from gold nodes and also collected upon killing enemies. If the player can survive the five waves of enemies, including a final boss, without losing their three lives, they win the game!
AI Logic and Automated Pawns
The AI logic in Tower Defense is a simple finite-state machine (FSM) implementation. The two possible states are moving toward the enemy base and attacking enemies, both of which are
separate classes inherited from StrategyAIAction
. The states are in a priority array, with the most important action first. This array is iterated and the most suitable action to execute is
selected, and the current action can be stopped if there is an action with higher priority to be executed.
Both enemy and friendly Pawns operate with the AI logic, moving towards the opposing base and attacking Pawns of the other team if they encounter them. While players cannot control the movement or behavior of their friendly Pawns, they are able to buy new units to spawn.
Blueprint are also used to add logic to Minion Pawns. Both friendly and enemy Pawns can be equipped with shields; friendly Pawns get shields if the armory upgrade has been purchased for the brewery, and enemy Pawns get shields if they were spawned due to the SpawnHeavyFunction or SpawnEndBossFunction being called in the Level Blueprint. If a Pawn has a shield, projectiles from the auto-arbalest will be destroyed and do no damage. This logic is carried out using Blueprint Interfaces. The Minion Blueprint also contains a network to slow down enemy Pawns when they are hit by a charged-up fire turret.
Building Construction
There are two building classes in Tower Defense - StrategyBuilding
and StrategyBuilding_Brewery
. All the turret types in Tower Defense, as well as the empty building slot, are designed using
Blueprint with StrategyBuilding
as the parent class. Players can click on an empty building slot to show the context menu and select a new building to construct. When a building is constructed, the empty building slot is destroyed and the new building is spawned.
There is a mechanism in place for upgrading buildings as well. The StrategyBuilding_Brewery
class implements this case so that upgrades are built in connected slots near the brewery base.
Again, the code in Tower Defense simply creates the base building classes. All logic and design of the buildings in the Tower Defense were made by level designers in Blueprint.
Brewery
The Brewery Blueprint has the parent class StrategyBuilding_Brewery
, and also includes an AIDirector component. There are two breweries placed in the TowerDefenseMap, one for the enemy where enemy
Pawns spawn, and one friendly brewery where an armory and a smithy upgrade can be built, and friendly Pawns can be spawned. There is no graph logic present in the Brewery Blueprint, just Defaults set for the
building properties and a Components list including the AIDirector, a TriggerBox, and a Static Mesh.
Upgrades
There are two upgrade slots attached to the friendly brewery. These slots are Blueprint Classes which are also derived from the StrategyBuilding
class. If an upgrade is selected from the Brewery menu,
one slot will be replaced by that upgrade. Only one smithy upgrade and one armory upgrade can be purchased.
Once a smithy upgrade is purchased, the build will be started, firing the OnBuildStarted event in the Wall_Smithy Blueprint. This Blueprint also informs the system that the upgrade has been built,
once the build completes. After this point, any friendly Pawns that are spawned will be equipped with a shield attachment, a Blueprint derived from the StrategyAttachment
class. The network which assigns the
"shield-attaching" behavior after the armory reports that it has been constructed is present in the PlayerBaseUpgrades collapsed graph in the TowerDefenseMap Level Blueprint. The StrategyAttachment
class simply contains a SkeletalMeshComponent
; the mesh and attachment point for the attachment are set in the Defaults for the Attachment_Armorer Blueprint.
The armory Blueprint contains the same logic setup with OnBuildStarted and OnBuildFinished events. After construction of the armory, any friendly Pawns spawned will be equipped with a hammer, also derived from the
StrategyAttachment
class. The network which assigns the "hammer-attaching" behavior after the smithy reports that it has been constructed is also present in the PlayerBaseUpgrades collapsed graph in the TowerDefenseMap Level Blueprint.
Turrets
Empty Slot
The empty slot is also a Blueprint with the StrategyBuilding
parent class, Wall_EmptySlot. No logic is present in the Blueprint graphs. This is a Blueprint Class with Defaults set for building
properties and Static Meshes and a trigger box set as the Components.
The possible turret upgrades are all set in the Defaults of the Wall_EmptySlot Blueprint, in the Upgrades section of the Building category.
Arbalest
The Wall_arbalest Blueprint contains the logic for the arbalest, the basic turret type. The arbalest shoots the closest enemy with a medium-strength projectile, automatically firing arrows in its default mode. The player can manually fire the arbalest as well, by clicking on it and dragging in the direction they want the arrow to fire. The longer the mouse drag is, the stronger the shot will be.
The arbalest projectile is stored in another Blueprint, Projectile_arbalest, derived from the Blueprint TestProjectile which has StrategyProjectile
. The Wall_arbalest Blueprint has a number of sub-networks, all contained within the EventGraph. No Blueprint logic is present in the ConstructionScript.
Auto-Arbalest
The Wall_arbalest_auto Blueprint contains the logic for the auto-arbalest. The auto-arbalest shoots projectiles in a straight line from the wall, doing a small amount of damage to every unit the projectiles pass through. The auto-arbalest arrows are not destroyed unless they hit a wall or an enemy with a shield. It is possible to aim the auto-arbalest by clicking and dragging so that the auto-arbalest faces the desired direction; the auto-arbalest will continue to fire in the aimed direction while the mouse button is held down, and will return to its default firing position when the mouse button is released.
Like the arbalest, this turret shoots projectile arrows contained in a separate Blueprint. The Projectile_arbalest_auto auto-arbalest arrows are not destroyed unless they hit an enemy Pawn with a shield or a wall, and this behavior is carried out with the aid of Blueprint Interfaces, Interface_Auto_Arbalest and Interface_Auto_Projectile.
Flamethrower
The flamethrower does not shoot projectiles like the other turret types. Instead, it burns all enemies in the flame area. It is possible for the player to charge the flamethrower by clicking on it and holding; depending on how long the mouse button is held, the flame released after charging can do up to three times more damage, as well as slowing down any enemy Pawns hit by the charged fire. If the player charges up the flamethrower, there is a small cool down afterwards and then the regular fire attack continues.
Camera
The camera in Tower Defense has a fixed view angle and the ability to zoom in and out with the mouse scroll wheel. The camera calculations are made inside the CalcCamera
function in the StrategyPlayerController
class, while
constants like the camera minimum and maximum offset, camera angle, and camera speed can be set in DefaultGame.ini
.
A spectator Pawn is used to create a player without a visible Pawn.
In-Game HUD
The in-game HUD is created with a mixture of Canvas drawing and Slate widgets.
In the top-left corner, a game timer counts down the warm-up time for the game, using the function GetGameTime
in the class SStrategySlateHUDWidget
. After the game begins, this countdown disappears,
and the display for the number of lives left (1) is revealed. The properties for the "lives left" display are set in the DrawLives
function of the AStrategyHUD
class; the initial number of lives
is set in the PlayerBaseUpgrades subgraph in the TowerDefenseMap Level Blueprint.
The current gold resources are displayed in the top-center of the screen (2). Both the game timer and the resource display are defined using basic widgets in the SStrategySlateHUDWidget
. The same class
is used to create all top level widgets, but not all of these widgets are displayed by default.
The mini-map is in the bottom-left corner of the HUD (3). It is built from an invisible Slate widget overlay which handles the input and the actual map image, which is drawn using Canvas. SStrategyMiniMapWidget
is responsible for moving the camera when the button is clicked or held on the mini-map area.
When a building slot is clicked on, the SStrategyActionGrid
menu appears. There is only one instance of this widget; its location is determined by the active building slot. Calculating the screen
position of the menu is done in the DrawHUD
method, which projects the selected Actor location to 2D coordinates. The look and event mapping of the action buttons for this menu are defined in the
AStrategyBuilding
class in either the ShowActionMenu
method or the ShowCustomAction
method. The Button
widget is defined in the SStrategyButtonWidget
class, and any additional information
bound to the action buttons is stored in the FActionButton
information structure.
The health bars of Pawns and buildings are drawn on the canvas using the DrawActorsHealth
method. Each team has a different healthbar texture.
In the bottom-right corner of the HUD, there is a PauseButton
(4) which toggles pausing the game and the in-game menu visibility.
After the game time is up, or one of the bases is destroyed, the game will pause and "Victory" or "Defeat" text will be animated from the center of the screen. The animation changes the font size over time.
This text is created using a simple STextBlock
widget with delegates for Visibility, Font, and Text.
Menus
Main Menu
The main menu is contained in the StrategyMenu map, which loads the main menu specific HUD. The menu is Slate-based, with SStrategyMenuWidget
being responsible for the main menu animations,
layout, and event handling. The SStrategyMenuItem
class inherits from the SStrategyButtonWidget
used in the In-Game HUD and describes a single menu item. Each menu item (and events attached to the items)
is defined in the StrategyMenuHUD
.
To go back to previous menus, an array of shared pointers to menus is stored in the MenuHistory
variable. This variable acts like a stack to hold previously viewed menus, so that it is easy to go back while
also removing the requirement to store the parent of a menu so that menus can be reused in multiple places.
Menu animations use interpolation curves defined in SStrategyMenuWidget::SetupAnimations
. Each curve has a start time, duration, and interpolation method defined, and can be played forward and in reverse.
To play animation attributes at a specific time, GetLerp()
is used, which returns a value between 0.0f and 1.0f.
In-Game Menu
When the in-game menu is active, a semi-transparent full-screen Slate overlay is shown, and the game is paused. PauseMenuButtons
are defined in SStrategySlateHUDWidget
. There are two buttons for the in-game pause
menu: one exits the game, and the second returns to the main menu. To exit the in-game menu, the player should press the pause button in the bottom right corner a second time.
Level Blueprint
The Level Blueprint has a modular structure for spawning each wave, as well as initialization and win/lose conditions.
Enemy Spawns
The waves are constructed using three Blueprint Macros: spawn fast, spawn normal, and spawn heavy. Each of these sets up the desired unit parameters and attachments and then waits for the SpawnMinions
function in the
StrategyAIDirector
to fire. The macro waits for the enemy brewery's StrategyAIDirector
to report back that the wave has been spawned, and then execution is allowed to leave the sub-network.
Each spawn macro takes two execution inputs, one for beginning the macro and one for opening the Gate after the OnWaveSpawned event fires, and an integer input for the number of Pawns to spawn.
Functions from the class StrategyAIDirector
are called, with inputs specific to each type of Pawn wave. The three functions are SetDefaultWeapon
, SetDefaultArmor
, and SetBuffModifier
.
SetDefaultWeapon
and SetDefaultArmor
take Blueprint as inputs and assign those Blueprint as the new default weapon or default armor for the spawn. For example, all enemy Pawns spawned by
the SpawnFastMacro have the Attachment_Smithy hammer Blueprint as their default weapon, and all enemy Pawns spawned by the SpawnHeavyMacro have the Attachment_Armorer shield Blueprint as their default armor.
The last StrategyAIDirector
function called by the spawning Blueprint functions is SetBuffModifier
, which has a number of data inputs including the attack abilities, health bonus, speed, and size
of the Pawns. These inputs are all exposed to the Blueprint, so it is straightforward for a level designer to create a new class of enemy Pawn to spawn. Finally, each spawning Blueprint function sets the WaveSize
property of the enemy brewery's StrategyAIDirector
.
There are five waves of enemies, each with different combinations of fast, normal, and heavy enemy Pawns. At the beginning of a wave, the Show Wave Title node displays the wave number. Then, the first enemy spawn of the wave is called. There are two types of delays after a spawn: a timer delay set by a Delay node, or a Pawn-based delay set by a WaitForWaveMacro. The WaitForWaveMacro macro continually checks to see how many enemy Pawns are alive and will not let execution leave the macro until either the delay time has expired or all of the enemy Pawns are dead. After all the spawns for a wave are complete and all of the enemy Pawns for that wave are dead (or two minutes have passed), a Remote Event node is used to call the Custom Event for the next wave.
Win and Lose Conditions
In the game, your base has 3 lives. A life is subtracted if an enemy Pawn makes it to the friendly brewery, and the game is lost if all lives are lost. The enemy boss reaching the friendly brewery also results
in losing the game. To win the game, you must defeat all five waves of enemy Pawns before losing all of your lives. The networks setting up both the win condition and the lose condition are in TowerDefenseMap's Level Blueprint, and call functions set up
in the class StrategyGameMode
, which is derived from the AGameModeBase
class. The StrategyGameMode
class also contains functions such as InitGame
which initializes the game and is called before the Actors' PreInitializeComponents, SetGamePaused
, and SetGameDifficulty
.
After the end boss spawned in Wave 5 has been killed, a Remote Event node is used to call the Winning Custom Event. This Custom Event, located in the Win Condition comment box, then fires and triggers execution of a sub-network WaitForWin,
which checks that no other enemy Pawns are still alive. If this is found to be true, the Finish Game
function is called with the WinningTeam input set to "Player".
There are two nodes in the Lose Condition comment box which call the Finish Game
function with the WinningTeam input set to "Enemy", causing the player to lose the game. The first is triggered when all three lives have
been lost, due to enemy Pawns reaching the friendly brewery. A MultiGate node is triggered each time an enemy Pawn reaches the friendly brewery. The first and second output execution pins of the MultiGate node each connect to nodes which update the
NumberOfLives of the friendly brewery, decreasing the value by one each time. The last output execution pin sets the number of lives of the friendly brewery to zero, and then triggers the Finish Game
function with the WinningTeam set to
"Enemy". Once the enemy boss has spawned, the BossSpawned Custom Event closes the Gate node leading to the "3 lives version" MultiGate, and opens another GateNode leading to the second Finish Game
function with the WinningTeam set to
"Enemy". This creates an open network so that if the end boss reaches the friendly brewery, the FinishGame function will be activated and the player will lose the game.
Resource Node - Gold
The gold nodes are Blueprint with the parent class StrategyResourceNode
. This class contains the public functions GetAvailableResources
and GetInitialResources
, the protected function OnDepleted
,
a BlueprintImplementableEvent
to report when the resource has been depleted, and the protected property NumResources
to set the amount of the resource present in the node.
The Blueprint for a gold node contains sub-networks to make the node appear and disappear on a timer. The ConstructionScript in the Blueprint sets the node to automatically be hidden when it is placed in the level. When the gold node appears, AppearFX particle effects are played along with an apparition noise. If the OnDepleted event fires because the node is successfully collected, the amount of gold present in the node is added to the player's total gold pool. The CollectFX particle effects and the CoinSound is played, and then the node is hidden again. If the player fails to collect the node in time by clicking on it, FadeFX particle effects and an appropriate sound is played.