2022-11-23 | 4 min read
Last week I fixed the ladder issue mentioned in my [first collision detection post]! I was definitely over thinking it, a lot, which I feel is a symptom of having basically copy-pasted and tweaked existing collision detection code from one of the included demos. Checking the ladder stuff was a _lot_ easier than I expected it to be and in the process I've come to a much better understanding of collision detection. So when I have the energy, I'm going to have to go through the collision detection code and make sure I understand it better and fix anything to make it simpler too.
first collision detection post
Something that's really helped me understand a lot of game mechanics, especially as I play around with PICO-8 which is uses a tile-based graphics system, is Youtube videos breaking down how old games implemented their game logic. My two favorite channels are [Displaced Gamers] and [Retro Game Mechanics Explained]. DG has been doing a series recently called _Behind the Code_ that peeks into the actual code of these old games to explain how they do what they do, and sometimes even provides fixes to the code to make things function "better." He recently wrapped up a three part series of the NES _Teenage Mutant Ninja Turtles_ game. I had this game on DOS as a kid and it means a lot to me. It's probably THE most frustrated a game has ever made me. I never could finish it, and his series goes into a lot of the why of that is. The short answer is, the game is broken. Jump physics are bad, hit boxes are terrible, and underwater movement is janky. His explanation and demonstration of the existing state as well as his fixes is just fascinating. It's really obvious too how much better the game would play with just a few code tweaks.
Retro Game Mechanics Explained
I also recently watched two videos from RGME about Pacman, the arcade cabinet. The first was about how math and memory addresses result in the level 256 kill screen bug, and the second was a really fascinating breakdown of the logic behind the way the ghosts operate, including the chase logic for each individual ghost. This kind of stuff has always been super fascinating to me, especially now that I'm really getting into game development. It was actually while watching the [Pac-Man Ghost AI Explained] video that I had my "breakthrough" about how to resolve the issue with ladders. Tile checking is a lot simpler than I initially understood it to be (again, because I directly copied the collision detection code from a demo which also involved acceleration and friction, physics elements my game does not implement, and was designed to handle collisions between two moving bodies as well as walls). I ended up rewriting my ladder detection and movement code pretty drastically.
function is_ladder(actor, dx, dy) if (dx > 0) dx = 1 if (dx < 0) dx = -1 if (dy > 0) dy = .3 -- this is a bit of a magic number :grimace: if (dy < 0) dy = -1 coords = xy_to_level_cel( actor.x + actor.left_offset, actor.y, levels[current_level]) sprite_at = mget(coords.x, coords.y) sprite_dx = mget(coords.x + dx, coords.y + dy + 1) return { fget(sprite_at, 3), fget(sprite_dx, 3) } end
Note: Function xy_to_level_ceL is just some quick little math to convert x and y values to map tile coordinates. PICO-8's screen is 128px horizontal by 128px vertical. Sprites are 8px by 8px. Map tiles are made up of individual sprites, 16 across by 16 down. It takes in the current level so that it can calculate the offset from the position in the tilemap if necessary.
function player_control() -- check for ladder presence ladder_at = is_ladder(player, 0, player.dy)[1] ladder_below = is_ladder(player, 0, 2)[2] ladder_above = is_ladder(player, 0, -2)[2] if (ladder_at) then player.dy = 0 else player.dy = global.gravity player.climbing = false end if (btn(⬇️)) then if (ladder_below) then player.dy = 2 player.climbing = true end end if (btn(⬆️)) then if (ladder_above) then player.dy = - 2 player.climbing = true end end ... end
It's still a little messy, and I can't adjust the ladder climb velocity without breaking the floor collision detection (another reason to re-write that code), but this **works** and it feels so good to climb ladders now. I'm actually really pumped about figuring this out. I was starting to feel a bit dumb about collision detection.
Since then, I've managed to resolve some weird bugs caused by Lua not natively doing deep copies of tables, as well as implemented wall collision detection, power ups with various effects, new enemy types*, new maps, main menu, persistent highscore storage, animation bugs, and more.
* example of new enemy type: this cute little slime boy
I'm actually a bit worried about burn out on this project. I spent a considerable amount of time last spring working on a [UI card] for Home Assistant and then just absolutely hit a wall. I just can't look at that code anymore. I really don't want that to happen here. I want to move on to another game once I feel like this one is ready.
As a side note, I don't know if I will ever get into 3D game development. I haven't even tempted sloped ground in 2D yet, I'm not ready for that extra dimension! That said, I also recently watch a really great video about how the gravity was implemented in Super Mario Galaxy: [How Spherical Planets Bent the Rules in Super Mario Galaxy]. To describe it with one over-used word from this post: it's fascinating.
How Spherical Planets Bent the Rules in Super Mario Galaxy
---