Adding the finishing touches to Barrel Hopper

Now that we have explored the basics of animation and sound with UE4, we can finish polishing our Barrel Hopper project. We are going to be adding a session timer to the HUD, a gameover screen, and ragdoll physics to our character so that when the player runs into a barrel, the character will go limp and hilarity will ensue.

Ragdolls and Event dispatchers

Let's start by adding ragdoll physics to the character upon death. The reason we are starting here is to implement the ragdoll. We are also going to have to implement an event dispatcher. An event dispatcher allows us to bind a collection of events that will trigger when the single event dispatcher is executed. This means we can execute all bound functionality across multiple blueprints from one call to the event dispatcher. This is similar to the Event Delegate we currently use in the BH_GameMode but allows us to bind events to a dispatcher that has no explicit functionality of its own.

We are also going to have to create a custom event in the BH_Character to bind to this Event Dispatcher, which will replace the functionality following Event Destroyed. We cannot use OnDestroy as the moment we call the Destroy function, the mesh will disappear and we will not be able to see the character ragdoll. Instead we will use this new custom event and a delay node to destroy the character.

Create an event dispatcher now by adding one to the MyBlueprint Panel of the BH_Character blueprint. Call this dispatcher DeathDispatcher. Then create a new custom event in the BH_Character event graph titled Kill. We are going to be transferring all of the Event Destroyed functionality to the new Kill event. We then have to bind this event to our new DeathDispatcher. We will do this in exactly the same way as we set up our delegate association in Chapter 2, Blueprints and Barrels – Your First Game. Be sure to delete the old Event Destroyed node and the subsequent functionality we just copied.

Navigate to the BeginPlay event node within the BH_Character Blueprint and you will see our old functionality that we created to set the spawn transform of the player. Append this functionality with a BindEventToDeathDispatcher node. This node can be found using the pallet or the context sensitive search. Now bind this to the Kill event by joining the red square pin in the top right-hand corner of the Kill event node to the red square pin in the bottom left-hand side of the Bind Event to DeathDispatcher node. Your finished arrangement should appear as follows:

Ragdolls and Event dispatchers

Now we need to also make sure that we bind the correct event in the BH_GameMode and that we call the correct dispatcher when our player hits a barrel. Do this now by searching for the Call DeathDispatcher node and replacing the previous Destroy call that we had at the end of the Event Hit functionality in our BH_Character event graph. Then in our BH_GameMode, replace BindEventToOnDestroy with BindEventToDeathDispatcher. You will see these changes in the BH_Character Blueprint:

Ragdolls and Event dispatchers

And this in the BH_GameMode Blueprint:

Ragdolls and Event dispatchers

Now run the game and ensure that nothing has changed and our character dies and respawns as before. With this change in place we are now free to add our ragdoll functionality to the Kill event without worrying about our character deleting itself before the player's view has moved to the respawned character.

Enabling ragdoll on the character mesh is actually quite simple as the skeleton's physics asset has already been created for us. If you are unfamiliar with this asset, it's called SK_Mannequin_PhysicsAsset and it will have been migrated with the skeleton mesh. Open the asset now and you will be presented with this view:

Ragdolls and Event dispatchers

Each of those capsules surrounding the player acts as physics bodies that are constrained by joints. They will act as the driving manipulators for the bone positions of the mesh. The result, a limp ragdoll that allows for the motion of the skeletal mesh! Usually these assets have to be created by hand, one capsule at a time, when the mesh is imported into the engine. These assets must be created if you would like to utilize physics simulations for the character mesh.

To enable ragdoll on the character mesh, simply drag a reference node into the Event Graph for the Mesh object. Drag a line off of the reference pin and search for the SetSimulatePhysics node, then check the bool simulate parameter. To ensure that the mesh ragdolls properly and collides with surrounding physics volumes, be sure to set the collision preset on the Mesh component to Ragdoll, the setting can be found in the Details panel with the Mesh component selected. The arrangement can be seen as follows:

Ragdolls and Event dispatchers

The next thing you must do is disable the collision on the Capsule collider as we no longer want objects in the world to collide with this component. You can do this by dragging a visual reference to the capsule component into the Event Graph, then searching for the SetCollisionResponsesToAllChannels node. Set the target to be the capsule component reference then set the New Response parameter to Ignore via the provided dropdown. Append this new ragdoll functionality to the Kill event functionality.

