Video Game Design 6 | Game Menus and Collectables

Series: Video Game Design

Video Game Design 6 | Game Menus and Collectables

  1. Game Menu and Collectables

(1) Set up Game Menu System

  • Duplicate the current Demo scene and rename it GameMenuScene
  • Select and move the Main Camera for a look-down view of some interesting parts of your scene. Then disable the Third Person Camera (script) of the Main Camera object in its inspector.

(2) Post Processing

  • Then, you will need to go to Window > Package Manager. Choose Packages: Unity Registry, and then search for Post Processing. Then click on the Install button to install the Post Processing package. You can also view its document at this link.
  • The quickest approach is to add a Post Process Layer and Post Process Volume to your Camera by selecting the Main Camera object, and then choose Add Component. Search and choose to add Post Process Layer and Post Process Volume.
  • Set the Inspector > Post Process Layer > Volume Blending: Trigger to the Camera’s transform (Main Camera Transform).
  • Add a new layer PostProcessing or another user-defined layer of your choosing because we are going to use it later.
  • Change the layer of your Main Camera to a user-defined layer PostProcessing
  • Set the Post Process Layer > Volume Blending: Layer to the same layer (e.g. PostProcessing).
  • In the Post Process Volume component, create a profile by selecting New and make sure it is assigned to the same component.
  • Lastly, in the new profile enable your desired effects. Depth of Field can be used for a blurry effect. Grain might also work (and is good for confirming that post-processing is working), possibly stacked with other effects.
  • You will probably need to enable Is Global in the Post Processing Volume to get the effect to work.
  • If you look into the settings of Grain and Depth of Field, there is also something to notice. First, the switch of On/Off for the profile is opposite to its meaning and setting them to Off actually turn the effects on. Second, you can click on All or None to open or close all the settings for that effect. And third, you can also change the default values of the effect to create the performance you want. It is easier to view the changes under the Game tag rather than the Scene tag.
  • On OSX, if you get errors from shaders then you might enable Metal Support for rendering. Go to Edit > Project Settings > Player: Other Settings > Metal Editor Support and enable.

(3) GUI Creation

  • Add a GameObject > UI > Canvas to your scene (confirm Render Mode is Screen Space — Overlay)
  • Next, add a Hierarchy > GameObject > UI > Panel to your Canvas.
  • In the Scene window, switch to 2D mode so you can see it in the correct perspective.
  • Note that if your Panel’s Rect Transform Anchors are at the maximum extents of the Canvas (default) then your Panel sizing will scale with Canvas resolution and aspect. If you drag the circular blue control points you will manipulate pixel offsets (Left, Right, Top, Bottom) from the Rect Transform Anchors. This will create a sort of hybrid situation where the panel will partially scale with Canvas resolution but there are absolute pixel count offsets from the Anchors. Often, this is not what you want. Typically, a designer wants to scale with resolution or have the same pixel size always. If you want to scale with the resolution, the Rect Transform’s Left, Right, Top, Bottom should stay at 0 offset.
  • If you want to scale with the resolution, set the Rect Transform’s Min and Max Anchors to the proportional offset within the Canvas. If you want your Panel to always be the same size, shrink your Anchors down to the same X, Y position (say the center of the canvas or a corner) then your panel will maintain the configured pixel size and position offset from the Anchors. You can use the following values to justify your panel.
  • To aid with GUI design, try switching the Game view from Free Aspect to a common full-screen aspect ratio. This will help you visualize positioning.
  • Now add a GameObject > UI > Button to your Panel. Note Unity’s default Rect Transform for the button versus the Panel. Change the default text to Start Game.
  • Now create a script called GameStarter and add a public method StartGame(). This method should simply call SceneManager.LoadScene (“SCENE_NAME”) where SCENE_NAME is the name of your original interactive scene. This command simply loads the scene during runtime. Add this script to your Button.
