DEV Community

Cover image for Ways to interact with the network in Unity with examples for organizing multiplayer games
Devs Daddy
Devs Daddy

Posted on

Ways to interact with the network in Unity with examples for organizing multiplayer games

Hey, everybody. Many people, when they start developing their multiplayer game think about the realization of the network part. In this article I would like to tell you about the main methods of network communication in the framework of client-server relations.

Introduction

Unity provides a powerful engine for creating games and interactive applications, including multiplayer networks. The main task in creating a network game is to synchronize data between clients and server, which requires the use of network protocols. There are two main types of network protocols: TCP and UDP, as well as their hybrid variants. Let's look at each of them in the context of using them with Unity.

Unity Networking for Games

In addition to this, I suggest looking at various off-the-shelf networking solutions, including protocols for communicating between client and server, as well as writing your own socket-based protocol.

So, Let's get started.


Basic network protocols

Unity Networking for Games

TCP (Transmission Control Protocol)

TCP is a protocol that provides reliable data delivery. It ensures that data is delivered in the correct order and without loss. This is achieved through the use of acknowledgments and retransmissions.

Advantages of TCP:

  • Reliability: guaranteed delivery of data.
  • Order: data is delivered in the order in which it was sent.
  • Flow control: controlling the rate at which data is transmitted.

Disadvantages of TCP:

  • Delays: due to the need to confirm delivery.
  • Network load: greater amount of service information.

UDP (User Datagram Protocol)

UDP is a lighter weight protocol that does not provide reliable and orderly data delivery, but minimizes latency.

Advantages of UDP:

  • Less latency: data is sent without waiting for an acknowledgement.
  • Less load: less service information.
  • Suitable for real time: better suited for games and applications that require fast updates (e.g. online shooters).

UDP disadvantages:

  • Unreliable: packets can be lost or duplicated.
  • Lack of order: packets can arrive in any order.

WebSockets

WebSockets is a protocol designed for two-way communication between a client and a server over a single TCP connection. WebSockets are often used for web applications, but can also be useful for games, especially those that run in a browser.

Benefits of WebSockets:

  • Persistent connectivity: maintains an open connection for two-way data exchange.
  • Ease of use: integrates easily with web technologies.

Disadvantages of WebSockets:

  • TCP dependency: shares all of its disadvantages.
  • May be redundant for some types of games.

For the most part, there are many add-ons and enhancements to the communication protocol for web-socket, for example json-rpc, which we will also cover in this article.


Building client-server architecture in Unity

Selecting a network library

As a basis for building multiplayer games, you can choose one of the many ready-made solutions for networking, or describe your own protocols for client and server.

Unity supports several network libraries and services such as:

  • UNet (deprecated): the original Unity networking library, now considered deprecated.
  • Mirror: a popular fork of UNet, actively supported and developed by the community.
  • Photon: a cloud-based networking service that provides lightweight and powerful networking functionality.
  • Netcode for GameObjects: a new library from Unity that supports modern approaches to network synchronization.
  • Heroic Nakama: a large set of open source libraries for networking (also supports hosting in heroic labs cloud).

Next, let's take a closer look at the advantages and disadvantages of off-the-shelf solutions with code examples for implementing simple networking.


UNet

Unity Multiplayer Games Development Examples - UNet
UNet is an obsolete built-in networking library in Unity that provided all the tools needed to create networked games. Although UNet is no longer supported and its use is not recommended for new projects, its legacy is still useful for learning the basic concepts of network gaming in Unity.

Benefits of UNet:

  • Integration with Unity: UNet was built into Unity, making it easy to use and integrate with other engine components.
  • Documentation and examples: At the time of its relevance, a lot of official and user materials were available, making it easy to learn and develop.

Disadvantages of UNet:

  • Obsolescence: UNet is no longer supported by Unity, and new projects should not use it due to lack of updates and patches.
  • Limited functionality: Compared to modern network libraries, UNet had limited features and performance.
  • Lack of support for cloud solutions: UNet did not provide built-in support for cloud services for scalability and usability.

Example of multiplayer game on UNet

Let's consider a simple example of creating a multiplayer game using UNet.

Network Manager Setup:



using UnityEngine;
using UnityEngine.Networking;

