Time for another blogpost! When looking at the
schedule detailing when you should make a post, it becomes apparent that whoever made the schedule, probably picked the dates very carefully to match certain phases in the project.
The
6 December blogpost(
s) were meant for us to prudently show our baby steps in the world of game design.
20 December was a good time to
chat a little bit about our (probably technical) progress. Next up, 10 January (split up into
1,
2,
3) was just after the holidays, when we had begun to realize the scale of the project we had taken upon us.
And now comes 24 January. Conveniently positioned just before the weekend that's just before our deadline. We are probably expected to write about how much we still have to do in so little time, or show our intelligence by reflecting on our progress. Or maybe write a panic-post about how we'll never get it done.
But here at Graile Games we're different. We carefully constructed an illusion of (almost apathetic) professionalism, and we mean to uphold it, until the bitter end. Because of that, this blogpost will be about boring technical details. You can't
make us write a panic-post!
To the point!
So what
are we going to talk about? Ah, how about the collision checking? Sounds good? Let's do it.
When we began developing Abyssus (or Roguelike, as we called it back then) we had to start from the scratch, and obviously, we'd need a collision system. We could have opted to just blatantly copy Tick Tick's, but this would most likely create more problems than it would solve, since it was made for a platform game, and its pixel-perfect collision checking was what a gamer might call 'overkill'.
The basics
Instead, we decided to make a simple yet effective collision engine using very simple shapes: squares, lines and circles. After all, that's all we needed. We could represent walls and other decoration in the game using squares, players and other entities using circles, and weapons as lines.
Using a set of light-weight methods we were able to implement all the bumps and impacts that were necessary.
Live footage
The aspect 'light-weight' was important, because our game needed to do a lot of collision checks. While this may not seem necessary in a simple topdown game, in the end it was probably for the best we didn't go for pixel-precise collisions. Otherwise we wouldn't have been able to implement the destructible enviroment the game has now, or projectiles hitting each other in mid-air.
Smoooooth
So collision
checking is one thing, but
handling it is another. You can't just do something like:
if(collisionAtCurrentPosition()) { position = previousPosition; }
.. as that would result in the player getting stuck in walls, which is especially a problem in a game with lots of tunnels, like ours. Because of that, we do it a bit differently. The problem with the above system is that it fails to compartmentalize the velocity into components orthagonal with the object we are colliding with, and parallel to it. Let me explain it using this image:
Evidence no. 1 as to why I am not this teams graphical artist
We are in luck because as it turns out, all squares in our game are
axis aligned, which makes collision checking a lot easier! Essentially, first we check whether moving to (position + velocity) will result in a collision. If not, all good! If it does result in a collision though, we can do a seperate checks for the component of the vector that is parallel to the square.
Since the square is
axis aligned we don't have to worry about splitting the velocity, we can just check the X and Y components! One of them has to be parallel! So if adding just the X component, or just the Y component will not result in a collision, we'll do that. The result is that the player slides smoothly along the walls.
Combat
Obviously there are other checks that don't require us to handle collisions like above. For example, when doing checks for the combat, all we have to do on impact is do damage, apply a little knockback, a few gore particles, and that's it.
More live footage
Combat collision checking brought it's own problems though. We can't just check if the distance between the player and an enemy is under a certain arbitrary limit when the player presses the left button, and then apply the damage. After all, the game has to make sense, we need to uphold
suspension of disbelief.
Because of that, the weapons of the player and enemies get
their own collision checks and protocols. This is where the line-circle intersection script you saw above comes in handy: we'll just run it for the circle that represents the player/enemy, and the line that represents the weapon! But here comes the trick: Every weapon is different.
We can't use the same line for the halberd as we use for the dagger, or the same line we use for the mace as for the sword. The 'stick' part of a weapon shouldn't do any damage, only the pointy ends. For that, we used simple dictionaries that 'described' what parts of the weapon actually did the damage (i.e. had to be checked for collisions), and what parts could just be ignored.
Trust me, it makes sense
Maybe you're now sitting there "well that's inefficient" or "it makes no sense to do it that way", but hey, we're just a bunch of rookies, after all.