public void StartGame()
{
SceneManager.LoadScene("SCENE_NAME");
}
  • Next up, view the Button in the Inspector. Under the Button script settings, you should see an OnClick() event. Assign the GameObject callee slot to the same Button that the script belongs to. Now use the dropdown to find GameStarter > StartGame and pick it. Now when you click the Start Game button at runtime the level will load.
  • Make another button called Exit. Make use of the existing script called GameQuitter, which is very similar to GameStarter but the command executed is Application.Quit() and is contained within the QuitGame() method of the GameQuitter script. Wire up QuitGame() to the Exit button. Note that the existing GameQuitter immediately calls QuitGame() if the hotkey x is pressed.
  • Go ahead and test or debug your menu buttons. Try testing with a build a well. Note that your build must include both the GameMenuScene and your actual gameplay scene to work correctly. Note that the GameMenuScene must be built in 0 priority so that we can call this scene at the beginning.

(4) Setting Up the In-Game Menu

  • Save your GameMenuScene and reopen your original scene demo with the controllable characters.
  • Leveraging what you learned previously, add a screen space overlay Canvas with a central panel that is smaller than the overall screen resolution. On the panel, create buttons with text Restart Level and Quit Game. You can reuse the GameStarter and GameQuitter scripts from above to wire up your Restart Level and Quit Game buttons.
  • Next up, let’s add the ability for the menu to be opened/closed when hitting the Escape (ESC) key on the keyboard. First, add a Canvas Group component to your Canvas. The Canvas Group allows you to control all child UI components and their descendants in a coordinated fashion.
  • In the Inspector view of the Canvas Group component, set Alpha to 0, Interactable to false, and Block Raycasts to false. This will completely hide and disable your menu.
  • Now, you’ll make a script that responds to the Escape key to toggle those settings on/off. Create a new script called PauseMenuToggle. This script should be attached to the Canvas.
  • Now, let’s work on the script. The script will be acting on the Canvas Group so add a component requirement to the class. You can refer to the script in this link if you don’t know where to add it.
[RequireComponent(typeof(CanvasGroup))]
  • Use GetComponent<CanvasGroup>() to grab a reference in Awake() and store in a private member variable named canvasGroup. You should print a Debug.LogError() if GetComponent() doesn’t find the component you are looking for. You can refer to this link for more information.
  • In Update() of the PauseMenuToggle script, add the following,
void Update()
{
if (Input.GetKeyUp(KeyCode.Escape))
{
if (canvasGroup.interactable)
{
canvasGroup.interactable = false;
canvasGroup.blocksRaycasts = false;
canvasGroup.alpha = 0f;
}
else
{
canvasGroup.interactable = true;
canvasGroup.blocksRaycasts = true;
canvasGroup.alpha = 1f;
}
}
}
  • One problem you may notice is that your game is still running in the background of the menu. That may be ok for a multi-player game, but for a single player, you should pause your game. Go back to PauseMenuToggle and add Time.timeScale = 0f for when the in-game menu is visible and Time.timeScale = 1f for when the menu is off. Setting Time.timeScale is a simple way to pause your game. It’s not always the complete solution for pausing, but it works for most simple games.
  • Be aware that Time.timeScale is preserved after loading a new level. So, if you paused in one scene then call SceneManager.LoadScene(), you will find the newly loaded scene to still be paused. To avoid this, you should set the timescale back to 1f immediately following the call to LoadScene().
SceneManager.LoadScene("demo");
Time.timeScale = 1f;

(5) Trigger-Based Collectables

  • Now, we will make some collectable items and we will create a simple one that SomeDude_RootMotion can pick up.
  • First, make a GameObject that is a sphere. Place it just above the ground and away from anything moving. Give it a pink color. Change the SphereCollider component to set IsTrigger to true (checked). This change will make the collider not cause collisions with Rigidbody, but will generate OnTriggerXXX() callbacks.
  • Note that it is common to make the size of the collider much bigger than the graphical object. For trigger-based collectables, that means the player doesn’t need to get as close to initiate the pickup. Also, trigger colliders don’t need a graphical representation at all. Invisible trigger zones are useful for detecting that a player has fallen to their death, etc. We can make it by setting a larger collider Radius for the sphere.
  • You will now implement a script called CollectableBall that will implement OnTriggerEnter(). In this script, delete Start() and Update() and add,