public class NetworkManagerCustom : NetworkManager
{
    public override void OnServerAddPlayer(NetworkConnection conn, short playerControllerId)
    {
        var player = Instantiate(playerPrefab);
        NetworkServer.AddPlayerForConnection(conn, player, playerControllerId);
    }
}


Enter fullscreen mode Exit fullscreen mode

Creating a network player:



using UnityEngine;
using UnityEngine.Networking;

public class PlayerController : NetworkBehaviour
{
    void Update()
    {
        if (!isLocalPlayer)
            return;

        float move = Input.GetAxis("Vertical") * Time.deltaTime * 3.0f;
        float turn = Input.GetAxis("Horizontal") * Time.deltaTime * 150.0f;

        transform.Translate(0, 0, move);
        transform.Rotate(0, turn, 0);
    }
}


Enter fullscreen mode Exit fullscreen mode

As you can see, creating a simple solution on UNet is quite localized and fits into the Unity API, however UNet is not currently used in production projects due to its outdated status and limitations.


Mirror

Unity Multiplayer Games Development Examples - Mirror
Mirror is an actively supported fork of UNet, providing updated and improved features. Mirror has become a popular choice for creating networked games due to its simplicity and powerful features.

Benefits of Mirror:

  • Active community: Mirror has an active community of developers who regularly update and improve the library.
  • UNet compatibility: Since Mirror is based on UNet, migrating from UNet to Mirror can be relatively easy.
  • WebGL support: Mirror supports WebGL, allowing for the development of browser-based multiplayer games.

Disadvantages of Mirror:

  • Difficulty in customization: Mirror can take more time to set up and understand compared to other solutions such as Photon.
  • Lack of built-in cloud support: Like UNet, Mirror does not provide built-in cloud solutions, which can make it difficult to scale.

Example of a multiplayer game on Mirror

Now, Let's consider a simple example of creating a multiplayer game using Mirror. As you can see - there are not many differences from UNet, from which Mirror emerged

Network Manager Setup:



using UnityEngine;
using Mirror;

public class NetworkManagerCustom : NetworkManager
{
    public override void OnServerAddPlayer(NetworkConnection conn)
    {
        var player = Instantiate(playerPrefab);
        NetworkServer.AddPlayerForConnection(conn, player);
    }
}


Enter fullscreen mode Exit fullscreen mode

Creating a network player:



using UnityEngine;
using Mirror;

public class PlayerController : NetworkBehaviour
{
    void Update()
    {
        if (!isLocalPlayer)
            return;

        float move = Input.GetAxis("Vertical") * Time.deltaTime * 3.0f;
        float turn = Input.GetAxis("Horizontal") * Time.deltaTime * 150.0f;

        transform.Translate(0, 0, move);
        transform.Rotate(0, turn, 0);
    }
}


Enter fullscreen mode Exit fullscreen mode

As you can already realize, Mirror is simply a development of the ideas of the original UNet with some improvements and fixes to the shortcomings of the original project. Despite the active love and large community, it is used with caution on large projects.


Photon

Unity Multiplayer Games Development Examples - Photon
Photon is a cloud-based networking service that provides easy and powerful tools for creating networked games. Photon PUN (Photon Unity Networking) is a popular library that allows developers to easily integrate networking functionality into their projects.

Photon Advantages:

  • Cloud infrastructure: Photon offers a scalable cloud infrastructure that removes server side worries and simplifies server management.
  • Feature rich: Photon provides many tools and features such as chat, rooms, matchmaking and data synchronization.
  • Multiple Platform Support: Photon supports multiple platforms including mobile devices, PCs and consoles.

Disadvantages of Photon:

  • Cost: Using Photon can be expensive, especially for games with a large number of users.
  • Dependency on a third-party service: Using a third-party cloud service means dependency on its policies, updates, and availability.

Example of multiplayer game on Photon

So, let's look at a small example for working with networking in Photon. For beginners, it is quite a simple solution combined with a lot of ready-made functionality.

Setup Photon Manager:



using UnityEngine;
using Photon.Pun;

public class PhotonManager : MonoBehaviourPunCallbacks
{
    void Start()
    {
        PhotonNetwork.ConnectUsingSettings();
    }

