Unity 5.0 introduced WebGL support for games built with Unity. Let’s see how we can use Web Sockets to build a real time multiplayer game with Unity. With the Web Socket support, we built a Pong game for our digital signage screens where two users can join the game and play against each other using their mobile phones as gamepads controlling the movement of their pad on the big screen.

Handling sockets on Unity Game

It is possible to use Web Sockets or WebRTC, both networking protocols supported by browsers. Neither of these protocols are exposed through built-in APIs in Unity yet, but it is possible to use a JavaScript plugin to implement this. Unity provides a sample plugin that implements WebSocket networking on WebGL.

The first step we need is to connect to a Web Socket server:

IEnumerator SetupWebSocket () {
  // ...
  mWebSocket = new WebSocket (webSocketServerUri);
  yield return StartCoroutine (mWebSocket.Connect ());
  // ...
}

Once we are connected to the server, we can receive messages on the socket using RecvString and send messages using SendString. The sample plugin doesn’t support events for messages, errors etc. To check for new messages available through the socket, use RecvString method in Update:

void Update () {
  if (!WebSocketClosed && mWebSocket != null) {
    // Check for new messages
    string reply = mWebSocket.RecvString ();
    if (reply != null) {
      // TODO Parse the reply
    }
    if (mWebSocket.Error != null) {
      Debug.LogError ("Error: " + mWebSocket.Error);
      WebSocketClosed = true;

      // Web Socket closed. Try to reconnect
      StartCoroutine(SetupWebSocket ());
    }
  }
}

Handling sockets on GamePads

The game that we are developing allows users to join the game through their mobile phones by navigating to a webpage. Let’s see how we can handle the sockets on the gamepad. As before, we first need to connect to the Web Socket server.

GateMedia.GamePad.prototype._init = function () {
  this.conn = new WebSocket(webSocketServerUrl);
  if (this.conn) {
    this._handleEvents();
  }
}

GateMedia.GamePad.prototype._handleEvents = function () {
  this.conn.onopen = function () {
    console.log('Connection open');
    // TODO We are connected. Enable gamepad
  }.bind(this);

  this.conn.onclose = function () {
    console.log('Connection closed');
    // TODO The connection was closed. Game over?
  }.bind(this);

  this.conn.onerror = function () {
    // TODO The connection was closed. Game over?
  }.bind(this);

  this.conn.onmessage = function (evt) {
    // TODO Parse received string data from `evt.data`
  }.bind(this);
};

The last step is to send the position of the pad to the game whenever it is moved.

draggablePad.on('dragMove', function (/*event, pointer, moveVector*/) {
  var padBounds = _this.elPad.getBoundingClientRect(),
      padCenter = (padBounds.top + padBounds.bottom) / 2,
      padPosition = (padCenter - padCenterMin) / availableHeight;
  if (Math.abs(lastUpdatedPosition - padPosition) > 0.01) {
    // TODO Create data string for new position
    var data = '';

    // Send new position on the socket
    _this.conn.send(data);

    lastUpdatedPosition = padPosition;
    console.log('Pad moved. New position: ', padPosition);
  }
});

Putting it all together

On a 3G network, we have less than 100ms ping roundtrip which allows for almost unnoticeable delay between user input on a mobile phone and the actual game displaying on another screen.