User interface - Adding functionalities

In the previous post we created the layout for our user interface. Before going on with the work on it, I want to add some useful stuff to C++ code.

I decided to store all informations about the player status (health points, resources, etc.) in the PlayerController class, becuse it seems the most logic place for them. So I'm adding a bunch of new variables:
  • lives: the number of actual health points left;
  • maxLives: the maximum number of available health points (it's 3 for now);
  • resources: the number of collected resources;
  • maxResources: the maximum number of collectible resources (let's say it's our cargo space, and I'm setting it to 5 for now).
All these variables are created with the UPROPERTY macro, so that they're visible from the Blueprints, and can be used to modify the User Interface widget.

I'm also going to create some new UFUNCTION methods that will handle the events coming from the User Interface buttons:
  • selectOptions(): will pause the game and open the Options window.
  • repair(): will check if we have enough resources, and eventually use 2 of them to restore 1 health point of the ship.
  • deployMine(): will check if at least 1 resource is available, and use it to deploy a mine in the actual position.  
Since all the logic for collisions is managed in the enemyShip class, I need a way to let every enemy know where to modify the health points in case of collision; that's why I'm going to add a method setController() to the spaceship class (it's the base class for our enemies, remember it?), where I can pass the PlayerController instance while creating the enemy ship:

Method body in spaceship class


Method call in MyPlayerController class

As you can see, I can pass the "this" object because I created all enemies inside the PlayerController class itself.

Now, back in the HUD widget, I can select one of the buttons and click on its "onClicked" event:


This will open the Graph layout and add the relative event; once we have all the 3 events we need, we can bind them to the previously created functions of the Player Controller class (remember to deselect the "Context sensitive" flag if you can't find them).

Here's the final result:


I sorrounded the 3 functions with a comment indicating they're all part of the Buttons behaviour; you can do this by selecting them all together, right-clicking on one of them, and selecting "Create Comment from Selection" in the context menu.

To set the target of the functions, I obtained the Player Controller instance at constructon time, and casted it to MyPlayerController class, creating a variable called Player Controller that I passed to the functions:




Now we are able to do something whenever the user clicks on one of the buttons, but we still need to update the interface when the player state changes, i.e. when we lose 1 health point or collect a resource; what we need is a custom event, firing from C++ code when one of these things happens.

I'm going to call my event DrawUI, and here's how to declare it in MyPlayerController.h:


It's declared as a void method in the header file, but we don't need to write its body in the relative .cpp file (and it's correctly underlined in green, warning us that there's no definition). The important keyword to use is the verbose BluePrintImplementableEvent, telling the compiler that we want it to be a custom event. Let's use it when the player gets hit:


here, I reduce the health points of 1 and call the DrawUI() method, that will fire an event in Unreal.

This method ReduceLives() will be called inside the Hit() method of EnemyClass, that we already created:


We should be done with C++ code, so let's switch back to Unreal Editor.
In the MyPlayerControllerBP blueprint, you should now be able to create a DrawUI event; now the problem is: we fire this event in the Player Controller blueprint, but we need to use it in the HUD blueprint, and it's not visible there... so what could we do? What we need now are Event Dispatchers, a useful tool that spreads the event messages to the entities in the game.

On the left side of the MyPlayerControllerBP Graph editor we have an Event Dispatchers panel. By clicking on the "+" icon I added a new one, and called it "DrawUI Dispatcher". 



Now I can create a "Call DrawUI Dispatcher" box and connect it to my DrawUI event, like this:


Now, every time the DrawUI event is fired, the Dispatcher will "send the message" (see the envelope icon?) to every listening entity.
Now let's go to the HUD Blueprint, to make it "listen" for the incoming messages from the DrawUI Dispatcher. To do this we have to create a Bind to DrawUI Dispatcher box:


This will bind a custom event we're going to create inside the Widget to this Dispatcher, firing it every time it receives the message from the dispatcher. Let's create a Custom Event box, and call it "DrawUI" (once again!):


Here are the 2 boxes connected. The Blue box shows an error because I tried to compile it, but it needs an instace of a MyControllerBP to know where the Dispatcher comes from, and I didn't give it one.
So I'm going to take the Player Controller variable, cast it to MyControllerBP and pass it to the function:


Here we are, with the complete set of functions to make the Event dispatching work! Basically, what we did here is the following: "When the DrawUI event fires from Player Controller, fire the same event in the HUD". I wish it was that simple!

You can easily check if it's working by chaining a "Print String" box to the DrawUI event, and see that whenever 2 ships hit each other, the string is printed in the console.

Next time we'll see how to actually redraw the HUD when the event is fired.

Game you next time!

Commenti