    public override void OnConnectedToMaster()
    {
        PhotonNetwork.JoinLobby();
    }

    public override void OnJoinedLobby()
    {
        PhotonNetwork.JoinRandomRoom();
    }

    public override void OnJoinRandomFailed(short returnCode, string message)
    {
        PhotonNetwork.CreateRoom(null, new Photon.Realtime.RoomOptions { MaxPlayers = 4 });
    }

    public override void OnJoinedRoom()
    {
        PhotonNetwork.Instantiate("PlayerPrefab", Vector3.zero, Quaternion.identity);
    }
}


Enter fullscreen mode Exit fullscreen mode

Creating a network player:



using UnityEngine;
using Photon.Pun;

public class PlayerController : MonoBehaviourPunCallbacks, IPunObservable
{
    void Update()
    {
        if (!photonView.IsMine)
            return;

        float move = Input.GetAxis("Vertical") * Time.deltaTime * 3.0f;
        float turn = Input.GetAxis("Horizontal") * Time.deltaTime * 150.0f;

        transform.Translate(0, 0, move);
        transform.Rotate(0, turn, 0);
    }

    public void OnPhotonSerializeView(PhotonStream stream, PhotonMessageInfo info)
    {
        if (stream.IsWriting)
        {
            stream.SendNext(transform.position);
            stream.SendNext(transform.rotation);
        }
        else
        {
            transform.position = (Vector3)stream.ReceiveNext();
            transform.rotation = (Quaternion)stream.ReceiveNext();
        }
    }
}


Enter fullscreen mode Exit fullscreen mode

As you can see, the implementation on Photon seems a bit larger than on UNet, but you need to realize that it has more functionality out of the box, allowing you to think less about networking issues.


Netcode for GameObjects

Unity Multiplayer Games Development Examples - Netcode for GameObjects
Netcode for GameObjects is a new library from Unity designed for creating modern networked games with support for all modern approaches to synchronization and management of networked objects.

Benefits of Netcode for GameObjects:

  • Modern approaches: Netcode for GameObjects offers modern methods for synchronizing and managing networked objects. Integration with Unity: As an official Unity solution, Netcode for GameObjects integrates with the latest versions of Unity and its ecosystem.
  • PlayFab support: Netcode for GameObjects integrates with PlayFab, making it easy to create and manage scalable multiplayer games.

Disadvantages of Netcode for GameObjects:

  • New technology: Being a relatively new library, Netcode for GameObjects may have fewer examples and tutorials compared to more mature solutions.
  • Incomplete documentation: Documentation and examples may be less extensive compared to Photon or Mirror, which can complicate training and development.
  • Difficulty of transition: For developers using other network libraries, transitioning to Netcode for GameObjects may require significant effort.

Example of multiplayer game on Netcode for GameObjects

Now let's look at an equally small example of networking using Netcode for GameObjects

Creating of Net Manager:



using Unity.Netcode;
using UnityEngine;

public class NetworkManagerCustom : MonoBehaviour
{
    void Start()
    {
        NetworkManager.Singleton.StartHost();
    }
}


Enter fullscreen mode Exit fullscreen mode

Creating a network player:



using Unity.Netcode;
using UnityEngine;

public class PlayerController : NetworkBehaviour
{
    void Update()
    {
        if (!IsOwner)
            return;

        float move = Input.GetAxis("Vertical") * Time.deltaTime * 3.0f;
        float turn = Input.GetAxis("Horizontal") * Time.deltaTime * 150.0f;

        transform.Translate(0, 0, move);
        transform.Rotate(0, turn, 0);
    }
}


Enter fullscreen mode Exit fullscreen mode

Creating multiplayer games in Unity has become more accessible thanks to various network libraries and services such as UNet, Mirror, Photon and Netcode for GameObjects. Each of these libraries has its own features and advantages, allowing developers to choose the most suitable solution for their projects.

However, this is not the only option and for a deeper understanding of the work, let's look at the option of writing your own network engine and using modern protocols for this.


Build your own UDP-based communication

Next we will try to create a simple client and server for your games based on the UDP protocol. We have talked about its advantages and disadvantages above.

Building UDP Server:



using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;

public class UdpServer
{
    private UdpClient udpServer;
    private IPEndPoint clientEndPoint;

