I have one month to make an MMO: Day 8
Today was the first time the game gained an ability for players to actually begin to see each other on the map, which is always an exciting time for multi-player game development. It means that most of the systems are talking to each other.
I also had to spend a couple of hours today fixing the website since an update seems to have broken an ability to register and set the display name.
Let’s go over a few key features achieved today:
Doors and warping
It turned out to be pretty easy to add the ability for a player to be transported between regions of the map, it was a simple case of adding an object to the Tiled maps, and add in the target location that a player would be sent to upon touching the door.
Once this was done, a corresponding obj_warp object was created in the GameMaker project, which the obj_player checks for collisions against, and performs the warp. At some point I need to add some server-side validation of this warp, to ensure that players only warp when they’re allowed to, but I’ll leave that for later.
Movement update
Here’s an interesting thing. There’s a couple of different methods for handling “pixel perfect” collisions in GameMaker, the most common one I’ve seen is this one:
What might not be immediately obvious is this code has a bug in it. Due to how gamemaker rounds coordinates when doing collision checks, there is an unhandled edge-case in this code where if the object is about 1px inside of a wall (which can happen due to things like collision masks changing), and when the speed is low. In this edge-case, the if
check correctly evaluates as true, but the subsequent while
loop, which was designed to shift the object up against the colliding wall one pixel at a time, doesn’t work as expected and either causes an infinite loop/game freeze, or cause the object to teleport across the room.
To resolve this issue, a variant of this code can be used:
In this example, the while
loop has been replaced by a repeat
loop, which guarantees that it’ll never enter an infinite loop, and at maximum move the object an amount it would have moved anyway without the edge-case condition.
In my case however, my tile collision scripts are slow, so I want to do as few tile checks as possible. So I’ve used FC’s version but combined the X and Y axis checks into a single check along the movement axis.
The benefit of this is it makes about half the number of collision checks as the other methods. It works in the same way as FC’s version, but rather than check movement in the two axes independantly, I simply form a unit vector from the object’s trajectory, and perform a check along that axis.
The results should be similar enough, but needing half the number of collision checks.
Seeing other players
Recall that we did a lot of work last sprint with getting the RPC system and Redis hooked up. This wasn’t for nought; today it allowed me to quickly hook up sending movement updates from the client to all other clients. This is the basis for how players will see and interact with each other and other entities in the game. The process goes something like this:
- The player’s client periodically (at 10Hz currently) sends position updates to the player node they’re connected to.
- The player node publishes this to the relevant chunk’s broadcast channel on Redis (every chunk has a broadcast channel that should be used by entities on the chunk to send out updates about themselves).
- Other player nodes all subscribe to broadcasts from the chunks around their own players, so will receive the broadcast if nearby.
- The player node collects all updates, and then periodically (at 10Hz currently) sends all of the information of entity updates to the client.
The results look a bit like this:
There’s some serious lag between when one client sends the movement data and the other side receiving it, there’s a couple of reasons for this:
- High Ping. I’m in London, and I set up the server in the US east coast; the ping time from me to there is 100ms round-trip. Meaning even without data processing, it takes a whole 100ms for data from my machine to reach there, and the reply to come back.
- Low update rate. I’ve set the game’s update rate to 10Hz across the board. Which means updates are only sent once every 100ms. There’s some buffering happening on the server-side, where it accumulates all movement updates received from nearby entities to send to the client at once. This too introduces some delay before data is sent.
As a result, we have somewhere between 200ms and 300ms of latency. I’m not too concerned about a 300ms latency at this point, it’s slow, but the pacing and mechanics in an MMO should mean that it doesn’t require the same kind of tight timings as say a fighting game or an FPS.
Day 8 Task Summary
The two tasks I had planned for today were to get the server sending player updates to every player so that they could see each other on the map. And to support going into doors/warps to get to other locations on the map. I finished these tasks today meaning we’re perfectly on-track, having just finished Sprint 2!
Tomorrow I’ll take some time to plan the next sprint, which will be about NPCs! If I feel up to it, I may also spend a bit of time looking at getting in some sprites for the player so that we’re not a floating diamond shape.