The last thing we have to do is ensure that we destroy the actor after a period of time. Summon a delay node, append this to the ragdoll functionality, then set the duration of the delay node to 3.0 seconds. Directly after this delay node, call Destroy actor. This means that, when the player dies, the camera will stay long enough over the dead character to see it ragdoll for a time, then after the camera moves back to the spawn position for the next character, the old character will delete itself. Your finished kill event functionality will look similar to this:

Ragdolls and Event dispatchers
Ragdolls and Event dispatchers

Now play the scene, deliberately kill the character by running into a barrel, and see the character mesh flail around, similar to what can be seen in the following:

Ragdolls and Event dispatchers

Creating a basic HUD

Now that we have all of our visual polish features in place, we can create a HUD that will show how long the player's current run attempt is. This HUD will also complete our game loop and allow the player to see their final time upon reaching the top of the level. We will also offer the player the choice to exit the game upon completion or to reset to the start position. To do this we are going to have to create two new objects, a BH_HUD object and a BH_Winner object. BH_HUD will inherit from the Unreal Engine HUD object and the BH_Winner will inherit from the trigger box object in a similar way to our BH_BarrelKiller objects.

Making the HUD object

Our BH_HUD object is going to be responsible for drawing the in-game HUD as well as the end game screen. This means we are going to be creating two functions, one called DrawInGame and one called DrawEndGame. We will need to call either function based off of a bool that represents whether or not the player has reached the end of the level. Create a BH_HUD object now by opening the blueprint wizard, be sure that the parent class of the blueprint is of type HUD.

In the MyBlueprint panel of our BH_HUD blueprint we are going to be adding two functions, DrawInGame and DrawEndGame. Then we are going to add a float variable titled PlayerAttempt, a Font reference titled ScreenFont, and a bool value titled GameWon. Do this now. In the event graph of the BH_HUD there will be an event titled EventRecieve DrawHUD, this event will be called by the engine when the renderer deems it necessary to draw the HUD. From this event create a branch node based off of GameWon. From the True Branch call DrawEndGame, and from the false Branch call DrawInGame. Great! The shell of our HUD object has been created. Now we need to add meaningful functionality to the two draw functions. Before we move on, use these images to ensure you have set up the BH_HUD blueprint properly, your graph should appear as follows:

Making the HUD object

And your MyBlueprint panel will look like this:

Making the HUD object

Drawing the in game HUD

Open the DrawInGame function graph by double-clicking on the entry in the Functions sections in the MyBlueprint panel. We need to draw the player's current run attempt to the screen. We can do this via a function called DrawText. We need to figure out how we can position this text so it appears at the top-center of the screen. We can do this by getting our viewport dimensions, dividing the width value by two, then subtracting the width of our text that will be drawn to the screen from this value. This calculation will give us an X position to draw the text that will compensate for the width of the text string so that the text always appears in the center of the screen.

This is easily done with the following node arrangement:

Drawing the in game HUD

As you can see, we are using the PlayerAttempt variable we added earlier to determine what is to be drawn. We are then getting our viewport dimensions via a pure function, dividing the x component by two (or multiplying it by 0.5), then subtracting the resultant X value by the string text length multiplied by the width of a character using our provided font. The font type as of yet has not been specified, but we will get that later. You will notice that the Y value has been set to zero for the screen text as we wish the text to be flush with the top of the screen.

Now to add functionality to DrawEndGame. This will be slightly different as we need to draw a few additional lines, and we need to have the following text appear:

Drawing the in game HUD

This is going to require more of the same calculations we did for the in-game text but we are going to position the text in the center of the screen. The arrangement of nodes appears as follows:

Drawing the in game HUD
Drawing the in game HUD

As you can see, the functionality is very similar to that of DrawInGame. This time however, we are also taking into account the vertical scale of the text as we wish for it to appear in the center of the screen. Also, instead of drawing three lines of text to the screen, we can instead create one big string that includes new line characters and draw that to the screen instead. To do this, simply hold shift when you press return to add a new line character to a text field. When taking the height into account, it's important to multiply the height by 3 as there are 3 lines of text that we are using for the end game scene. There will be a small margin of error as this does not account for the vertical spacing of the lines.

Making the font

Font objects are very easy to create and are done so in the same way as traditional Unreal Engine objects. Right-click in a content browser folder and create a font object found under the User Interface dropdown. Create a new font now and call it BH_Font. Open this font now. You will be presented with a new window, the font editing window. Here you can specify which installed font you wish to use. You can use any installed font file for this. You can also specify how the font is cached in memory as well as a default font size and name.