    public UdpServer(int port)
    {
        udpServer = new UdpClient(port);
        clientEndPoint = new IPEndPoint(IPAddress.Any, 0);
    }

    public void Start()
    {
        Thread receiveThread = new Thread(new ThreadStart(ReceiveData));
        receiveThread.Start();
    }

    private void ReceiveData()
    {
        while (true)
        {
            byte[] data = udpServer.Receive(ref clientEndPoint);
            string message = Encoding.UTF8.GetString(data);
            Debug.Log("Received: " + message);

            // Say hello from server to client
            SendData("Hello from server");
        }
    }

    private void SendData(string message)
    {
        byte[] data = Encoding.UTF8.GetBytes(message);
        udpServer.Send(data, data.Length, clientEndPoint);
    }
}


Enter fullscreen mode Exit fullscreen mode

Now, let's build an UDP client:



using System.Net;
using System.Net.Sockets;
using System.Text;

public class UdpClient
{
    private UdpClient udpClient;
    private IPEndPoint serverEndPoint;

    public UdpClient(string serverIp, int serverPort)
    {
        udpClient = new UdpClient();
        serverEndPoint = new IPEndPoint(IPAddress.Parse(serverIp), serverPort);
    }

    public void SendData(string message)
    {
        byte[] data = Encoding.UTF8.GetBytes(message);
        udpClient.Send(data, data.Length, serverEndPoint);
    }

    public void ReceiveData()
    {
        udpClient.BeginReceive(new AsyncCallback(ReceiveCallback), null);
    }

    private void ReceiveCallback(IAsyncResult ar)
    {
        byte[] data = udpClient.EndReceive(ar, ref serverEndPoint);
        string message = Encoding.UTF8.GetString(data);
        Debug.Log("Received: " + message);

        // Recieve Processing
        ReceiveData();
    }
}


Enter fullscreen mode Exit fullscreen mode

Thus we have simply exchanged messages via UDP, but you should realize that in order to build your own network - you will have to lay a lot of functionality, watch for packet loss and use UDP better in cases where we do not care about data loss (for example, for some analytical purposes).


Implementation by the example of WebSockets

One of the most popular ways to build a network is based on Web Sockets. Many solutions choose it as a reliable and time-tested TCP-based protocol. In addition, additional solutions to improve communication can be bolted on to it (which we will discuss further), but for now let's look at the basic implementation.

Creating a WebSocket server (using WebSocketSharp):



using WebSocketSharp.Server;
using WebSocketSharp;

public class WebSocketServer
{
    private WebSocketServer wss;

    public WebSocketServer(int port)
    {
        wss = new WebSocketServer(port);
        wss.AddWebSocketService<ChatBehavior>("/Chat");
    }

    public void Start()
    {
        wss.Start();
    }

    public void Stop()
    {
        wss.Stop();
    }
}

public class ChatBehavior : WebSocketBehavior
{
    protected override void OnMessage(MessageEventArgs e)
    {
        Send("Hello from server");
    }
}


Enter fullscreen mode Exit fullscreen mode

Create a basic WebSocket Client (using WebSocketSharp):



using WebSocketSharp;

public class WebSocketClient
{
    private WebSocket ws;

    public WebSocketClient(string serverUrl)
    {
        ws = new WebSocket(serverUrl);
        ws.OnMessage += (sender, e) =>
        {
            Debug.Log("Received: " + e.Data);
        };
    }

    public void Connect()
    {
        ws.Connect();
    }

    public void SendData(string message)
    {
        ws.Send(message);
    }

    public void Close()
    {
        ws.Close();
    }
}


Enter fullscreen mode Exit fullscreen mode

In terms of comparing basic approaches, building a client-server network in Unity requires understanding the different network protocols and choosing the right library or service. TCP is suitable for applications that require reliability and data consistency, while UDP is better suited for games with high speed requirements and low latency. WebSockets offer flexibility for web applications and ease of use.

Depending on the requirements of your project, you can choose the most appropriate protocol and tools to create an efficient and reliable client-server network.

Now let's take a look at the various add-ons over WebSocket and over protocols to simplify the work of exchanging data between client and server.


Messaging protocols

