As alluded to in last week’s update, my ultimate goal for the week was actually implementing some collision detection, but I got a little side tracked with finishing up the task of sprite sheets and animation lists instead. This week I actually made some strides in this regard, although progress was not as fast as it could have been as I kept trying out various ways to structure the code.
Eventually I settled on how I want things to work, and thus I’m pretty close to a final solution, although no commits have been pushed to the repository as of yet. In fact before I go any further, I should point out that I have started an “official” development branch to allow me to collect changes in preparation for an actual release. The master branch is (as it has always been) the official release branch.
Historically I don’t share code for my own personal projects, and so I tend to not have a lot of branches going. Instead, I have a strict policy of not checking in breaking changes. It’s also my policy to “commit early, commit often” as I find this allows me to focus on incremental changes easier. Now that I’m actually sharing the code with the public (assuming anyone in the public cares) I thought I would be more disciplined in my repository usage, especially as it pertains to anonymous people cloning it and expecting things to actually work.
However, that’s not the topic of this week’s update, collision detection is.
Since the prototype that I’m currently working on is pong, which includes a simple ball and paddles, I thought I would start with the most basic of collision detection, collisions between rectangles and circles. The mechanics of this is pretty simplistic, and currently I have all combinations of circle/rectangle/point collision laid out, but where I got “tripped up” such as it is, was in how to structure the code.
I started off with a simple Collision namespace with functions to do the appropriate math, with methods in the Actor class itself that would do the collision detection. However I found this rather unsatisfactory from a maintenance standpoint. I’m still also not 100% sure if the core update loop will do any sort of collision detection itself or not; currently it does not.
I briefly toyed with several ideas, such as having a collision manager class that would do all of the work or a mix-in type of situation for the Actor class that would make it a collider or not.
In the end I decided on a new base class named Collider. This class contains a position, origin, angle of rotation, bounds and a collision type. There are currently three collision types available: NONE, CIRCLE and RECTANGLE. Simplistically enough, there are only a couple of external facing methods in this class (not counting the properties for getting at the innards of the actual class); renderVolume(), contains() and collidesWith().
The renderVolume method does the same as the Actor.renderBounds method does; the collision bounds of the underlying object are graphically represented, along with a dot that shows where the positioning is relative to. This renders either just a dot, a circle, or a rectangle as appropriate.
The contains method takes a point (or an x, y pair) and tells you if that point falls within the bounding volume of the collider object. For rectangles this is a simple bounds check, while for circles it is a check of the distance of the point to the center of the circle as compared to the radius.
The collidesWith() method is the real meat, which determines if this is a Rect->Rect, Circle->Circle or Rect->Circle collision and invokes an internal method that determines if the two objects collide or not. Here rectangles check if their bounds overlap, circles check if their radii are too far apart, and circles determine their distance to the closest point on the perimeter of the rectangle in order to determine if collisions are happening or not.
My ultimate idea is that this class is the super class of the Actor class, which would extend it (removing the duplicate properties) and thus make all actors officially colliders while keeping the code nice and clean. Better still, for hybrid type actors (such as the paddles in ts-tennis with their rounded ends), the collidesWith() method could be overridden to allow for checking against rounded ends as well as the main body of the paddle, which I think provides a lot of flexibility.
One last thing to add to this API before I merge it over, officially release it and go back to ts-tennis with the changes is the idea of line collision checking. I have a method which determines the intersection point of two lines, but it is not yet a part of the collider interface; I would also like to include simple “does this line intersect this rectangle or circle” bounds checking, which will also come in handy. However, I’m not sure if I’ll get around to that right now or not.
In any case, the ability to collide lines with other lines allows for the beginnings of a nascent “polygon” collision volume, which might come in handy in the future. I’m definitely not going to implement that at the moment, and that’s too far off the flight path even for me.