This shows you the differences between two versions of the page.
pjv:laboratoare:08 [2020/01/08 13:53] alexandru.gradinaru |
pjv:laboratoare:08 [2020/01/15 16:51] (current) alexandru.gradinaru |
||
---|---|---|---|
Line 1: | Line 1: | ||
- | ====== Multiplayer ====== | + | ====== Multiplayer (bonus) ====== |
Exista mai multe metode de a face un joc controlabil de mai multi jucatori in acelasi timp: | Exista mai multe metode de a face un joc controlabil de mai multi jucatori in acelasi timp: | ||
Line 6: | Line 6: | ||
===== Non-network ===== | ===== Non-network ===== | ||
- | .. | + | <hidden>https://answers.unity.com/questions/1293382/instance-a-scene-twice-two-worlds-two-cameras-etc.html</hidden> |
+ | 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]]. | ||
+ | |||
+ | {{ :pjv:laboratoare:inspectorcamera35.png?200 |}} | ||
+ | |||
+ | Astfel, pentru un split-screen vertical se pot folosi doua camere cu valorile urmatoare pentru ViewPort Rect (x, y, width, height) | ||
+ | |||
+ | <code> | ||
+ | Camera 1 : (0, 0, 0.5, 1) ► left | ||
+ | Camera 2 : (0.5, 0, 0.5, 1) ► right | ||
+ | </code> | ||
+ | {{ :pjv:laboratoare:camera_horizontal_splitscreen.png?600 |}} | ||
+ | iar pentru pentru un split-screen orizontal | ||
+ | <code> | ||
+ | Camera 1 : (0, 0.5, 1, 0.5) ► top | ||
+ | Camera 2: (0, 0, 1, 0.5) ► bottom | ||
+ | </code> | ||
+ | {{ :pjv:laboratoare:camera_vertical_splitscreen.png?600 |}} | ||
+ | |||
+ | 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 ([[https://docs.unity3d.com/ScriptReference/Camera-cullingMask.html|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). | ||
+ | |||
+ | {{ :pjv:laboratoare:layer-cullingmask.png?200 |}} | ||
Line 238: | Line 263: | ||
... | ... | ||
+ | ==== DarkRift ==== | ||
+ | [[https://darkriftnetworking.com/DarkRift2|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 [[https://assetstore.unity.com/packages/tools/network/darkrift-networking-2-95309|DarkRift Networking 2]] | ||
+ | |||
+ | Folosind un server dedicat trebuie gestionate mai multe elemente: | ||
+ | * conexiunile si lista curenta de clienti conectati | ||
+ | * datele stocate despre player | ||
+ | * mesajele de update | ||
+ | |||
+ | DarkRift foloseste un sistem modular, bazat pe plugin-uri C#, pentru implementarea serverului. | ||
+ | Astfel, putem defini un plugin in felul urmator: | ||
+ | <code> | ||
+ | public class NetPlugin : Plugin | ||
+ | { | ||
+ | |||
+ | Dictionary<IClient, Player> players = new Dictionary<IClient, Player>(); | ||
+ | |||
+ | public override bool ThreadSafe => false; | ||
+ | |||
+ | public override Version Version => new Version(1, 2, 5); | ||
+ | |||
+ | public NetPlugin(PluginLoadData pluginLoadData) : base(pluginLoadData) | ||
+ | { | ||
+ | ClientManager.ClientConnected += ClientConnected; | ||
+ | ClientManager.ClientDisconnected += ClientDisconnected; | ||
+ | } | ||
+ | } | ||
+ | </code> | ||
+ | unde clasa Player reprezinta datele stocate despre player. Spre exemplu se pot stoca date legate de pozitie, rotatie sau id de utilizator. | ||
+ | <code> | ||
+ | public struct Player | ||
+ | { | ||
+ | public ushort ID; | ||
+ | public float X, Y, Z; | ||
+ | public float rotX, rotY, rotZ; | ||
+ | } | ||
+ | </code> | ||
+ | |||
+ | In momentul in care se conecteaza un client, trebuie realizate urmatoarele lucruri: | ||
+ | * instantierea Player-ului in server | ||
+ | * notificarea tuturor celorlalti clienti conectati de conectarea unui nou player | ||
+ | * trimiterea de informatii despre ceilalti clienti, clientului nou conectat | ||
+ | * ascultarea de mesaje de actualizare | ||
+ | |||
+ | <code c#> | ||
+ | void ClientConnected(object sender, ClientConnectedEventArgs e) | ||
+ | { | ||
+ | //new player instance | ||
+ | Player newPlayer = new Player(); | ||
+ | newPlayer.ID = e.Client.ID; | ||
+ | newPlayer.X = 0; | ||
+ | newPlayer.Y = 3; | ||
+ | newPlayer.Z = 0; | ||
+ | newPlayer.rotZ = 0; | ||
+ | newPlayer.rotY = 0; | ||
+ | newPlayer.rotZ = 0; | ||
+ | |||
+ | using (DarkRiftWriter newPlayerWriter = DarkRiftWriter.Create()) | ||
+ | { | ||
+ | newPlayerWriter.Write(newPlayer.ID); | ||
+ | newPlayerWriter.Write(newPlayer.X); | ||
+ | newPlayerWriter.Write(newPlayer.Y); | ||
+ | newPlayerWriter.Write(newPlayer.Z); | ||
+ | newPlayerWriter.Write(newPlayer.rotX); | ||
+ | newPlayerWriter.Write(newPlayer.rotY); | ||
+ | newPlayerWriter.Write(newPlayer.rotZ); | ||
+ | |||
+ | //notify other clients | ||
+ | using (Message newPlayerMessage = Message.Create(0, newPlayerWriter)) | ||
+ | { | ||
+ | foreach (IClient client in ClientManager.GetAllClients().Where(x => x != e.Client)) | ||
+ | client.SendMessage(newPlayerMessage, SendMode.Reliable); | ||
+ | } | ||
+ | } | ||
+ | |||
+ | players.Add(e.Client, newPlayer); | ||
+ | |||
+ | using (DarkRiftWriter playerWriter = DarkRiftWriter.Create()) | ||
+ | { | ||
+ | foreach (Player player in players.Values) | ||
+ | { | ||
+ | playerWriter.Write(player.ID); | ||
+ | playerWriter.Write(player.X); | ||
+ | playerWriter.Write(player.Y); | ||
+ | playerWriter.Write(player.Z); | ||
+ | playerWriter.Write(player.rotX); | ||
+ | playerWriter.Write(player.rotY); | ||
+ | playerWriter.Write(player.rotZ); | ||
+ | } | ||
+ | | ||
+ | //send all other players data to new client | ||
+ | using (Message playerMessage = Message.Create(0, playerWriter)) | ||
+ | e.Client.SendMessage(playerMessage, SendMode.Reliable); | ||
+ | } | ||
+ | |||
+ | e.Client.MessageReceived += MovementMessageReceived; | ||
+ | } | ||
+ | </code> | ||
==== Steam ==== | ==== Steam ==== | ||
Line 256: | Line 379: | ||
... | ... | ||
+ | ===== Cerinte ===== | ||
+ | |||
+ | Realizarea unui joc multiplayer (network) | ||
+ | |||
+ | * Realizarea unui client si a unui server de networking prin oricare varianta descrisa in laborator (sau altele) | ||
+ | * Clientii (players) trebuie sa vada miscarile si animatiile celorlalti clienti | ||
+ | * Clientii trebuie sa poata interactiona intre ei si cu mediul: | ||
+ | * coliziuni cu mediul (vizibile de catre toti clientii) | ||
+ | * coliziunile dintre playeri afecteaza doar playerii implicati (de ex impuscarea/lovirea unui alt player ii va lua damage doar acelui player, si se va actualiza interfata grafica doar pentru playerul respectiv) | ||