Messaging protocols serve as a simplification for server and client communication, by which you can send various events to the server and it will in due course do a calculation and give you the result using the same protocol. They are usually built on top of off-the-shelf network protocols like WebSocket, etc.

Today we'll look at several variations of messaging protocols:

  • JSON-RPC: is a simple remote procedure call (RPC) protocol that uses JSON;
  • REST: is an architectural style that uses standard HTTP methods and can also be used on sockets;
  • gRPC: high-performance HTTP/2-based remote procedure call protocol;

And of course, let's try to create our own fast protocol for exchanging messages between client and server.


JSON RPC

What is JSON-RPC?

JSON-RPC is a simple remote procedure call (RPC) protocol that uses JSON (JavaScript Object Notation) to encode messages. JSON-RPC is lightweight and uncomplicated to implement, making it suitable for a variety of applications, including games.

Advantages of JSON-RPC:

  • Simplicity: JSON-RPC is easy to use and implement.
  • Lightweight: Using JSON makes messages compact and easy to read.
  • Wide compatibility: JSON-RPC can be used with any programming language that supports JSON.

Disadvantages of JSON-RPC:

  • Limited functionality: JSON-RPC does not provide features such as connection management or real-time data stream processing.
  • Does not support two-way communication: JSON-RPC works on a request-response model, which is not always convenient for games that require constant state updates.

Example of using JSON-RPC in Unity

Python server using Flask and Flask-JSON-RPC:



from flask import Flask
from flask_jsonrpc import JSONRPC

app = Flask(__name__)
jsonrpc = JSONRPC(app, '/api')

@jsonrpc.method('App.echo')
def echo(s: str) -> str:
    return s

if __name__ == '__main__':
    app.run(host='0.0.0.0', port=5000)


Enter fullscreen mode Exit fullscreen mode

Client in Unity using UnityWebRequest:



using UnityEngine;
using UnityEngine.Networking;
using System.Text;

public class JSONRPCClient : MonoBehaviour
{
    private const string url = "http://localhost:5000/api";

    void Start()
    {
        StartCoroutine(SendRequest("Hello, JSON-RPC!"));
    }

    IEnumerator SendRequest(string message)
    {
        string jsonRequest = "{\"jsonrpc\":\"2.0\",\"method\":\"App.echo\",\"params\":[\"" + message + "\"],\"id\":1}";
        byte[] body = Encoding.UTF8.GetBytes(jsonRequest);

        using (UnityWebRequest request = new UnityWebRequest(url, "POST"))
        {
            request.uploadHandler = new UploadHandlerRaw(body);
            request.downloadHandler = new DownloadHandlerBuffer();
            request.SetRequestHeader("Content-Type", "application/json");

            yield return request.SendWebRequest();

            if (request.result != UnityWebRequest.Result.Success)
            {
                Debug.LogError(request.error);
            }
            else
            {
                Debug.Log(request.downloadHandler.text);
            }
        }
    }
}


Enter fullscreen mode Exit fullscreen mode

Often JSON-RPC can be an option for exchanging data with an authorization server, or matchmaking, which gives room launch data for your games. It is easy to install, customize, and understand when developing your games.


REST

What is REST?

REST (Representational State Transfer) is an architectural style that uses standard HTTP methods (GET, POST, PUT, DELETE) to communicate between a client and a server. RESTful API is widely used in web applications and can be useful for creating game servers.

Advantages of REST:

  • Broad support: REST uses standard HTTP, making it compatible with most platforms and programming languages.
  • Simplicity: Easy to implement and understand by using standard HTTP methods.
  • Caching: HTTP allows responses to be cached, which can improve performance.

Disadvantages of REST:

  • Not optimal for real-time: REST uses a request-response model, which is not always suitable for applications that require constant updates.
  • Data overload: Each HTTP message can contain redundant headers that increase the amount of data transferred.

Most often, REST, like JSON-RPC is used for basic exchange with your server's API. It is convenient to get profile data and other data models for meta-game.

REST Examples

NodeJS simple server with Express Framework:



const express = require('express');
const app = express();
const port = 3000;

app.use(express.json());

app.post('/echo', (req, res) => {
  res.json({ message: req.body.message });
});

app.listen(port, () => {
  console.log(`Server listening at http://localhost:${port}`);
});


