Building HTML5 Multiplayer Game with Node.js
Submitted by admin on Friday, December 9, 2016 - 11:11.
In a previous tutorial, I showed how to build a Socket.IO chat and connect few people around the globe with the magic of WebSockets. In this article I want to make this project a little bit more fun and turn it into a multiplayer game: "rock, paper, scissors".
So let's grab the previous code and introduce few modifications. Firstly, on a client side, we'll add 3 buttons: for rock, paper and scissors.
As you see, building a simple matching system is a matter of just 10 lines of code. There is a space for improvement though. Think, how would you handle disconnect events for this scenario?
Now, the "meat" of the game. The game logic that we will write in startGame() function. The game allows users to "blindly" select their moves, and once both moves are selected, it will find who is a winner. Let's leave the win condition for now and simply print who selected what after round ends.
This code does a lot of things: firsly it keeps track of both player turns (p1Turn and p2Turn). Then it checks if both turns are 'filled' and if so, notifies players about round end.
In this code we introduced a feature of Socket.IO called 'rooms'. A room is simply a group of sockets
that share a name. It is then easy to address all the sockets in the same 'room'. You will often use
rooms to organize matches between players, or to separate one discussion subject from the other in your online chat.
As you see, turning a chat into a game was very easy. With this basic features you can start thinking about more feature-rich online games. Who knows, maybe next World of Warcraft will be played in browsers.
Here's the code of the finished application.
index.html:
main.js (server):
And then add an event listener that sends the 'turn' event to our socket.io server.
- ['rock', 'paper', 'scissors'].forEach(function(figure) {
- var btn = document.getElementById(figure);
- btn.addEventListener('click', function() {
- io.emit('turn', figure);
- });
- });
- <javascript>
- And this code will enough to have our minimalistic Rock, Paper, Scissors client.
- Now we need to update the server code too to support the game. Firstly, we need to match our players
- against each other. We will not implement lobby (even though, it is a good exercise to do on your own). Insted we'll match players in pairs as they come. If there's a player waiting, the next joined player will start a match against him, otherwise the newly joined user will be patiently waiting for his turn.
- <javascript>
- let waiting = null;
- io.on('connection', (sock) => {
- sock.emit('msg', 'You are connected');
- sock.on('msg', (msg) => io.emit('msg', msg));
- if (waiting === null) {
- waiting = sock;
- } else {
- startGame(waiting, sock);
- waiting = null;
- }
- });
- function startGame(p1, p2) {
- // Game Logic goes here
- }
- function startGame(p1, p2) {
- const roomName = 'RPS' + roomId++;
- let p1Turn = null;
- let p2Turn = null;
- [p1, p2].forEach((p) => p.join(roomName));
- io.to(roomName).emit('msg', 'Game Started!');
- p1.on('turn', (e) => {
- console.log(e);
- p1Turn = e;
- checkRoundEnd();
- });
- p2.on('turn', (e) => {
- console.log(e);
- p2Turn = e;
- checkRoundEnd();
- });
- function checkRoundEnd() {
- if (p1Turn !== null && p2Turn !== null) {
- io.to(roomName).emit('msg', 'Round Ended! P1 - '
- + p1Turn + ' P2 - ' + p2Turn);
- io.to(roomName).emit('msg', 'Next round!');
- p1Turn = p2Turn = null;
- }
- }
- }
- <!doctype html>
- <html lang="en">
- <head>
- <meta charset="utf-8">
- </head>
- <body>
- <form id="say-form">
- </form>
- <div>
- </div>
- <script>
- var sock = io();
- sock.on('msg', onChatMessage);
- function onChatMessage(msg) {
- console.log('Chat message!');
- var ul = document.querySelector('#chat');
- var item = document.createElement('li');
- item.innerHTML = msg;
- ul.appendChild(item);
- }
- function sendChatMessage(e) {
- e.preventDefault();
- var inp = document.querySelector('#say-form input');
- var msg = inp.value;
- inp.value = '';
- sock.emit('msg', msg);
- }
- (function init() {
- var form = document.querySelector('#say-form');
- form.addEventListener('submit', sendChatMessage);
- ['rock', 'paper', 'scissors'].forEach(function(figure) {
- var btn = document.getElementById(figure);
- btn.addEventListener('click', function() {
- sock.emit('turn', figure);
- });
- });
- })();
- </script>
- </body>
- </html>
- const express = require('express');
- const http = require('http');
- const app = express();
- const server = http.createServer(app);
- const io = require('socket.io')(server);
- let waiting = null;
- io.on('connection', (sock) => {
- sock.emit('msg', 'You are connected');
- sock.on('msg', (msg) => io.emit('msg', msg));
- if (waiting === null) {
- sock.emit('msg', 'You Are Waiting');
- waiting = sock;
- } else {
- startGame(waiting, sock);
- waiting = null;
- }
- });
- let roomId = 1;
- function startGame(p1, p2) {
- const roomName = 'RPS' + roomId++;
- let p1Turn = null;
- let p2Turn = null;
- [p1, p2].forEach((p) => p.join(roomName));
- io.to(roomName).emit('msg', 'Game Started!');
- p1.on('turn', (e) => {
- console.log(e);
- p1Turn = e;
- checkRoundEnd();
- });
- p2.on('turn', (e) => {
- console.log(e);
- p2Turn = e;
- checkRoundEnd();
- });
- function checkRoundEnd() {
- if (p1Turn !== null && p2Turn !== null) {
- io.to(roomName).emit('msg', 'Round Ended! P1 - '
- + p1Turn + ' P2 - ' + p2Turn);
- io.to(roomName).emit('msg', 'Next round!');
- p1Turn = p2Turn = null;
- }
- }
- }
- app.use(express.static(`${__dirname}/public`));
- server.listen(3000, () => console.log('Ready on 0.0.0.0:3000'));
Add new comment
- 679 views