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.
To ensure that multiple players at a time can enjoy a smooth, reliable gameplay experience, PubNub is used to power the infrastructure to communicate this real-time information between these players and other social features to help make online games feel more interactive. Follow along as you learn about how each of these languages and technologies enables you to create your own browser-based multiplayer game.
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
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 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, 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.
Obtain PubNub 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 keys. 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 obtain the publish/subscribe keys necessary to connect to the PubNub network. Please view this how-to guide to learn how to create an application, keyset, and obtain keys.
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
Build and Run Your Online Multiplayer Game: Tutorial
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 should be completely black since there are no assets loaded in the scene yet. The
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.
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 be used to 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
_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.
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.
jump, the properties to determine if the player can jump or not are also set.
update, the sprite animation is updated only if it needs to be changed based on player input.
freeze, the animation of the player going through the door is played.
_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
_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
_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(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.
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
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 to see 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
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.
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.
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.
message(messageEvent) callback, the message channel name is checked to see if it is equal to the current fire channel name. If
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.
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.
window.listener event listener that was just added but above the load external JS files code, implement two functions
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.
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.
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
If you would like to learn more about how PubNub powers games, take a look at our growing collection of updates, demos, tutorials, and documentation.
1. See how PubNub can help you attract and retain players with in-app chat, live -leaderboard updates, and mobile push notifications.
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.