Background

Cocos Creator is a lightweight 2D/3D cross-platform game engine, it’s popular in H5 and mini-game platform. It supports Javascript and Typescript languages. Socket.IO is a real-time application framework to enable bidirectional event-based communication, and widely used in NodeJS.

Recently, I need to use Cocos Creator to develop a mini-game. Since the game needs realtime communication, I went with Socket.IO. I’ll demonstrate how to set up and code the game client and backend server in the following section.

Backend Server

The backend server was created using Node.js. Make sure you have installed Node.js and npm before we start. Firstly, create a new project folder, open a console and enter the project folder directory, type the following command to initialize a Node.JS project:

$ npm init myserver

Then we need to install Socket.IO (version 2.3.0 is used here):

$ npm install [email protected]

Now we create a new javascript file serverapp.js in the project folder, the following server code can do a basic public and private message function.

// serverapp.js
var SocketIO = require('socket.io');
// create socket.io server, listening on port 3000
var ioServer = SocketIO.listen(3000);

ioServer.on('connection', function(iosocket) {
    console.log('A new user %s connected.', iosocket.id);

    iosocket.on('new user', function(username) {
        console.log('Add new user event.');
        newUser(username, iosocket)// add the user to online users and sockets map
    });
	
    iosocket.on('public message', function(msg) {
        console.log('A new public message comes in.');
        var sentMsg = iosocket.id + ': ' + msg;
        iosocket.emit('public message', sentMsg); // public the message to other clients;
    });

    iosocket.on("private message", function(anotherSocketId, msg) {
        console.log('A new private message comes in.');
        iosocket.to(anotherSocketId).emit("private message", iosocket.id, msg);// forward private msg to another client
    });
	
    iosocket.on('disconnect', function() {
        console.log('A user disconnected.');
        userDisconnect(iosocket);// delete the user from online users and sockets map using iosocket.id
    });
});

Start the server by the following command:

$ node serverapp.js

Frontend Game

I am assuming you are already familar with creating a cocos creator project and the basic using, so I won’t go into detail on this part. The game’s frontend was created by Cocos Creator 2.x.x version with Javascript language. Overall project directory is shown in the below, script code files are in the /assets/scripts/.

├── project.json
├── jsconfig.json
├── creator.d.ts
├── settings/
├── assets/
│   ├── resources/
│   ├── scenes/
│   ├── Texture/
│   └── scripts/
│       ├── lib/
│       |   └── socket.io.js   
│       ├── util/
│       |   └── Global.js
│       ├── mygame.js
│       └── ......

Firstly, we need to import the socket.io javascript library code into the project script, you can get the socket.io.js from my github or CDN. Just copy the socket.io.js file to the /assets/scripts/libs/socket.io.js. We must make sure both backend server and frontend game use SAME version socke.io, so we still import socket.io v2.3.0 in the game side.

In order to use socket.io anywhere in the code, we can declare a global socket variable in the /assets/scripts/util/Global.js.

window.G = {
    ioSocket = null,
    ......
}

Now we can create our game code, here I use mygame.js as demonstration.

// mygame.js
cc.Class({
    extends: cc.Component,

    properties: {
        sendPublicButton: cc.Button,
        sendPrivateButton: cc.Button,
        msgLabel: cc.Label,
    },

    onLoad: function () {
        // initialize the sockeio.io socket and connet to server
        G.ioSocket = io.connect("http://your-server-ip:3000", {'reconnect': true});

        G.ioSocket.on('connect', function(){
            console.log('You have connected to the socket.io server.');
        });

        G.ioSocket.on('connect_error', function(){
            console.log('connect_error');
        });

        G.ioSocket.on('disconnect', function(){
            console.log('You have disconnected from socket.io server.');
        });

        G.ioSocket.on('reconnect', function(){
            console.log('You have reconnected to the socket.io server.');
        });

        G.ioSocket.on('reconnect_error', function(){
            console.log('reconnect_fail');
        });

        // init receive public message event
        G.ioSocket.on('public message', function(newMsg){
            this.msgLabel.string = "A new public message: " + newMsg;
        });

        // init receive private message event
        G.ioSocket.on('private message', function(id, newMsg){
            this.msgLabel.string = "A new private message from " + id + ": " + newMsg;
        });
    },

    onsendPublicButtonPressed: function(event, customEventData) {
        var msg = "Hello, I am client.";
        // send public message
        G.ioSocket.emit('public message', msg);
    },

    onsendPrivateButtonPressed: function(event, customEventData) {
        var msg = "Hello, I am client.";
        var friendSocketID = xxxxxxx // you can construct your online users dictionary(socket.id: username) in the server.
        // send private message
        G.ioSocket.emit('private message', friendSocketID, msg);
    },

    start () {
        ......
    },

    update (dt) {
        ......
    },
});   

The above code should be quite clear. Normally, the connection and most event registration are done in the onLoad() function, we can emit message from anywhere in our code as we have decalre the socket globally.

The game client will send a public/private message to the backend server, then the server will forward message to other clients, if we press sendPublicButton/sendPrivateButton.

Multiple Clients

To support mutilple clients, we can construct an online socket/username dictionary in the server code, so the server can recognize the user by its socket id.

var socketmap = {}, users = [];

function newUser(username, socket) {
    if(!(username in socketmap)) {
        socket.username = username;
        socketmap[username] = socket;
        users.push(username);
        console.log('<Online Users>: ', users);
    }
}

function userDisconnect(socket) {
    if(socket.username in socketmap){
        console.log('- User (%s) Disconnected.', socket.username);
        delete(socketmap[socket.username]);
        users.splice(users.indexOf(socket.username), 1);
        console.log('<Online Users>: ', users);
    }
}

–END–