Lisp Game Jam Log #6

Screenshot of a game level with several characters. The characters have colored lines drawn around and over them. Today I started on narrow phase collision detection. I also made many "under the hood" improvements to the code. And, I decided to simplify the game in order to save time.

First I'll write about narrow phase collision detection (NPCD). In my last post, I wrote about broad phase collision detection (BPCD), in which the goal is to quickly detect possible collisions between an entity and a tile, or between one entity and another entity. After BPCD has narrowed down the possible collisions, NPCD performs more precise mathematical checks to find out whether or not a collision is actually occurring.

Zoomed-in screenshot of a character with colored lines drawn around and over him. The approach I'm using for entity NPCD is for each entity to have four "hitboxes", smaller rectangles that are positioned on each side: left, right, top, and bottom. (The hitboxes are drawn in green in this screenshot, and the overall bounding box is drawn in red.) I can then check each hitbox to see whether it is colliding with a tile or other entity. Depending on which of the four hitboxes is colliding, the program will be able to calculate in which direction the entity should move in order to stop colliding. This can also be used for gameplay purposes. In many platformer games, the player will be injured if she touches an enemy with her left, right, or top hitboxes, but she will defeat the enemy if she touches it with her bottom hitbox (i.e. her feet).

Also today, I made several improvements under the hood related to coordinate spaces. The biggest improvement is that I established a distinction between "worldspace" — the coordinate space in which the gameplay occurs — and "screenspace" — the pixels on the screen. (There is also "gridspace", the rows and columns of the tile grid. One unit in gridspace equals 21 pixels in screenspace.)

Before, worldspace and screenspace were the same: one pixel on the screen was the same as one unit in the game world. In order to avoid choppy, imprecise movement, I implemented my own bounding box type that used floating point numbers, and I used floating point numbers for the position, velocity, and acceleration of entities.

But, my bounding box type was essentially the same as SDL's rect type, except that my type used floats and SDL's type strictly uses integers. I found myself duplicating a lot of the functionality of rects, as well as procedures for converting from one type to the other. I decided that it would be better to just use SDL's rect and point types directly. This also has benefit of allowing me to easily use SDL's rect and point intersection functions.

In order to work around the fact that SDL's rects and points use integers, I decided to define worldspace as being 1000 times the scale of screenspace. In other words, one pixel on the screen is equal to 1000 units in the game world. That means I get milli-pixel precision for movement and collision detection, while be able to use SDL's functions, and have better performance because integer math is faster than floating point math.

As a future optimization, I might change the scale to 1024, so that I can use bit-shifting to convert between worldspace and screenspace. But for now, having a round 1000 makes it easier for me to interpret worldspace coordinates in my head while debugging.

I wrote a bunch of procedures for working with SDL rects and points. These are generally useful, so I put them in a shared directory so that I can use them in future example games. I am thinking about maybe adding some of them to chicken-sdl2. The fixnum procedures could be implemented in C for efficiency.

Finally, I have decided to simplify the game concept, in order to save time. Physics and collision detection have already used up three days, and probably the next day or two as well. It would take even longer if I tried to implement slopes, ladders, and water. So, I have decided to postpone those elements of the game until after the jam. Instead, I'm only going to have solid rectangular platforms that you can jump around on. Rectangular platforms will make collision detection and resolution simpler.

Tomorrow I hope to finish up collision detection and resolution for entities and tiles. I probably will not have player-to-player collision detection for this jam, but will add it later, after the jam.

Previous post: Lisp Game Jam Log #5 Next post: Lisp Game Jam Log #7