Socket.io For Multiple User Game
There Will Be a Party
So everyone was assigned to come up with a party game. My first few ideas sucked. Then I tried to move on to something geeky and came across the idea of multiple user games.
I need to build a simple game, real time that supports multiple players and in a non-compete but hard to cooperate way.
The Maze
Let’s get through the maze together, like, 100 of us. Seems easy enough for one patient player, but could have unexpected results for a group of people. It will suddenly becomes hard when players try to make a narrow turn.
First, start with a maze. Just grab one here.
The tricky part is setup the multiple user control. We need to have something fast, lightweight, and easy to write, born async and browser-based.
The Control
In comes socket.io. There is a collaborative canvas in their official documents. But actually the simplest example would be helpful enough. I did not use the Express
framework because it is a single page game app.
Client side - control pad
<script src="https://cdn.socket.io/socket.io-1.4.4.js"></script>
<script>
var socket;
socket = io('http://127.0.0.1:8888');
</script>
<div class="table-cell" ontouchstart="socket.emit('command',{control:38})">
<img class="rs rotate270" src="arrow.png" />
</div>
<style>
.rotate90 {
-ms-transform: rotate(90deg);
/* IE 9 */
-webkit-transform: rotate(90deg);
/* Chrome, Safari, Opera */
transform: rotate(90deg);
}
</style>
Notice I used ontouchstart
event listener. That’s because this game is for mobile
devices. On iOS devices there would be a 300ms delay if you want to trigger an onclick
event, which makes this app appear to be slow. But it is not the socket.io part that
slows down the reaction feedback loop, rather the built-in browser.
This is only one button setup. You need to add other three buttons in your preferred layout and style. Add in the event listeners and you are all set.
Client side - maze app
You still need to write the script tag to import socket.io library.
var socket = io.connect('http://127.0.0.1:8888');
socket.on('control', function(data) {
console.log(data);
var e = {
keyCode: data.control
};
moveRect(e);
});
Server side - messaging
var app = require('http').createServer(handler);
var io = require('socket.io')(app);
var fs = require('fs');
app.listen(8888);
function handler(req, res) {
fs.readFile(__dirname + req.url,
function(err, data) {
if (err) {
res.writeHead(500);
return res.end('Error loading:' + req.url);
}
res.writeHead(200);
res.end(data);
});
}
io.on('connection', function(socket) {
socket.on('command', function(data) {
console.log(data);
// *important*: io.emit is global emit
// If we use `socket.emit` here, the socket we use would be the same
// socket we are receiving data, which is on the controller pad webpage.
// That means the maze game app is not getting the data it needs.
// We need to send the control message by broadcasting to the maze game app.
io.emit('control', data);
});
});
It is worth mentioning that you need to separate the two channels, one from game pad to the server and the other one from server to the maze app.
You can use two event names, or you can put them in different socket namespaces. Either way you don’t want inadvertent infinite message loop.