Tuesday, July 10, 2018

Gamepad menus and more!

I decided I wanted to give full gamepad support for the playable version of the project, which means menus must be navigatable using the gamepad. UE4 does not natively support gamepad navigation through UMG (only point-and-click with a mouse), so the programming must be done manually. I watched this video (several times) to learn how to do this: 



Figure 1: the programming that is called when the game starts. It displays the Main Menu, and defines actions for gamepad Up and Down buttons (in red), as well as the A button (Gamepad Face Button Bottom) to select. The nav types within the two blue functions are defined by an enumeration (not shown) that contains of two variables: main_up and main_down. 


Figure 1 - Main Menu Level Blueprint

Figure 2: the designer tab for the menu widget, which I created in December. Not much to see here other than that there is an animation timeline keyed to all of the elements:

Figure 2 - Main Menu designer tab

Figure 3: the blueprint for the menu widget. There is a lot going on! At the top, starting with EventConstruct, the fade-in animation is called, and Debussy's "La cathédrale engloutie" (Sunken Cathedral) is played. Then the button appearance is set, which is referenced by functions on other tabs. On the far left, there are red OnClicked and custom Event functions. These each get their own instructions, which flow from left to right. For example, when OnClicked_Main00Explore is called, this displays the loading screen, waits a few seconds, opens the UHS_1914_building level, then removes the Main menu widget. The OnClicked events are created automatically from the Designer tab; the custom events are created by me. At the top of the blueprint, within the gray box, is the array which sets the appearance of the buttons, and at the bottom within the gray box is the index that defines each of the buttons as an integer so that the buttons change with OnMouseHover as well (usually, the Mouse Hover colors are defined on the designer tab, but then it ignores the "Update Appearance" function, so I have them both updated here). In theory, I could have cleaned up this blueprint by making each button action as a separate function, which would each be listed on separate tabs, but chose not to. 

Figure 3 - Main Menu Widget blueprint
Figure 4: the function that is actually referenced on the level blueprint. It receives the gamepad navigation requests (from the level blueprint, figure 1) -- either up or down based on the nav type -- and calls additional functions based on this request. 


Figure 4 - "Receive Navigation Request" function
Figure 5: the Receive Main Down function. If the player moves the joystick down or presses down on the d-pad, the Main Navigation Index is increased by +1. There are five buttons;  the top button is 00, the second 01, etc. If the navigation index integer is less than 4, it adds +1. If the Navigation index integer is greater than 4, the index is returned to 0. That way, if the player has the bottom button selected (button 05), the selection loops back to the top (button 00). There is also a subtle "tick" sound played when the index is changed. 

Figure 5 - "Receive Main Down" function
Figure 6: the function for Receive Main Up. It is the same as the down function, but instead of adding 1, subtracts 1. If the integer is greater than 0, then -1 (it will not be any larger than 4, according to the Receive Main Down function, figure 5). If the integer is less than 0, it is changed to 4. 

Figure 6 - "Receive Main Up" function
Figure 7: the function that changes the appearance of the buttons. There is a ForEachLoop that creates an array referencing the navigation index (the integer modified in figures 5 and 6) , and sets the background of the button based on that integer (clear if false, subtle gray if true). 

Figure 7 - "Update Main Nav Appearance" function
Figure 8: the function called when the gamepad A key is pressed (as defined on the level blueprint at the beginning, figure 1). This uses the navigation index integer to choose which of the custom events are called (in red on the left of the Main Menu blueprint, figure 3).

Figure 8 - "Receive Gamepad Select" function
All of these blueprints together allow the player to interact with the main menu with either a mouse or a gamepad. Whew!

After spending eight hours adding this to the Main Menu, it was easy to add gamepad functionality to the pause menu within the game itself. The differences within the level are that I had to set the game as paused so that the Up/Down navigations did not make the player pawn move in the background. Otherwise, the gamepad could select invisible Pause menu items as the player navigated the map, since the functions for the pause menu navigation are available thoughout play. Also, the Pause Menu is actually created from a separate level that streams temporarily over the UHS_1914_building level. This sets the main game as paused, but also means the gamepad actions defining the pause menu navigation have to be set as "execute when paused", otherwise nothing happens. With Play-in-Editor (PIE), I was able to navigate from the Main Menu, to the building level, back to the main menu, and to the Gallery level -- all with a gamepad. Success! Then I clicked Save, and closed the editor. Enter: roadblock!

Here is where the roadblock appeared: I had great success with the Main Menu's gamepad functionality, but could not get the pause menu to work after saving and re-opening the level. I got the same error I received in March from my bug report: after reloading, the variables from the designer tab disappeared from the graph side of the UMG blueprint. This not only threw a compile error to the blueprint itself, but caused the project not to be able to be packaged (export-all to a stand-alone application). In addition, not only did I have the variable for the animation disappear (the error from march), but the variables that referenced the buttons also disappeared. In a few cases, trying to fix the missing variables crashed the editor. No good!

The a-ha moment came late yesterday afternoon. The question: why was the Main Menu working, but the Pause menu not working? The difference between the two situations is from where the CreateWidget is called. I had been calling the Pause menu from the FirstPersonPlayerBP (the blueprint that defines the player actions), since that is where I have successfully called the footstep sounds and the mini-map. However, the FirstPersonPlayerBP blueprint somehow negates the variables created within UMG blueprints. The Main Menu works fine because it is called not from the FirstPersonPlayerBP, but from a level blueprint for the empty Main Menu level. As soon as I switched the Pause menu to call from the level blueprint instead of the FirstPersonPlayerBP, it worked! Nearly the entire project can now be controlled both by a mouse/keyboard, and from a gamepad. 

To celebrate this success, I modeled a vault ceiling to add to the porch on the Gallery level: 



Edit July 11: with the addition of the options menu, the entire project now has full gamepad support!

No comments:

Post a Comment