Enter fullscreen mode Exit fullscreen mode

Unity client with UnityWebRequest:



using UnityEngine;
using UnityEngine.Networking;
using System.Text;

public class RESTClient : MonoBehaviour
{
    private const string url = "http://localhost:3000/echo";

    void Start()
    {
        StartCoroutine(SendRequest("Hello, REST!"));
    }

    IEnumerator SendRequest(string message)
    {
        string jsonRequest = "{\"message\":\"" + message + "\"}";
        byte[] body = Encoding.UTF8.GetBytes(jsonRequest);

        using (UnityWebRequest request = new UnityWebRequest(url, "POST"))
        {
            request.uploadHandler = new UploadHandlerRaw(body);
            request.downloadHandler = new DownloadHandlerBuffer();
            request.SetRequestHeader("Content-Type", "application/json");

            yield return request.SendWebRequest();

            if (request.result != UnityWebRequest.Result.Success)
            {
                Debug.LogError(request.error);
            }
            else
            {
                Debug.Log(request.downloadHandler.text);
            }
        }
    }
}


Enter fullscreen mode Exit fullscreen mode

gRPC

What is gRPC?

gRPC is a high-performance remote procedure call protocol developed by Google. gRPC uses HTTP/2 for data transport and Protocol Buffers (protobuf) for message serialization, which provides high performance and low latency.

Benefits of gRPC:

  • High performance: The use of HTTP/2 and protobuf ensures fast and efficient data transfer.
  • Multi-language support: gRPC supports multiple programming languages.
  • Streaming: Supports real-time data streaming.

Disadvantages of gRPC:

  • Complexity: More difficult to configure and use compared to REST.
  • Need to learn protobuf: Requires knowledge of Protocol Buffers for message serialization.

Examples of gRPC usage for Unity Games

Python server using grpcio:



import grpc
from concurrent import futures
import time

import echo_pb2
import echo_pb2_grpc

class EchoService(echo_pb2_grpc.EchoServiceServicer):
    def Echo(self, request, context):
        return echo_pb2.EchoReply(message='Echo: ' + request.message)

def serve():
    server = grpc.server(futures.ThreadPoolExecutor(max_workers=10))
    echo_pb2_grpc.add_EchoServiceServicer_to_server(EchoService(), server)
    server.add_insecure_port('[::]:50051')
    server.start()
    try:
        while True:
            time.sleep(86400)
    except KeyboardInterrupt:
        server.stop(0)

if __name__ == '__main__':
    serve()


Enter fullscreen mode Exit fullscreen mode

Client in Unity using gRPC C#:



using UnityEngine;
using Grpc.Core;
using GrpcEcho;

public class GRPCClient : MonoBehaviour
{
    private Channel channel;
    private EchoService.EchoServiceClient client;

    void Start()
    {
        channel = new Channel("localhost:50051", ChannelCredentials.Insecure);
        client = new EchoService.EchoServiceClient(channel);
        var reply = client.Echo(new EchoRequest { Message = "Hello, gRPC!" });
        Debug.Log("Received: " + reply.Message);
    }

    void OnDestroy()
    {
        channel.ShutdownAsync().Wait();
    }
}


Enter fullscreen mode Exit fullscreen mode

The choice of messaging protocol for creating networked games in Unity depends on the specific requirements of the project. JSON-RPC and REST are easy to use and implement, but may not be suitable for applications that require real-time data exchange. gRPCs provide low latency and efficient data transfer, but require more complex configuration and connection management. Understanding the features of each protocol will help developers choose the best solution for their game projects.


Creating your own WebSocket-based binary messaging protocol

WebSocket is an excellent protocol for creating games that require real-time communication. It supports two-way communication between client and server over a single TCP connection, which provides low latency and efficiency. Next, we'll look at how to create your own WebSocket-based binary messaging protocol for games on Unity.

Why a binary protocol?

Binary protocols offer several advantages over text-based protocols (e.g. JSON or XML):

  • Efficiency: Binary data takes up less space than text-based formats, which reduces the amount of information transferred and speeds up transmission.
  • Performance: Parsing binary data is typically faster than parsing text formats.
  • Flexibility: Binary protocols allow for more efficient encoding of different data types (e.g., floating point numbers, integers, fixed-length strings, etc.).

