This is an old revision of the document!
Exista mai multe metode de a face un joc controlabil de mai multi jucatori in acelasi timp:
Pentru jocuri locale de multiplayer se foloseste in general o tehnica de split-screen: folosirea mai multor camere care se afiseaza impart viewport-ul intantei locale a jocului https://docs.unity3d.com/ScriptReference/Camera-rect.html.
Astfel, pentru un split-screen vertical se pot folosi doua camere cu valorile urmatoare pentru ViewPort Rect (x, y, width, height)
Camera 1 : (0, 0, 0.5, 1) ► left Camera 2 : (0.5, 0, 0.5, 1) ► right
iar pentru pentru un split-screen orizontal
Camera 1 : (0, 0.5, 1, 0.5) ► top Camera 2: (0, 0, 1, 0.5) ► bottom
Controlul jucatorilor va trebui sa se codeze separat prin definirea de axe sau taste specifice fiecarui jucator.
Daca se doreste implementarea unui GUI personalizat/diferit pentru fiecare jucator, se pot folosi masti de camera (culling masks). Se vor plasa toate obiecte Jucatorului 1 intr-un layer separat (de ex Player1Layer), si obiectele jucatorului 2 in alt layer (Player2Layer) si se va asigura ca pentru fiecare camera sunt selectate layer-urile corecte (se vor exclude pentru camera 1 layer-ul cu Player2, si pentru camera 2 layer-ul cu Player 1).
Exista mai multe moduri de a programa un joc in retea. In general se foloseste un server separat programat special, la care se conecteaza clientii sub diverse forme. In functie de necesitati, se folosesc diverse metode de implementare a comunicarii, persistentei si a spatiului:
Pentru necesitati mici, un server dedicat nu este neaprat necesar, unul din clienti putand prelua si aceasta responsabilitate.
Unity pune la dispozitie un layer de networking de nivel inalt, HLAPI (Multiplayer High Level API), care permite realizarea de jocuri multiplayer relativ usor. HLAPI contine componente, scripturi si comenzi predefinite in pachetul UnityEngine.Networking, astfel incat sa usureze dezvoltarea multiplyer:
Exista si servere dedicate stand-alone care se pot configura usor astfel incat sa fie integrate cu Unity:
Pentru a folosi functionalitatea implicita oferita de Unity in materie de networking, va trebui in primul rand sa initializat un script Network Manager
, care se gaseste ca si componenta in meniu.
Acesta are nevoie de mai multe elemente de config, printre cele mai importante fiind:
Pentru a adauga o referinta la un obiect in layerul de networking, acesta trebuie sa aiba cateva componenta importante:
Pentru testare si prototipare usoara a partii de networking, se poate folosi componenta de NetworkManagerHud
oferita de Unity.
Aceasta va afisa un meniu pentru conectare usoara.
Parcurgand pasii de mai sus va permite deja sa porniti un joc cu mai multi clienti si un server, care sa functioneze si sa sincronizeze miscarile jucatorilor.
Scriptarea obiectelor intr-un joc de multiplayer are anumite particularitati pentru ca trebuie sa asigure mai multe elemente:
Astfel, exista definite in pachetul UnityEngine.Networking, flag-urile urmatoare:
Astfel, in cazul unui script de PlayerController, trebuie sa ne asiguram ca actiunile sunt facute doar pentru clientul local. O metoda usoara, este prin stergerea efectiva a obiectelor/componentelor atunci cand acestea nu sunt locale. Sau, verific ca fiecare actiune sa fie facuta doar local.
using UnityEngine.Networking; .. public class PlayerController : NetworkBehaviour { public GameObject player; void Start () { //varianta 1 - distrug componenta curenta pentru ceilalti, local, in clientul propriu if(!isLocalPlayer) { Destroy(this); return; } } void Update() { //sau verific ca afecteaza doar obiectul de pe clientul curent if(isLocalPlayer == true) { float translation = Input.GetAxis("Vertical") * Time.deltaTime; transform.Translate(0, 0, translation); float h = horizontalSpeed * Input.GetAxis("Mouse X"); float v = verticalSpeed * Input.GetAxis("Mouse Y"); transform.Rotate(v, h, 0); if(Input.GetAxis("Mouse ScrollWheel")) { //another gun - obiect creat cu autoritate pentru clientul local GameObject gun1 = (GameObject)Instantiate(Sniper, transform.position, Quaternion.identity); NetworkServer.SpawnWithClientAuthority(gun1, connection); } } } }
Pentru ca obiectele sa poata fi instantiate din prefab-uri pentru networking, ele trebuie inregistrate in lista din setarile NewtorkManagerului:
Astfel, in cazul in care obiectele nu fac parte efectiv din player, cum ar fi cele de PickUp, sau arme care se pot schimba etc. putem folosi flag-ul de hasAuthority:
using UnityEngine.Networking; .. public class Gun: NetworkBehaviour { void Update() { if(hasAuthority == true) { if(Input.GetAxis("Fire1")) { //fire bullet } } } }
Un caz particular de obiecte instantiate sunt cele care ar putea crea coliziuni. Astfel, daca avem gloante instantiate ca obiecte, aceste trebuie sa:
Pentru acest caz de obiecte avem la indemana posibilitatea de a trimite o comanda severului, astfel incat sa instantieze aceste tipuri de obiecte.
using UnityEngine.Networking; .. public class Gun: NetworkBehaviour { void Update() { if(hasAuthority == true) { if(Input.GetAxis("Fire1")) { //fire bullet CmdFireBullet(); } } } //Functie apelata de pe clientul local si rulata in server //Prefixul `Cmd` este necesar in fata denumirii functiei [Command] void CmdFireBullet() { //fire bullet - obiect creat pentru toti clientii GameObject bullet = (GameObject)Instantiate(Bullet, transform.position, Quaternion.identity); //add force , translation etc. NetworkServer.Spawn(bullet); } }
Mai departe, pentru a scripta Bullet-ul in server, trebuie sa adaugam cateva elemente, astfel incat serverul sa gestioneze bucla de Update() si coliziunea efectiva.
public class Bullet : NetworkBehaviour { [ServerCallback] void Update() { life +=Time.deltaTime; if(life >= maxSpawnLife) { NetworkServer.Destroy(gameObject); //daca a trecut timpul maxim de existenta a unui bullet, il stergem din tot serverul (deci va disparea de la toti clientii) } } void OnCollisionEnter(Collision other) { //sectiune vizibila pentru toti clientii - animatii, audio, particule etc. hitParticles.Play(true); if(!isServer) //verific daca nu sunt pe server - si nu mai continui return; //verificam mai departe coliziunile, doar o data, pe server PlayerController player = other.GameObject.GetComponent<PlayerController >(); player.takeDamage(bulletDamage); } }
Inca un lucru important, ar fi elementul de camera, care ar trebui sa fie vizeze doar pentru clientul local. Astfel, in player controller putem avea ca referinta camera, pe care sa putem activa doar pentru clientul local.
public class PlayerController : NetworkBehaviour { public GameObject playerCamera; void Update() { if(isLocalPlayer == true) { playerCamera.SetActive(true); else playerCamera.SetActive(false); ... }
…
DarkRift 2 este un server dedicat, modular, care implementeaza protocoale de comunicare ca TCP, UDP, dar si cu extensii pentru WebSocket sau RUDP. Pentru integrarea cu Unity, se poate folosi pachetul din Asset Store DarkRift Networking 2
..
..
..
..
…
Realizarea unui joc multiplayer (network)