Monday 14 December 2015

How I handled Conditional Statements and Events (like Menus and Player Input) in my game

Today's blog post will cover examples of:

-Conditional Statements
-Events
-Menus
-And Player Input

In my Unity game.

To get started: I've used various conditional statements (using if commands), to make decisions in my C# code, which can be decided upon by a player's input. This is to help the game decide when every piece of code should be applied, as if the game just applied code at the start and not when appropriate conditions are met most games would not function very well at all (since the player would activate every possible command for every scenario at once). For example, here's an extract of my code that uses if statements for some of the game's physics, to decide if the player should flip left, right, or jump, and then it calls to the respective lines of code elsewhere in the script to follow through with the command:


If statements:

if (Input.GetAxis ("Horizontal") < -0.1f)
{
transform.localScale = new Vector3 (-1, 1, 1); //Flips player horizontally for when you're moving left, Vector 3 represents 3d movement and space using the X, Y and Z axis
}

if (Input.GetAxis ("Horizontal") > 0.1f)
{
transform.localScale = new Vector3 (1, 1, 1); //For when you're moving right
}

if (Input.GetButtonDown ("Jump")) //If you press space, the bird will jump! Check if grounded before you can jump again.
{

Essentially the code breaks down IF any of these three situations should happening, depending on the situation. So for example, the last few lines handle the code where if the player presses the 'Jump' key on their keyboard (set in Unity as Spacebar for when the player presses down the key as input), the game should execute the jump command (where the code to execute the command is positioned elsewhere in the Player script as a reference). Next, if the player moves either left or right along the horizontal X axis (and it classes moving as going at a speed past 0.1, which only occurs when the player presses left or right on their keyboard, which in turn adds a force to the player's rigid body using the code of rb2d.AddForce with a speed set by the speed and maxSpeed variables), then the code should flip the bird character's sprite to match the direction they should be facing in reality when moving that way. This works because the 1, 1, 1 used in the code stand for X, Y and Z size of the main character. So having -1 as the first number turns the bird's scale in the opposite direction until changed again).

I set it up like this because it streamlines the C# code to the player's three main attributes, and it means as the main player script continues to expand it will be easier to locate where everything is in the code and not lose track of everything. And also it's necessary to flip the bird in the direction it's facing when moving, as it would break the player's immersion with the game otherwise to have it walking backwards every time the player wants to move left! The game very much has a focus on horizontal movement, since the main character is a bird they can jump over huge distances when moving left or right along the x axis, however he has a purposely limited jump height on the y axis, to enforce the fact that the bird can't fly very well and has difficulty getting up to high places. Here's an image of the bird facing both directions in-game, and also jumping in the air (with different animations for idle, walking and jumping using Unity's built-in animator), showing the working code in action:
Facing right

Facing left

Jumping

Another example of player input resulting in an event in the game is the Door game object. This is the primary win condition for the game: reach the door at the end of each level, which takes you to the next level, until you finish the game and get the Bird back home. Essentially in-game when the player gets close to it it will display a message on the screen saying how to enter (pressing the 'E' key), and when the player does it will load the next level of the game. If the player leaves the trigger area it instead prints text onto the screen that breaks the 4th wall and reads: 'What? where are you going!?', which adds to the charm of the game with a light-hearted nature, and isn't intrusive to the player as it appears when they reach the end of the stage anyway with nowhere else to go (how the text is actually drawn on the screen will be talked about in the score blog post as it follows a similar procedure). Here is a portion of the code that the door uses to get the player's input, and two images showing it in action:


Door object script sample:

' void OnTriggerEnter2D(Collider2D col)
{

if (col.gameObject.tag == "Player") //If the player game object (tagged 'Player') collides with the Door, print the text onto the screen. And if they press E while inside the triggerbox, load the next level of the game

{
gm.inputText.text = ("Press (E) to Enter!"); //Load up the text continuously on the screen until stated otherwise
{
if (Input.GetKeyDown ("e"))
{

Application.LoadLevel("Scene2");

}
}
}'


Before the Bird hits the Door
After the Bird its the Door

The code essentially reads where when the Bird collides with the Door's trigger box (and it will only respond to the Bird, as it is tagged with 'Player' in the Unity Inspector, as we don't want enemy projectiles hitting the Door and automatically loading the next level!), if they press the E key, then load up the next level in the game. This is specified in the brackets (so in this case it's 'Scene2') and allows me to separate levels into different Unity Scenes, and not have everything in the game placed in one scene and have the whole viewport get very cluttered. Plus it's also just more efficient and easier to load up a new level, as you don't have to manually find out the in-editor coordinates for each starting location this way!


Next up for in-game events is making my game draw a HUD (heads-up display) onto the screen. A good example of this is my game's pause menu, where when the player presses the escape key (set with Unity's custom keys under Edit - Project Settings - Input), the whole game freezes and a menu game object appears until the player clicks a button on-screen, or presses the escape key to close it. The code is placed in a separate PauseMenu script (following professional working practises by separating out code and naming things correctly), and this script is attached to the game's camera object and is designed to pop-up over the whole screen by following the camera (otherwise when the player pauses the game, the game would freeze, but the pause menu would only appear where I placed it and not in front of the player at all times). Here's a large extract of the code for the pause menu:


Pause menu script sample:

"void Start(){ //Gets called in at the start of the game

PauseUI.SetActive (false); //Means that the pause screen doesn't load the moment the game begins, it gets triggered later on

}

void Update(){ //Update function

if (Input.GetButtonDown ("Pause")) { //If the pause button is pressed by the player, bring up the Pause UI. The pause button is set to the Esc key, under Unity's Edit - Project Settings - Input tab

paused = !paused; //Paused is the opposite of pause (variable), as it's toggling my boolean that is by default false (or off). So if it's false, then the variable will be true.

}

if (paused) { //If you pause the game

PauseUI.SetActive(true); //If you pause the game by pressing the button, bring up the pause menu UI
Time.timeScale = 0; //Set the game time to 0 if paused, which freezes everything and stops the game from running which allows the player to click on a button

}

if (!paused) { //If the game isn't paused

PauseUI.SetActive(false); //If the game isn't paused
Time.timeScale = 1; //Set the time back to normal running time and continue playing the game. 1 is full speed and 0 is stopped, so any o.x speed between these values will actually slow the game down instead!

}"

Essentially the game loads with the pause menu set to false and not appear on screen (I did this on purpose, as otherwise the game would always load with the menu on the screen and render the game unplayable! Since you wouldn't be able to see anything!). Afterwards whenever the player presses the 'Esc' key it will change the pause menu to be true, and not false. Since the pause menu UI is constantly following the camera and is just invisible, being set to true makes it appear to the user. However this isn't just how it works, I also wrote the code to see if the game is pause, set the game's time scale (essentially the running speed), to 0, which freezes the entire game. This is on purpose, as if the player freezes the game and it keeps running, it's normally to take a break, so if the game kept running they could easily be killed by an enemy or fall to their death if they pause mid-jump. Afterwards the code is copied using an if statement and set in reverse, to check and see if the game isn't paused, and to set the game speed to 1 again (fullspeed) and continue playing.

There are various other quality of life effects I added to the pause menu to make it more interesting and convenient, like adding a dark-but transparent (to still see the game in the background) canvas behind the pause menu buttons to make the player's options stand out more from the backdrop, and also have a form of physical feedback to the player so they know that the game has truly stopped (which is a professional industry standard technique commonly used in pause menus for platformer games like Super Mario Galaxy, and has become a universally used trope in UI design). Also to add to my game's charm I added some more images of my bird that I created in the Digital Graphics assignment, since the bird's design is to change appearance and colour depending on how it's feeling I added a cute sprite of the bird thinking (representing the player choosing a menu option), and a picture of the bird sleeping (to represent the player taking a break). Here are a few more screenshots, this time of the pause menu in action as it freezes the player mid-jump to show time stopping:

The game unpaused

The game paused, note the darker background behind it
As you can see from the screenshots the menu has four options: Continue, Restart, Main Menu and Quit. These all act as you'd expect, where continue sets the pause menu to false and sets time to 1, restart loads the level that's already loaded into memory, main menu loads a separate Unity scene that can act as a main menu and the default scene that Unity loads at the start of the game, and quit simply terminates the program. There are various options here to suit the player's needs, and were added to make the game fit a more professional standard and include all the various features you'd expect a pause menu to include in this day and age. The code for each button is shown below:



Pause menu button functions:

//Button functions for all four of them
public void Continue(){ //All the buttons can access these functions, so it must be a public variable

paused = false; //If the continue button is clicked, unpause the game (ensure camera object is added to the 'On Click' section of all the buttons, as it can draw up a list of all the custom actions I've made for each individual button

}

public void Restart(){

Application.LoadLevel(Application.loadedLevel); //Restarts by loading the same level again. The code says that Unity should load a level, and the level it should load is the one that is already loaded

}

public void MainMenu(){

Application.LoadLevel(0); //Loads the scene with an index number of 0, which is the scene of the main menu so the player can quit back it it

}

public void Quit(){

Application.Quit (); //Literally just closes the game when the button is pressed

}

I also have a main menu in the game, which has a dedicated Unity scene just for it, and this menu is the first thing that opens when the player loads up the game. It's designed to be visually pleasing, with a logo (made by me in Photoshop, and the game is now titled 'Pippy The Bird!') that animates to grow and shrink slowly. It also has a scrolling background of the desert the Bird will be travelling through for a while to get home (the effect is made by duplicating the background image, setting it as a repeatable texture, applying it to a quad game object as a texture and adding code to it by offsetting the vector 2 horizontally at a speed of 0.3). The menu also has two transparent silhouettes of the Bird's expressions as it's trying to get home on both sides of the screen. The main menu also has four buttons with separate functions in a script (listed below), in order to add a lot of choice for the player before they start the game. These buttons are: starting the game, loading the test level (both of these use similar lines of code as the pause menu since they work the same way), how to play (which toggles a canvas box to appear and vanish again, with text inside it explaining the rules of the game, this helps give the player an idea of how to play before the game even begins) and lastly a quit button (this also uses the same code as the pause menu, which quits the game when clicked). Here is the C# code used in the main menu's script:


Main menu variables and functions:

'{
//Variables to reference the canvas game object (it is assigned to _Main) with text assigned to it and default it's open state to being false (so not visible), so when the player clicks 'how to player' the text box will appear
public Canvas PanelInfo;

public bool infoOpen = false;

// Use this for initialization
void Start () {

infoOpen = false; //When the game loads, hide the text box that lists how to play the game and disable it
PanelInfo.enabled = false;

}

//Functions for each button on the menu

public void StartGame(){

Application.LoadLevel(1); //Loads the scene with an index number of 1, which in this case is the game's first level value as listed in the Unity build settings tab in order

}

public void TestLevel(){

Application.LoadLevel(4); //Same as start game, only loads the test level instead which exhibits all of the functionallity I've implimented into the game in a small area for testing purposes. Takes you to the first level of the game once you 'beat' it

}

public void InfoPanel()
{

infoOpen = !infoOpen; //Code used to switch the info panel (the how to play button's function) on and off everytime it's clicked. If it's enabled, open up the window
PanelInfo.enabled = infoOpen;




}


public void Quit(){

Application.Quit (); //Literally just closes the game and Unity application when the button is pressed

}
}'


The main menu as it appears when the game loads, with no help box.

The main menu as it appears when the 'how to play' button is toggled on. Also note the repositioned logo and background as they cycle through their animations.


As briefly mentioned earlier, throughout testing my code underwent a few changes here and there. For one my pause menu didn't even stop the game at first and didn't darken the background either, so both were later added for both functionality and quality of life purposes. Also the text on the pause menu buttons was pretty pixelated at first, and it turns out it's because the font size was small but being stretched out onto the button. To remedy this I made the font size huge (140), but then set the horizontal and vertical overflow to overflow (to align the text over the area of the box), and shrunk it down with the Unity scale tool to fit. This means that the text won't look blurry even on high-resolution PC monitors in fullscreen, due to the high DPI of the writing.

Researching beforehand also helped tremendously when programming this, my conditional statements post for instance helped when I was typing out my if and else statements since it listed out clearly the different types of them and their purpose (else isn't shown in my example, but is used for the double jump code where if the player is grounded, they can double jump and it resets the code, but if they are not (or else) and are in the air after double jumping, they cannot use it again until touching the ground). Since I researched that conditional statements can branch out code into different sections, I used it in my own C# scripting to space out the information into digestible chunks for both reading and optimisation purposes.

No comments:

Post a Comment