Open up your scene, for this guide we will use Mirror/Examples/Tanks
You should be familiar with the examples, if not, have a quick play, build and run, join yourself by having two games on same PC connecting via 'localhost'.
Part 2:
Create a script called SceneScript, due to this guide modifying Mirrors Example scenes, make sure it is in same folder as Tank script.
Add this script onto a game object in the scene, also named SceneScript, this will be our canvas to player link as well as our 'scene manager', then attach a NetworkIdentity component, like below.
Part 3:
Open up this SceneScript.cs, and add the following code, parts will be commented to explain :)
SceneScript.cs
usingUnityEngine;usingUnityEngine.UI;usingMirror;// For your game, no need to use this namespace, its just for tank example modification :)namespaceMirror.Examples.Tanks{publicclassSceneScript:NetworkBehaviour { [SyncVar(hook =nameof(OnReadyChanged))]publicint readyStatus =0;voidOnReadyChanged(int _Old,int _New) { //this hook is fired when readyStatus is changed via client cmd or directly by host // you dont need to use _New, as the variable readyStatus is changed before hook is calledif (readyStatus ==1) {statusText.text="Server Says Go!";buttonReady.gameObject.SetActive(false); } // updating our canvas UISetupScene(); }publicTank playerScript; // this is set by local player, so UI has reference to your player onlypublicText statusText;publicButton buttonReady, buttonDeath, buttonRespawn;privatevoidStart() { //Make sure to attach these Buttons in the InspectorbuttonReady.onClick.AddListener(ButtonReady); //you could choose to fully hide the server only buttons from clients, but for this guide we will show them to have less code involved
buttonDeath.onClick.AddListener(ButtonDeath);buttonRespawn.onClick.AddListener(ButtonRespawn); } //[ServerCallback]publicvoidButtonReady() { // you can use the [ServerCallback] tag if server is only ever going to use the function, or do a check inside for if( isServer ) { }
if (isServer) { // optional checks to wait for full team, you can add this in connection joining callbacks, or via UI, upto you.
//if (NetworkServer.connections.Count > 2) //{ readyStatus =1; //} //else //{ // playerStatusText.text = "Not enough players."; //} }else {statusText.text="Server only feature"; } } // For faster prototyping, we will have these as buttons, but eventually they will be in your raycast, or trigger code
publicvoidButtonDeath() {playerScript.CmdPlayerStatus(true); }publicvoidButtonRespawn() {playerScript.CmdPlayerStatus(false); }publicvoidSetupScene() {if (isServer ==false) {buttonReady.interactable=false; }if (readyStatus ==0) {buttonRespawn.interactable=false;buttonDeath.interactable=false; }elseif (playerScript) { // quick check to make sure playerScript is set before checking its variables to prevent errorsif (playerScript.isDead==true) {buttonRespawn.interactable=true;buttonDeath.interactable=false; }elseif (playerScript.isDead==false) {buttonRespawn.interactable=false;buttonDeath.interactable=true; } } } }}
Part 4:
Now open Tank.cs, this is the guides equivalent to your eventual PlayerScript.
And in the Update function, below the isLocalPlayerCheck, add:
if (sceneScript.readyStatus != 1) return;
This will stop the tanks below code (movement and projectile firing) from running until ready.
Add the code at the bottom of Tank.cs, after the “RpcOnFire” ends.
(But still inside the namespace { } )
Tank
publicGameObject[] objectsToHide;privateSceneScript sceneScript;[SyncVar(hook =nameof(OnChanged))]publicbool isDead =false;voidOnChanged(bool _Old,bool _New){if (isDead ==false) // respawn { // allow user to kill themselves via button, just for prototypingforeach (var obj in objectsToHide) {obj.SetActive(true); }if (isLocalPlayer) { // Uses NetworkStartPosition feature, optional. this.transform.position = NetworkManager.startPositions[Random.Range(0, NetworkManager.startPositions.Count)].position;
}sceneScript.statusText.text="Player Respawned"; }elseif (isDead ==true) // death { // have meshes hidden, disable movement and show respawn buttonforeach (var obj in objectsToHide) {obj.SetActive(false); }sceneScript.statusText.text="Player Defeated"; }if (isLocalPlayer) {sceneScript.SetupScene(); }}voidAwake(){ //allow all players to run this, maybe they will need references to it in the future sceneScript =GameObject.FindObjectOfType<SceneScript>();}publicoverridevoidOnStartLocalPlayer(){ // local player sets reference to scene scripts variable, so they can communicate with each other // you could also use regular Start() and if( isLocalPlayer ) { } instead of OnStartLocalPlayer()sceneScript.playerScript=this;sceneScript.SetupScene();}[Command]publicvoidCmdPlayerStatus(bool _value){ // player info sent to server, then server changes sync var which updates, causing hooks to fire isDead = _value;}
Part 5:
For this method during death and respawn, rather than destroying the player, we will hide and reshow it.
Open up the Tank player prefab, and add the two child objects to the “Objects To Hide” array, “ProjectileMount” is just an empty position marker, and does not need to be added.
Upon the sync var change of “isDead”, the hook callback will cycle through this array.
Part 6:
Create a Canvas in the scene, by either right clicking, UI canvas, or the menu at top, GameObject, UI, Canvas.
Set the canvas scaler to “Scale with Screen Size”, this will help with keeping everything same size, on both low and high resolution screens, and is best to set before adding Canvas contents.
Create your Unity Canvas UI, with a text, and 3 buttons, label them for easier reference.
Here I have placed Status text at top, Server Set Ready button in the middle, and the Die and Respawn buttons to the right.
After this set the variables to the correct UI on your SceneScript, like below.
Part 7:
Build and Run!
Enjoy :)
(To help with side by side play testing - Project settings, Player, Resolution and Presentation, Fullscreen Mode to “Windowed”.)