This is a detailed look at the concepts and code for player movement for multiplayer games, part of our comprehensive series on building a JavaScript multiplayer game.
Building a multiplayer game can be a daunting task for most developers since there are complicated design patterns that developers must follow to create a game that operates efficiently. With PubNub, building a multiplayer game is easy since PubNub handles all of the back-end infrastructure, one of the most challenging parts of building an efficient multiplayer game. PubNub guarantees message delivery in less than a quarter of a second and deals with all of the complicated back-end infrastructure. This way, you can focus on building your application with proper design patterns which increases game performance and saves money.
In this post, we'll look at a basic function of building multiplayer games – player movement. We'll use the open source Phaser API and the PubNub JavaScript SDK. We've got a deep dive multiplayer game tutorial here, but in this part, we'll flesh out player movement concepts in greater detail.
In addition, we'll look at how I'm limiting the quantity of messages sent by only publishing information to subscribers on input events. If we were to send messages every time the player's x and y coordinates changed, your device would be sending a message almost every frame. Sending a publish message every frame would slow the performance of the application down and lead to huge costs.
Interpolation for Player Movement
Instead, we only send a publish message when the user presses down a key and send another publish message when the user releases a key. Then on both of the users' screens, we use interpolation to estimate where the player is based on the input events we received. Here's a good description of how interpolation works from the Unity 3D forums:
Interpolation is where we store the state of the last two updates and interpolate between them based on the current amount of time that has passed since the update before last. In this setup, each object must have an associated position and previous_position. In this case, our drawing will represent at worst one update tick behind the current game state, and at best, at the exact same state as the current update tick.
Input Events
Now let's take a look at some code so we can understand how we are handling input events.
_handleInput() { handleKeyMessages(); if (this.hero) { // Added this so we can control spawning of heros if (this.keys.left.isDown) { if (!keyStates.leftIsDown) { window.sendKeyMessage({ left: 'down' }); } keyStates.leftIsDown = true; } else { if (keyStates.leftIsDown) { window.sendKeyMessage({ left: 'up' }); } keyStates.leftIsDown = false; } if (this.keys.right.isDown) { if (!keyStates.rightIsDown) { window.sendKeyMessage({ right: 'down' }); } keyStates.rightIsDown = true; } else { if (keyStates.rightIsDown) { window.sendKeyMessage({ right: 'up' }); } keyStates.rightIsDown = false; } ...
The code above is a snippet from the _handleInput()
function which runs every frame to check if any inputs from the keyboard have occurred.
In the first part of this function (this.keys.left.isDown)
, we check to see if the left arrow key has been pressed down with the function call window.sendKeyMessage({ left: 'down' });
. When the left arrow key has been pressed down, we send a publish message (see below), saying the left key has been pressed down. When the key is released, it sends another message saying the left arrow has has been release (or “up”).
window.sendKeyMessage = (keyMessage) => { try { if (window.globalMyHero) { window.pubnub.publish({ message: { uuid: window.UniqueID, keyMessage, position: window.globalMyHero.body.position, frameCounter: window.frameCounter }, channel: window.currentChannelName, sendByPost: false, // true to send via posts }); } } catch (err) { console.log(err); } };
The above function sends a PubNub Publish with the players UUID
(used to determine which player is sending the message), the keyMessage
which tells the all clients connected to the channel what key the client pressed, the position
of the player, and the frameCounter
which is used for interpolation (discussed above).
That's it! Stay tuned for future posts on multiplayer gaming concepts. If you have any additional questions about input events, check out our full tutorial here You can also reach me at via email with further questions!