void OnTriggerEnter(Collider c) 
{
}
  • In the body of this method, call,
EventManager.TriggerEvent<BombBounceEvent, Vector3>(c.transform.position);
Destroy(this.gameObject);

This will generate an event that results in a sound played and will also delete the ball GameObject. (Note that the use of BombBounceEvent is just a placeholder. Ideally, you would create a new event for collection and add a new sound to the AudioEventManager)

  • Try out the collectable by playing the game. This is close to being a collectable, but we never updated the character to know that the ball was collected. In order to support collection, you will make a script that can be attached to characters that the ball collectable will act upon. So we have to create a new script BallCollector. Then, add a public member variable,
public bool hasBall = false;

Also, add

public void ReceiveBall() 
{
hasBall =true;
}

Then save and attach the script to SomeDude_RootMotion.

  • Next up, modify the CollectableBall script to only respond to triggering GameObjects that contain the BallCollector script. In OnTriggerEnter(), firstly check if c.attachedRigidbody is not null. If that is the case, attempt to grab a reference to a BallCollector component like so,
BallCollector bc = c.attachedRigidbody.gameObject.GetComponent<BallCollector>();
  • Only if bc is not NULL (this means the current character is a ball collector) and bc.hasBall equal false (this means the current character has no ball) we can pick the ball up. Confirming that only SomeDude_RootMotion can collect the ball and that the hasBall bool switches to true.
  • Now let’s test it out. Notice that any character without a BallCollector component should not be able to collect the ball.

(6) Trigger-Based Animated Object

Your last task is to create a trigger-based prefab game object that plays a Mecanim animation if the player gets close enough. You should select a game object concept that you think will be useful in completing your team project. Mecanim should be used for animation support. You should place the animatable geometry under a root empty game object. This will allow you to animate coordinates relative to where the prefab is placed. You can refer to this link for more information. Here are some requirements,

  • Object is prefabbed
  • Object animates via Mecanim in a compelling way when the player gets close enough
  • Object resets when the player is far enough away (if obj is in the triggered state)

2. Submission

(1) Update the HUD

Update the HUD in both scenes to show your name. Edit the Name game object under the “FramerateCounter — NAME TEXT IS HERE”.

(2) Modification Check List

  • [25 pt] Working Game Start Menu with the dedicated scene
  • [25 pt] Working In-Game Menu in original gameplay scene
  • [20 pt] Collectable ball that only SomeDude_RootMotion can collect
  • [30 pt] Trigger-based animated prefab object placed in three (3) locations in the scene
  • [Extra 5 pt] Make SomeDude_RootMotion throw the collected ball

(3) Game Building

  • Edit > Project Settings > Player > Product Name: Last_FirstInit_m3
  • File > Save to save the file
  • Select File > Build Settings
  • Choose MacOS, Intel 64-bit + Apple Silicon
  • Create a new folder named Last_FirstInit_m3 (replace with your name Info). This is the root folder.
  • Create a path <root>/Build/OSX . Name the build Last_FirstInit_m3 and save it here.
  • Select File > Build Settings again
  • Choose Windows, x86_64
  • Build it in the path <root>/Build and name it Windows .

(4) Buttons Not Work After Built

This is a problem with the Auditor canvas. Try disabling it once you have your packaging sorted out. Or set the Canvas sorting layer for both canvas components.

(5) Create UNTESTED file

Because one of the builds (.app or .exe) has not been tested, we should add a file UNTESTED to the folder, if we don’t test it.

$ touch UNTESTED

(6) Clean Up the Root

We have to clean the root before we submit.

$ rm -rf .git
$ rm -rf Library
$ rm -rf temp
$ rm -rf Obj
$ rm -rf Logs
$ rm *.sln
$ rm *.csproj