Binary protocol basics

When creating a binary protocol, it is important to define the format of messages. Each message should have a well-defined structure so that both client and server can interpret the data correctly.

A typical message structure might include:

  • Header: Information about the message type, data length, and other metadata.
  • Body: The actual message data.

Example message structure:

  • Message Type (1 byte): Specifies the message type (e.g. 0x01 for player movement, 0x02 for attack, etc.).
  • Data length (2 bytes): The length of the message body.
  • Message Body (variable length): Contains data specific to each message type.

Binary protocol implementation in Unity

First, let's create a WebSocket server on Node.js that will receive and process binary messages.

Server Code:



const WebSocket = require('ws');

const wss = new WebSocket.Server({ port: 8080 });

wss.on('connection', ws => {
  ws.on('message', message => {
    // Parse Message Type
    const messageType = message.readUInt8(0);

    switch (messageType) {
      case 0x01:
        // Handle Player Movement
        handlePlayerMove(message);
        break;
      case 0x02:
        // Handle Attack Message
        handlePlayerAttack(message);
        break;
      default:
        console.log('Unknown Message Type:', messageType);
    }
  });
});

function handlePlayerMove(message) {
  const playerId = message.readUInt16BE(1);
  const posX = message.readFloatBE(3);
  const posY = message.readFloatBE(7);
  console.log(`The Player ${playerId} moved to (${posX}, ${posY})`);
}

function handlePlayerAttack(message) {
  const playerId = message.readUInt16BE(1);
  const targetId = message.readUInt16BE(3);
  console.log(`Player ${playerId} attacked ${targetId}`);
}

console.log('Server based on WebSocket runned at port 8080');


Enter fullscreen mode Exit fullscreen mode

And don't forget about depedencies:



npm install ws


Enter fullscreen mode Exit fullscreen mode

Now let's create a client in Unity that will send binary messages to the server (Based on WebSocketSharp library):



using UnityEngine;
using WebSocketSharp;
using System;

public class WebSocketClient : MonoBehaviour
{
    private WebSocket ws;

    void Start()
    {
        ws = new WebSocket("ws://localhost:8080");
        ws.OnMessage += (sender, e) =>
        {
            Debug.Log("Message Received: " + BitConverter.ToString(e.RawData));
        };
        ws.Connect();

        // Send Movement Data
        SendPlayerMove(1, 10.0f, 20.0f);

        // Send Attack Data
        SendPlayerAttack(1, 2);
    }

    void OnDestroy()
    {
        ws.Close();
    }

    private void SendPlayerMove(int playerId, float posX, float posY)
    {
        byte[] message = new byte[11];
        message[0] = 0x01; // Message Type
        BitConverter.GetBytes((ushort)playerId).CopyTo(message, 1);
        BitConverter.GetBytes(posX).CopyTo(message, 3);
        BitConverter.GetBytes(posY).CopyTo(message, 7);
        ws.Send(message);
    }

    private void SendPlayerAttack(int playerId, int targetId)
    {
        byte[] message = new byte[5];
        message[0] = 0x02; // Message Type
        BitConverter.GetBytes((ushort)playerId).CopyTo(message, 1);
        BitConverter.GetBytes((ushort)targetId).CopyTo(message, 3);
        ws.Send(message);
    }
}


Enter fullscreen mode Exit fullscreen mode

Here we covered the basics of binary protocols, their advantages and disadvantages, and gave an example of implementing a server in Node.js and a client in Unity. Using binary messages can significantly reduce overhead and increase the performance of a network game.

Conclusion

Networking is a complex process that encompasses many nuances to implement. In general, we have covered basic protocols for transport and messaging, and next time we will learn more advanced examples of synchronizing players, data and try to create our own matchmaking.

And of course thank you for reading the article, I would be happy to discuss your own networking schemas.


You can also support writing tutorials, articles and see ready-made solutions for your projects:

My Discord | My Blog | My GitHub | Buy me a Beer

BTC: bc1qef2d34r4xkrm48zknjdjt7c0ea92ay9m2a7q55
ETH: 0x1112a2Ef850711DF4dE9c432376F255f416ef5d0

Top comments (0)