Mirror/doc/Articles/Guides/CommunityGuides/MirrorQuickStartGuide/index.md

440 lines
11 KiB
Markdown
Raw Normal View History

# Mirror Quick Start Guide
Written by [StephenAllenGames.co.uk](http://stephenallengames.co.uk/) <br />
Edited by [James Frowen](https://github.com/James-Frowen)
***
This guide currently shows you:
- [Basic scene setup](##part-1)
- [Player movement](##part-4)
- [Names and colours](##part-8)
- [Scene script with canvas buttons](##part-11)
- [Weapon switching](##part-12))
It is best to first make a mini practice game before converting your single player game, or creating your ideal brand new multiplayer.
The Pre-made Mirror examples are great for using as reference, it is recommend to use them regarding connection setup, with ports and firewalls. This can be a huge topic that changes from person to person, and is not covered in this guide, here we will use localHost (multiple games on same PC).
## Part 1
Blank Project, import Mirror from [Asset Store](https://assetstore.unity.com/packages/tools/network/mirror-129321).
## Part 2
- Create new scene, save it, and add it to build settings
- Create a new GameObject, name it NetworkManager in the scene, and add these 3 components
- NetworkManager
- TelepathyTransport
- NetworkManagerHUD
- On the NetworkManager component, drag your Offline and Online scene into the slots, we have only one scene
for now, so put your scene in both
- The scene must be in the build settings before dragging it to the field
![](./image--000.jpg)
![](./image--001.jpg)
## Part 3
Setup the scene
- Add a simple Plane floor with:
- positions (0, -1, 0)
- scale (2, 2, 2)
- (optional) add a material to this, I added one called dirt that is used one of mirrors examples
- Next we add a GameObject, name does not matter
- Add `NetworkStartPosition` component to this GameObject
- Duplicate the GameObject a few times, and scatter around your scene floor so that you have multiple spawn points. I did 4, one near each corner
![](./image--002.jpg)
## Part 4
Creating the player
- Create a capsule using the menus as shown in the image
- Attached a NetworkTransform component, this will auto add a Network Identity
- Tick Client Authority on the NetworkTransform
![](./image--003.jpg)
- Rename that object Player
- Add an empty PlayerScript
- Drag into Project to create a prefab
- Then delete Player from scene
![](./image--004.jpg)
- Drag your player prefab into Network manager,
- Set spawn method to Round Robin.
![](./image--005.jpg)
## Part 5
Add the following to your PlayerScript.
```cs
using Mirror;
using UnityEngine;
namespace QuickStart
{
public class PlayerScript : NetworkBehaviour
{
public override void OnStartLocalPlayer()
{
Camera.main.transform.SetParent(transform);
Camera.main.transform.localPosition = new Vector3(0, 0, 0);
}
void Update()
{
if (!isLocalPlayer) { return; }
float moveX = Input.GetAxis("Horizontal") * Time.deltaTime * 110.0f;
float moveZ = Input.GetAxis("Vertical") * Time.deltaTime * 4f;
transform.Rotate(0, moveX, 0);
transform.Translate(0, 0, moveZ);
}
}
}
```
## Part 6
Press play in Unity editor, and then Host (server + client) button in the game window. You should be able to move around with a first person view capsule.
![](./image--006.jpg)
## Part 7
Build and run your scene, open it, host on one, and press the Client button on the other. Congrats you made a mini multiplayer game!
![](./image--007.jpg)
## Part 8
Player name above heads
- Inside your player Prefab, create an empty GameObject
- name it something like `FloatingInfo`
- position Y to 1.5
- scale X to -1
- Inside that `FloatingInfo`, create a 3D text using Unity menu (GameObject - 3D Object - 3D Text),
- Set it up as shown in the picture below
![](./image--008.jpg)
## Part 9
Update your PlayerScript.cs with this:
```cs
using Mirror;
using UnityEngine;
namespace QuickStart
{
public class PlayerScript : NetworkBehaviour
{
public TextMesh playerNameText;
public GameObject floatingInfo;
private Material playerMaterialClone;
[SyncVar(hook = nameof(OnNameChanged))]
public string playerName;
[SyncVar(hook = nameof(OnColorChanged))]
public Color playerColor = Color.white;
void OnNameChanged(string _Old, string _New)
{
playerNameText.text = playerName;
}
void OnColorChanged(Color _Old, Color _New)
{
playerNameText.color = _New;
playerMaterialClone = new Material(GetComponent<Renderer>().material);
playerMaterialClone.color = _New;
GetComponent<Renderer>().material = playerMaterialClone;
}
public override void OnStartLocalPlayer()
{
Camera.main.transform.SetParent(transform);
Camera.main.transform.localPosition = new Vector3(0, 0, 0);
floatingInfo.transform.localPosition = new Vector3(0, -0.3f, 0.6f);
floatingInfo.transform.localScale = new Vector3(0.1f, 0.1f, 0.1f);
string name = "Player" + Random.Range(100, 999);
Color color = new Color(Random.Range(0f, 1f), Random.Range(0f, 1f), Random.Range(0f, 1f))
CmdSetupPlayer(name, color);
}
[Command]
public void CmdSetupPlayer(string _name, Color _col)
{
// player info sent to server, then server updates sync vars which handles it on all clients
playerName = _name;
playerColor = _col;
}
void Update()
{
if (!isLocalPlayer)
{
// make non-local players run this
floatingInfo.transform.LookAt(Camera.main.transform);
return;
}
float moveX = Input.GetAxis("Horizontal") * Time.deltaTime * 110.0f;
float moveZ = Input.GetAxis("Vertical") * Time.deltaTime * 4f;
transform.Rotate(0, moveX, 0);
transform.Translate(0, 0, moveZ);
}
}
}
```
## Part 10
Add the `PlayerNameText` and `FloatingInfo` objects into the script on the player prefab, as shown below.
![](./image--009.jpg)
Now if you build and run, host on one, join on the other, you will see player names and colors synced across the network!
Well done, 5 stars to you!
![](./image--010.jpg)
## Part 11
A scene networked object all can access and adjust.
Create a SceneScript.cs, add it onto an empty GameObject in the scene called SceneScript.
Then create a Canvas with text and button, similar to below.
![](./image--011.jpg)
Add the sceneScript variable, Awake function, and CmdSendPlayerMessage to PlayerScript.cs
Also add the new playerName joined line to CmdSetupPlayer();
```cs
private SceneScript sceneScript;
void Awake()
{
//allow all players to run this
sceneScript = GameObject.FindObjectOfType<SceneScript>();
}
[Command]
public void CmdSendPlayerMessage()
{
if (sceneScript)
{
sceneScript.statusText = $"{playerName} says hello {Random.Range(10, 99)}";
}
}
[Command]
public void CmdSetupPlayer(string _name, Color _col)
{
//player info sent to server, then server updates sync vars which handles it on all clients
playerName = _name;
playerColor = _col;
sceneScript.statusText = $"{playerName} joined.";
}
public override void OnStartLocalPlayer()
{
sceneScript.playerScript = this;
//. . . . ^ new line to add here
```
Add this code to SceneScript.cs
```cs
using Mirror;
using UnityEngine;
using UnityEngine.UI;
namespace QuickStart
{
public class SceneScript : NetworkBehaviour
{
public Text canvasStatusText;
public PlayerScript playerScript;
[SyncVar(hook = nameof(OnStatusTextChanged))]
public string statusText;
void OnStatusTextChanged(string _Old, string _New)
{
//called from sync var hook, to update info on screen for all players
canvasStatusText.text = statusText;
}
public void ButtonSendMessage()
{
if (playerScript != null)
{
playerScript.CmdSendPlayerMessage();
}
}
}
}
```
- Attach the ButtonSendMessage function to your Canvas Button.
- Attach Canvas Scene Text to SceneScript variable.
- ignore SceneScripts, playerScript variable, it automatically sets this!
![](./image--012.jpg)
![](./image--013.jpg)
Now if you build and run, host and join, you can send messages, and have a text log for actions!
Wahooo!
![](./image--014.jpg)
![](./image--015.jpg)
Experiment and adjust, have fun!
![](./image--016.jpg)
## Part 12
Weapon switching! The code bits.
Add the following to your PlayerScript.cs
```cs
private int selectedWeaponLocal = 1;
public GameObject[] weaponArray;
[SyncVar(hook = nameof(OnWeaponChanged))]
public int activeWeaponSynced;
void OnWeaponChanged(int _Old, int _New)
{
// disable old weapon
// in range and not null
if (0 < _Old && _Old < weaponArray.Length && weaponArray[_Old] != null)
{
weaponArray[_Old].SetActive(false);
}
// enable new weapon
// in range and not null
if (0 < _New && _New < weaponArray.Length && weaponArray[_New] != null)
{
weaponArray[_New].SetActive(true);
}
}
[Command]
public void CmdChangeActiveWeapon(int newIndex)
{
activeWeaponSynced = newIndex;
}
void Awake()
{
// disable all weapons
foreach (var item in weaponArray)
{
if (item != null)
{
item.SetActive(false);
}
}
}
```
Add the weapon switch button in update. Only local player switches its own weapon, so it goes below the `!isLocalPlayer` check.
```cs
void Update()
{
if (!isLocalPlayer)
{
// make non-local players run this
floatingInfo.transform.LookAt(Camera.main.transform);
return;
}
float moveX = Input.GetAxis("Horizontal") * Time.deltaTime * 110.0f;
float moveZ = Input.GetAxis("Vertical") * Time.deltaTime * 4f;
transform.Rotate(0, moveX, 0);
transform.Translate(0, 0, moveZ);
if (Input.GetButtonDown("Fire2")) //Fire2 is mouse 2nd click and left alt
{
selectedWeaponLocal += 1;
if (selectedWeaponLocal > weaponArray.Length)
{
selectedWeaponLocal = 1;
}
CmdChangeActiveWeapon(selectedWeaponLocal);
}
}
```
## Part 13
Weapon models
Add the basic cube weapons first, change these later.
- Double click your player prefab to enter it
- Add a "WeaponsHolder" empty GameObject, with position and rotation at 0,0,0.
- Inside that GameObject, create a cube from unity menu, (GameObject, 3D object, cube)- Remove the box colliders.
- Rename this `Weapon1`, change position and scale to match the below pictures.
![](./image--017.jpg)
Duplicate weapon 1 for a Weapon 2, and change its scale and position, now you should have 2 different looking weapons!
![](./image--018.jpg)
## Part 14
Weapon switch finale.
- Add these 2 GameObjects to your PlayerScript.cs weapons array.
- Disable weapon 2, so only weapon 1 shows when spawning.
![](./image--019.jpg)
Build and run!
You should see each player switching weapons, and whatever your player has equipped, will auto show on new joining players (sync var and hook magic!)
![](./image--020.jpg)