In our case, we need to press the Add font button that can be found in the Composite Font panel at the top of the window. This will then prompt you to name the font, select an installed font you wish to use, and which hinting algorithm to use with the font. Hinting of fonts is an algorithm used when rendering a font so that the outline of a font lines up with a rasterize grid (pixel grid). This makes the font appear smoother and removes visual artifacts when rendering a font to the screen. We can leave all of these values as default for now as our font will be nothing special.

We need to ensure our font is of a correct base size. The reason we will adjust the size of the font and not scale it from the DrawText node is because this way the font will maintain resolution. If we were to scale the font up using the DrawText node, it will appear pixelated. To adjust the size of the font, change the Legacy Font Size under the Runtime Font section in the details panel to 24. Now navigate back to our BH_HUD object and be sure to set the default value of our font reference screen to BH_Font.

Parsing information to and setting the HUD

To make sure we populate the PlayerAttempt variable with the correct value, we need to be able to increment that value while the player is alive and has yet to reach the end of the level. To do this, we simply need to increment this value from inside the BH_Character blueprint via the Tick event. We can do this by navigating to the Event Tick node in the BH_Character event graph. To get the HUD object we have created, simply use the GetPlayerController node to get a reference to the player controller, then from the output reference search for the function GetHUD. Cast this reference to BH_HUD. Now all we need to do is get the PlayerAttempt value from this BH_HUD reference, increment by the Delta Seconds variable that has been parsed via the Event Tick node, then set the PlayerAttempt variable with the result.

This can be done with the following arrangement:

Parsing information to and setting the HUD

The next thing we need to do is ensure we use our new BH_HUD object as the HUD for the game. This can be done back in the Details Panel of the BH_GameMode. We can set the HUD class parameter under the Classes section of the Details Panel of BH_GameMode to ensure that our new HUD object is used.

An end goal for the player, the chapter, and the project

Now all we need to do is create and flesh out the BH_Winner object. This object is simply going to dictate when the player reaches the end of the level, pause the game at this time, inform the HUD to end the game, and bring up the Game End text.

Start off by creating a blueprint that inherits from Trigger Volume. Call this blueprint BH_Winner. Address the blueprint Viewport and select the Collision Component. Make sure that the Box Extent settings in the Shape section of the Details panel is set to 96 across all axis. This will ensure that this box occupies 96cm3 of space in the scene.

We simply need to append functionality to the ActorBeginOverlap. Drag a line from the input OtherActor from the Event ActorBeginOverlap node. Call GetClass on this input reference via searching for it using the context sensitive search. Check that this class is equal to the BH_Character class by creating a class comparison node. Do this by searching for == after dragging a line from the purple class output pin. Create a branch node based off of this comparison operation. Now we need to get the HUD again and cast it to the BH_HUD. Do this in the same way we did when setting the PlayerAttempt variable. Instead, this time we will be setting GameWon to true then pausing the game by using the SetGamePaused function node.

The node arrangement appears as follows:

An end goal for the player, the chapter, and the project
An end goal for the player, the chapter, and the project

The only thing we have left to do is ensure that our game mode can receive input then when the player presses R, the game will restart or if the player has reached the end, press X to close the game.

This is very simply done by navigating to the event Begin Play in the BH_GameMode. From the functionality following this node, append one more node. Search for the Enable Input node. This node will take in a reference to the player controller you wish the object to receive input from. In our case we want our game mode to receive input from the default player controller (the keyboard) so we will use the GetPlayerController node and specify index 0, then plug the resultant player controller reference into the Player Controller parameter of the Enable input node.

Next, summon two input event nodes X and R. You can do this by simply searching for X in the pallet or context sensitive search. This is the other way to receive input events from within blueprints. It saves you having to create an input mapping, however you will not be able to change this input to something else without modifying the graph, like you can with an input mapping.

From the R event, summon a node called RestartGame. This node will reset the game to default values and positions. From the X event, check whether the game is paused by searching for the pure function IsGamePaused, then use a branch node to check whether this bool value resolves to true. If so, summon the node QuiteGame. This will take in a reference to a specific player and a quit preference. Leave both of these as default. The node arrangement should appear as follows:

An end goal for the player, the chapter, and the project
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.143.17.127