How to Make a Multiplayer Game with JavaScript: Tutorial
Creating a fast and scalable multiplayer game is no easy task, especially if the game is meant to be played online in a web browser. Not only do you have to design a game that is functional on multiple browsers including desktop and mobile, but you also have to ensure that the gameplay is fluid and responsive when multiple players are enjoying the game in real time.
How to Make an Online Multiplayer Game
This tutorial guides you through building the online multiplayer game Ninja Platformer, a browser-based collaborative puzzle game written in less than 1000 lines of code that encourages you to work with your friends to collect the keys to complete the levels.
Creating the Multiplayer Game Using Phaser, Javascript, HTML and CSS
The game is built using Phaser, an HTML5 game development framework for Canvas and WebGL browser games. Phaser is designed for web and mobile games development with HTML, CSS, and JavaScript. Using Phaser's Arcade Physics Library, each character and object has its own physics body and properties, and the levels themselves are generated via JSON files.
Although this tutorial guides you step-by-step to create a multiplayer browser game, you can view the completed version of the game in the GitHub repository.
Talk to an Expert
Let's connect to discuss your real-time project.
Create Your Multiplayer Game Environment
To create your own HTML5 multiplayer platform game, you'll need a few basic tools to get started. Download a basic code editor, such as Visual Studio Code, and ensure you have access to the terminal.
Install Node.js to Start Building Your Multiplayer Game
Node.js, an open-source JavaScript server environment, is necessary to install packages and execute necessary commands. Install the runtime environment that is appropriate for your operating system, which includes npm
.
Run a Local Web Server to Create Your Multiplayer Game Code
For the Phaser game engine to run in your browser, you are going to need to run a local web server. The Phaser files will not load without a webserver because browsers block files loading from different domains.
Create a folder on the desktop and call it Ninja-Multiplayer-Platformer. In the terminal, navigate to the directory where you created this folder. You have two options for running a local web server, http-server
or browser-sync
.
http-server
launches a simple, but powerful, web server. Execute the following command to install the http-server package.
Run the server with the command http-server
.
Instead of http-server
, you can use a tool called Browser Sync, which will automatically reload the browser every time you modify a file. Install browser-sync with the following command.
Start the tool with the following command, which will launch the server and reload the browser when you modify any JavaScript file.
Obtain PubNub API Keys for Your Multiplayer Game Functionality
To build an application that leverages PubNub's real-time data APIs, you need to sign up for your account to obtain API key. Remember, PubNub will serve as the server-side infrastructure that powers the online multiplayer functionality of the game.
Once you have successfully signed up you will be taken to the admin page, where you can create an application that is associated with specific projects and keysets associated with those projects (development, testing, production, etc) to create the publish/subscribe keys necessary to connect to the PubNub network.
Once you’re in the Admin Dashboard, name your application whatever you wish, and click the Create New App button. Once you create the application, click on the application and create a new keyset.
Click on the keyset, and it should load up a page that shows your keys in addition to features you can enable for your keys. You'll need to enable a few different features in your keyset for this application. You'll need to enable Presence to detect when new users come online, as well as check the Generate Leave on TCP FIN or RST setting. Also, enable Message Persistence to persist messages as they are published. Click on the Save Changes button to save the changes. Copy and save the publish and subscribe keys as you'll need these later on.
Project Assets for the Online Multiplayer Game
Download the ProjectAssets.zip file from the GitHub repository and extract these files to the Ninja-Multiplayer-Platformer folder. These files are the initial starting point of the tutorial, as it contains the starting HTML files, JavaScript files, and other media packages such as images and audio files.
Build and Run Your Online Multiplayer Game: Tutorial
In the Ninja-Multiplayer-Platformer folder, open js/main.js. Copy the following code to load the other Javascript files and initialize the phaser window.
If you have not already started your local web server, be sure to do so now with one of the methods mentioned earlier. Save main.js and refresh the HTML window. You should see that the phaser window has been created. It would be completely black since there are no assets loaded in the scene yet. The window
.Phaser.AUTO
parameter is to specify whether you want a 2D canvas or a WebGL canvas. In this case, you will use WebGL by default but will fall back to 2D if it’s not supported.
Loading State Tutorial
Next, you'll create the loading state to load the assets into the scene. Open js/loadingState.js and add the following functionality.
This code executes several actions. The object window.LoadingState
is created with the loading state information inside of it. In the init
function, the sprite objects in the game are made to look smoother by using the Phaser API this.game.renderer.renderSession.roundPixels = true;
.
In the preload function, the JSON-level information is loaded from the data folder. This data information will be used to generate the levels. Then, every asset that will be used in the game needs to be preloaded into the cache. The various sprite sheets and audio are also preloaded during this time (audio is not used in this tutorial; however, you can easily add it by uncommenting code).
The final piece of code for loadingState.js runs create
that starts the game and loads what the current level is via window.globalCurrentLevel
. This will be handled in main.js.
Navigate back to main.js. Add the following at the top of the file which will create the global variables necessary for the rest of the tutorial.
Play State Tutorial for Your Multiplayer Game
Navigate to js/playState.js and instantiate the PlayState game state and set up some variables.
In init(data)
, this.keys
is set to be the command to detect which key on the keyboard has been pressed. In create
, more variables are set and also make the screen fade in upon loading the web page by using the Phaser API call this.camera.flash('#000000');.
The sound effect variables in the this.sfx
object are then set. For this multiplayer game tutorial, all of the sound effects are commented out. You can enable sound effects for your own game by uncommenting the sound effects. The background image is also set by calling the command this.game.add.image(0, 0, 'background');
.
Next, set the text objects that will detect presence events in each room. There is an if
statement that checks to see if the window.globalLevelState
is equal to null
. If it is equal, the coinCache
is set equal to level:0
since you want the scene to load the first level if it doesn’t receive any information from PubNub. The _loadLevel
function and _createHud
functions are also called.
If you attempt to refresh and run the multiplayer game code, you will receive errors since you are calling functions that have yet to be created. Add some more functionality inside of window.playState
and below the create
function.
_loadLevel(data)
creates asset groups that are needed later on in the code. The function spawns all of the level decorations (the mushrooms, grass, etc) from the JSON file information that was stored in the cache earlier in the code. Next, the animated coins, key, and door are spawned in the level along with setting and enabling gravity.
_spawnPlatform(platform)
spawns each platform object and turns them into a sprite. They are set to not be affected by gravity and be immovable so other sprite objects can’t impact their position.
_spawnCoin(coin)
creates each coin asset and places them on the screen and adds their animations.
_addOtherCharacter(uuid)
adds a hero (another character) that is not your own to the screen when someone else connects to the same PubNub channel.
_spawnCharacters(data)
spawns the hero asset into the game. The hero information is defined in heroScript.js
which will be created shortly. playerText
is set to appear above your player to differentiate between all of the connected players.
_spawnKey(x,y)
creates the key that unlocks the door. A tween is applied to give the key the animation effect.
_spawnDoor(x,y)
places the door in a set position.
_createHud
creates the overlay at the top left of the screen that checks to see if you have collected the key for that level and also how many coins you have obtained.
Keep in mind you will still get errors in the console since the heroScript.js code has not yet been initialized.
Hero Script for the Multiplayer Javascript Game Animations
Open js/heroScript.js and add the following code.
heroScript.js sets up the player animations and handles the player movement. In move(direction)
, the orientation the player is facing depends on the velocity of the player.
In jump,
the properties to determine if the player can jump or not are also set.
In update,
the sprite animation is updated only if it needs to be changed based on player input.
In freeze
, the animation of the player going through the door is played.
In _getAnimationName,
the various animation names are set depending upon the hero body’s velocity.
Save your document then refresh your browser window. You should see something such as this.
Player Movement for the Online Multiplayer Game
Next, you need to implement the logic for player movement. Navigate back to js/playState.js. Below create
, add the following update logic.
update
adds one to the frame count every frame. This is used to sync the player movements across all devices without the need to send PubNub publishes every frame. It also calls _handleInput()
and _handleCollisions
every frame.
shutdown
is used to stop the background music from playing if you so wish to enable audio.
_canHeroEnterDoor(hero)
checks to see if the key has been collected and if the hero is touching the platform object to be allowed to enter through the door.
_handleCollisions
handles all of the scenes' object collision events. For instance, if the hero collides with a key, it will execute the this._onHeroVsKey
function.
_handleInput
runs every frame and checks to see if your hero object exists on the screen. If it does, it determines if any of the keys have been pressed down. If true
, a message is sent that the button pressed is up. You'll notice that some code is commented out until the PubNub functionality is implemented - this will be updated later on to send key event messages through the PubNub network. The character moves by calling this.hero.move(-1)
, this.hero.move(1)
or this.hero.move(0)
. This calls the move function that was implemented in heroScript.js.
Save all of your documents and refresh the web browser. You should see the following screen and be able to move your character around using the left, right, and up arrows.
Implement Javascript for Object Collisions
After testing that you are able to perform basic move mechanics, you will add the functionality for object collision between players and the objects on the screen. Each function is executed when a specific collision event occurs. Add these functions below _handleInput
in playState.js.
If you refresh the window, you will get an error since logCurrentStateCoin
has yet to be defined. Go to the top of playState.js and add the following right below the window.frameCounter
variable.
Although logCurrentStateCoin
has been defined, an error will still be thrown since window.fireCoins
has not yet been defined. Go ahead and comment out that line of code by using the //
tags in front of the statement.
Refresh the window and you should be able to move your player around and collect the coins without trouble. However, if you try to collect the key, you will get an error since sendKeyMessage
has yet to be defined either. This will be handled shortly.
Handle Messages for Your Multiplayer Game
In playState.js, the handleKeyMessages
function will be added below the logCurrentStateCoin
function. This function handles all of the messages that get received by the client. Essentially this function is syncing all the clients so the movements are accurately displayed on the screen.
This gaming function handles all messages coming from other clients that are connected to the game. The function won’t properly work until the multiplayer components are added to the game, but still handles important behavior. The message data checks if the message is equal to the current channel the client is subscribed to. If the client receives a message from someone who is not in the game, a new player is created and their position is set. A message is sent to update all clients about this new player and their position. handleKeyMessages
also checks frame count to make sure all clients are in sync. The messageEvent.message.keyMessage
for the input events of all other users and will update the player state for all clients.
Implement Multiplayer for Your Online Game
You will now be able to implement the multiplayer component of the game by adding PubNub functionality, which allows other players to join the game. In js/main.js, add the following PubNub initialization and event listener functionality after the global variables you and above the JavaScript files you loaded into the scene.
window.createMyPubNub
initializes variables and channel names that PubNub is going to use for network communication. window.currentChannelName
is set to equal the user's current level. The PubNub keys are set up and the subscribed channels are specified. An event listener is added that detects when the browser is unloaded, and a message is sent that a user has left the channel so the presence event updates for all other clients. The globalUnsubscribe
function removes the listener for the client and unsubscribes the client from the channel.
The window.listener
event listener is listening for events every frame but will run on the initial connection status to PubNub, when a message is sent on the channel, or when a presence change occurs.
In the status(status)
callback, messages are sent with request-level information from the KV store. You can learn more about the KV store in PubNub's Functions feature.
In the message(messageEvent)
callback, the message channel name is checked to see if it is equal to the current fire channel name. If true
, window.StartLoading
is called to load the game. Then, if the message channel is equal to the window.currentChannelName,
another player is added to the game and its position is set in the correct location based on the message data.
In the presence(presenceEvent)
callback, if a player joins, leaves, or timeouts the channel, then a call to hereNow
is made that checks to see how many people are in the channel and outputs the current occupancy along with the UUIDs in the channel.
Below the window.listener
event listener that was just added but above the load external JS files code, implement two functions window.sendKeyMessage
and window.fireCoins
. window.sendKeyMessage
will send messages out to all clients connected to the channel. The message will contain player UUID information, position, and frame count. window.fireCoins
will send a message to Functions to inform the function of the current cache state of the user.
Update Functionality & Run the Code for Your Javascript Multiplayer Game
You'll need to go back and uncomment lines of code in other files that allow usage of the recently added functions. At the bottom of main.js, uncomment the following code where the event listener loads the scene.
Navigate to playState.js uncomment window.fireCoins();
in the logCurrentStateCoin()
function. Go down to _handleInput
and uncomment the following.
Finally, in _spawnCharacters
, uncomment window.sendKeyMessage({});
.
Save your files and refresh your window. If you open up two separate windows of the game, you should be able to move your character on one window and see the character move on the other window with low latency.
Make Your Online Multiplayer Game with Functions to Manage Game State
You will notice that if you collect a coin in one window, there will be coins missing on the other player's screen. This functionality is managed by PubNub's Functions feature, which enables you to capture events that are happening on the PubNub Platform.
Navigate back to your PubNub Account and select the application that is powering the game. On the left-hand side of the dashboard, click the Functions box. Click the Create Module button and name it whatever you wish. Then create a new Function and call it what you would like as well. Make sure you select Before Publish or Fire and the channel name is realtimephaserFire2.
Copy the following code into the portal, where you can enter your own JS functionality.
The information saved and processed is the contents of the JSON-level information that was sent earlier. The function essentially determines that if JSON information exists in the Function, publish that information to the newly connected user. If there is no information, use the local JSON information for that client. The only time the JSON updates in the Function is when a coin is collected in the scene by any player.
Now click the + button in the Functions dashboard and create a new function and name it onLeave
except call it only After Presence and on the channel realtimephaserFire2. Add the following code to the function.
This Function will only run when someone joins, leaves, or timeouts a channel. If the total occupancy of the game equals zero, it resets the current level cache to nothing so the coins will appear for anyone new that joins the game.
How to Make an Online Multiplayer Game: What's Next
You've successfully created your own browser-based, multiplayer platform game with PubNub! Phaser, HTML, CSS, and JavaScript were used to build, style, and operate the game itself, while PubNub is used to power the online social component of the game that allows players to interact.
1. See how PubNub can help you attract and retain players with in-app chat, live -leaderboard updates, and mobile push notifications.
2. Explore PubNub's Unity Developer Path, a guided journey in how you can use PubNub's Unity SDK to build real-time games with ease.
3. Play the Unity demo PubNub Prix, a racing game where you can chat with other players in and view leaderboard updates in real time.
4. Learn about how PubNub can help your game in the how-to guides.
5. Follow a step-by-step tutorial to set up and build the Unity game PubNub Prix.
6. Dive into our documentation for the PubNub Unity SDK, to learn how to customize PubNub's features that uniquely fit your application.