As I’ve mentioned previously I’m participating in One Game a Month. My January game was Yet Another Space Shooter. My February game is a platform game that doesn’t yet have a name. It’s coming along nicely but I have run into some interesting problems along the way.
As it turns out platform games can be broadly classified into two groups. Tile based and vector based.
In a tile based game the world is internally represented as a big grid. Each cell of that grid can be empty or it can be filled with platform, monster, a player or whatever. Elements, such as the player, may occupy a single cell or they may occupy several, for example being 2 cells wide and 6 cells tall. Typically nothing can occupy less than one cell.
In a vector based game the world consists of a big bag of objects. Each object has a size, a position and a velocity. It’s this big, it’s at this location and it’s moving in this direction at this speed. Due to my experience with Yet Another Space Shooter this was the model that I was familiar with and thus the model I chose. At the time I wasn’t even really aware that most platform games are actually tile based.
Platform games often go for the tile based approach because it makes determining when two objects are touching and whether the player’s movement is blocked quite simple. If the cell directly below the player is occupied by platform then the player is sitting on a platform. If the cell to the right of the player isn’t empty then the player can’t move to the right. Determining this stuff in a vector based world is a little more complex.
Without being aware of what I was doing at the time I compounded this complexity by representing positions in the world as floating point numbers. I’m using Python and in Python, be default, decimal numbers are floats. Thinking back on my time at university I do vaguely recall learning that floating point numbers are approximations and should not be relied upon for precise calculations. I don’t think this has ever come up during my years as a professional computer programmer. Until now.
It finally came up when dealing with interactions between the player and the platforms. Sometimes the player would just fall through a platform for no apparent reason. Sometimes the player would fall into the floor and get stuck. It was all very mysterious.
The problem was, of course, my use of floating point numbers. For the player to be on a platform the gap between the player and the platform must be zero. Not very small, zero. Every once in a while the approximate nature of floating point numbers would cause the player’s position to be slightly below the surface of the platform and the player would then fall through.
This took me quite some time to figure out. That it was a symptom of the computer’s internal representation of decimal numbers was not the first thing that came to mind when debugging this.
Fortunately the fix was actually very simple. While I still store velocities as floats I now store positions as integers. Positions no longer look like (127.00000001, 520.99999999990). Now they look like (127, 520). When the player is sitting on a platform the gap between the player and the platform is now precisely exactly zero.
Once I switched positions from floats to ints all of these weird little events went away. It was pretty magical actually. It’s still nowhere near a finished game but just being able to bounce around the world with confidence that you won’t spontaneously fall into the floor has done wonders. It actually feels like something approaching a proper platformer.
For my next game I think I’m going to try out a tile based platform game. I’d like to see what things are like on the other side of the tile vs vector fence.