2 - Lobby UI
This Part will focus on creating the User Interface (UI) for the lobby.
It's going to stay very basic as it's not really related to Networking per se.
The Play Button
Currently our lobby is automatically connecting us to a room, that was good for the early testing, but really we want to let the user choose if and when to start playing.
So we'll simply provide a Button for that.
- Open the scene
Launcher
. - Create a UI Button using the Unity menu 'GameObject/UI/Button', name that button
Play Button
Notice it created a Canvas and an EventSystem GameObject in the Scene Hierarchy, so we don't have to, nice :) - Edit the child Text value of the
Play Button
to "Play" - Select
Play Button
and locate theOn Click ()
section inside the Button Component - Click in the small '+' to add a entry
- Drag the
Launcher
GameObject from the Hierarchy into the Field - Select in the drop down menu
Launcher.Connect()
We have now connected the Button with our Launcher Script, so that when the user press that Button, it will call the method "Connect()" from our Launcher Script. - Open the Script
Launcher
. - Remove in the Start() the line where we call
Connect()
- Save the Script
Launcher
and save the Scene as well.
If you hit play now, notice you won't be connecting until you hit the Button.
The Player Name
One other important minimal requirement for typical games is to let users input their names, so that other players know who they are playing against.
We'll add a twist to this simple task, by using PlayerPrefs to remember the value of the Name so that when the user open the game again, we can recover what was the name.
It's a very handy and quite important feature to implement in many areas of your game for a great User Experience.
Let's first create the script that will manage and remember the Player's name and then create the related UI.
Creating the PlayerNameInputField
Create a new C# Script, call it
PlayerNameInputField
Here is the full content of it. Edit and save
PlayerNameInputField
script accordingly:C#
using UnityEngine; using UnityEngine.UI; using Photon.Pun; using Photon.Realtime; using System.Collections; namespace Com.MyCompany.MyGame { /// <summary> /// Player name input field. Let the user input his name, will appear above the player in the game. /// </summary> [RequireComponent(typeof(InputField))] public class PlayerNameInputField : MonoBehaviour { #region Private Constants // Store the PlayerPref Key to avoid typos const string playerNamePrefKey = "PlayerName"; #endregion #region MonoBehaviour CallBacks /// <summary> /// MonoBehaviour method called on GameObject by Unity during initialization phase. /// </summary> void Start () { string defaultName = string.Empty; InputField _inputField = this.GetComponent<InputField>(); if (_inputField!=null) { if (PlayerPrefs.HasKey(playerNamePrefKey)) { defaultName = PlayerPrefs.GetString(playerNamePrefKey); _inputField.text = defaultName; } } PhotonNetwork.NickName = defaultName; } #endregion #region Public Methods /// <summary> /// Sets the name of the player, and save it in the PlayerPrefs for future sessions. /// </summary> /// <param name="value">The name of the Player</param> public void SetPlayerName(string value) { // #Important if (string.IsNullOrEmpty(value)) { Debug.LogError("Player Name is null or empty"); return; } PhotonNetwork.NickName = value; PlayerPrefs.SetString(playerNamePrefKey,value); } #endregion } }
Let's analyze this script:
RequireComponent(typeof(InputField)):
We first make sure that this script enforce the InputField because we need this, it's a very convenient and fast way to guarantee trouble free usage of this script.PlayerPrefs.HasKey(), PlayerPrefs.GetString() and PlayerPrefs.SetString():
PlayerPrefs is a simple lookup list of paired entries (like an excel sheet with two columns), one is thekey
, one is theValue
.
The Key is a string, and is totally arbitrary, you decide how to name and you will need to stick to it throughout the development.
Because of that, it make sense to always store your PlayerPrefs Keys in one place only, a convenient way is to use a [Static| variable declaration, because it won't change over time during the game and is the same everytime. One could go all the way and declare itconst
, but this is something you'll get into as you gain more and more experience with C#, this is just teasing with C# scope of possibilities here.
So, the logic is very straight forward.
If the PlayerPrefs has a given key, we can get it and inject that value directly when we start the feature, in our case we fill up the InputField with this when we start up, and during editing, we set the PlayerPref Key with the current value of the InputField, and we are then sure it's been stored locally on the user device for later retrieval (the next time the user will open this game).
- PhotonNetwork.NickName:
This is main point of this script, setting up the name of the player over the network.
The script uses this in two places, once during Start() after having check if the name was stored in the PlayerPrefs, and inside the public methodSetPlayerName()
.
Right now, nothing is calling this method, we need to bind the InputFieldOnValueChange()
to callSetPlayerName()
so that every time the user is editing the InputField, we record it.
We could do this only when the user is pressing play, this is up to you, however this is a bit more involving script wise, so let's keep it simple for the sake of clarity.
It also means that no matter what the user will do, the input will be remembered, which is often the desired behavior.
Creating the UI for the Player's Name
- Make sure you are still in the scene
Launcher
. - Create a UI InputField using the Unity menu 'GameObject/UI/InputField', name that GameObject
Name InputField
- Set the
PosY
value within the RectTransform to35
so it sits above thePlay Button
- Locate the
PlaceHolder
child ofName InputField
and set it's Text Value to "Enter your Name..." - Select
Name InputField
GameObject - Add the
PlayerNameInputField
Script we've just created to it - Locate the
On Value Change (String)
section inside the InputField Component - Click in the small '+' to add a entry
- Drag the
PlayerNameInputField
component attached to the same GameObject into the field
- Select in the dropdown menu the
PlayerNameInputField.SetPlayerName
underDynamic String
section - Save the Scene
Now you can hit play, input your name, and stop playing, hit play again, the input that you've entered just showed up.
We are getting there, however in terms of User Experience we're missing feedback on the connection progress as well as when something goes wrong during connection and when joining rooms.
The Connection Progress
We are going to keep it simple here and hide away the name field and play button and replace it with a simple text "Connecting..." during connection, and switch it back when needed.
To do so, we are going to group the Play Button and name Field so that we simply activate and deactivate that group.
Later on more feature can be added to the group and it will not affect our logic.
- Make sure you are still in the scene
Launcher
. - Create a UI Panel using the Unity menu 'GameObject/UI/Panel', name that GameObject
Control Panel
- Delete the
Image
andCanvas Renderer
component from theControl Panel
, we dont' need any visuals for this panel, we only care about its content. - drag and drop the
Play Button
andName InputField
onto theControl Panel
- Create a UI Text using the Unity menu 'GameObject/UI/Text', name that GameObject
Progress Label
don't worry about it interfering visually, we'll activate/deactivate them accordingly at runtime. - Select the
Text
Component ofProgress Label
- Set
Alignment
to becenter align
andmiddle align
- Set
Text
value to "Connecting..." - Set
Color
to white or whatever stands out from the background. - Save the Scene
At this point, for testing, you can simply enable/disable Control Panel
and Progress Label
to see how things will look during the various connection phases.
Let's now edit the scripts to control these two GameObjects activation.
Edit the Script
Launcher
Add the two following properties within the
Public Fields
RegionC#
[Tooltip("The Ui Panel to let the user enter name, connect and play")] [SerializeField] private GameObject controlPanel; [Tooltip("The UI Label to inform the user that the connection is in progress")] [SerializeField] private GameObject progressLabel;
Add to the Start() method the following
C#
progressLabel.SetActive(false); controlPanel.SetActive(true);
Add at the beginning of
Connect()
method the followingC#
progressLabel.SetActive(true); controlPanel.SetActive(false);
Add to the beginning of
OnDisconnected()
method the followingC#
progressLabel.SetActive(false); controlPanel.SetActive(true);
Save the Script
Launcher
and wait for Unity to finish compilingMake sure you are still in the scene
Launcher
.Select the GameObject
Launcher
in the HierarchyDrag and Drop from the Hierarchy
Control Panel
andProgress Label
to their respective Field within theLauncher
ComponentSave the Scene
Now, if you play the scene, you'll be presented with just the Control Panel, visible and as soon as you click Play, the Progres Label will be shown.
For now, we're good for the lobby part.
In order to further add features to the lobby, we need to switch to the Game itself, and create the various scenes so that we can finally load the right level when we join a room. We'll do that in the next sections and after that, we'll finally finish off the lobby system.