Part 8: Asteroid Collisions
End of the series for now
It’s been a while since my last post, and this will be the end of the series here with the possibility of coming back to it later. The last couple of months I haven’t worked on this project very much at all - the only thing I really did was put together more collision detection which I will go over here.
I’d like to announce that I will be starting the OMSCS (Online Masters of Science in Computer Science) program through Georgia Tech this fall (starting next week). Because of this, this project will slow down even more, so I figured I will just end it here. I’m really looking forward to this program and I may post some things about what I’ve learned.
I do have some thoughts of returning to this project in the future. I could realistically finish the game in its current state by putting in a simple shooting mechanic and spawning multiple asteroids to fly around. There are some more complex systems that I didn’t get to, such as rotation and scaling, phasing in and out tons of asteroids, flying the player around, and many others that I might come back to.
Back to it
We left off Part 7 with a line colliding with the border of the render window. The logical next step would be to actually get this line colliding with asteroids, and that’s what I took on.
Drawing asteroids in my game currently is simply drawing several lines between a series of points, with the last point connecting back to the first. All of these points are in relation to the Center point of the asteroid, so for moving the asteroid around, all I have to do is move the Center and the rest go with it.
enum asteroid_state
{
AsteroidState_Inactive = 0x0,
AsteroidState_Active = 0x1,
};
struct asteroid
{
v2 CenterP; // 'Center' of the asteroid
v2 StartP; // Start Position of movement
v2 EndP; // End Position of movement
u32 PointCount;
v2 Points[16];
real32 Speed;
asteroid_state State;
v3 Color;
};
Starting out on collisions, I first did a Bounding Box around the asteroid, because I’ve seen that before and assumed this was how it’s always done. This mostly worked, but wasn’t exactly what I wanted.
From here, I just moved straight on to colliding with the lines of the asteroid. At first, this seemed like a huge task. How do I know which line it’s colliding with? How do I calculate the exact collision point? How would I then calculate the angle of the line afterwards? Well, it turns out it’s simpler than I thought.
Really, colliding with asteroids is checking the collision between two lines, and after some research, this has some math calculating just about everything we need.
internal line_collision_result
CheckCollisionLines(v2 A, v2 B, v2 C, v2 D)
{
// Checks collision between two lines (AB and CD)
line_collision_result Result = {};
real32 Denom = (((D.y - C.y) * (B.x - A.x)) - ((D.x - C.x) * (B.y - A.y)));
real32 NumeratorA = (((D.x - C.x) * (A.y - C.y)) - ((D.y - C.y) * (A.x - C.x)));
real32 NumeratorB = (((B.x - A.x) * (A.y - C.y)) - ((B.y - A.y) * (A.x - C.x)));
if (Denom == 0.0f)
{
// Lines are parallel and don't collide
return(Result);
}
real32 Ua = NumeratorA / Denom;
real32 Ub = NumeratorB / Denom;
if (Ua == Ub)
{
// Lines are conincident (they are exactly the same line)
return(Result);
}
if (Ua < 0 || Ua > 1 || Ub < 0 || Ub > 1)
{
// The collision point isn't along either line segment
return(Result);
}
// Collision point
real32 CollisionX = A.x + (Ua * (B.x - A.x));
real32 CollisionY = A.y + (Ua * (B.y - A.y));
Result.IsColliding = true;
Result.CollisionP = V2(CollisionX, CollisionY);
return(Result);
}
However, I quickly ran into a few issues with this..
It may be hard to see because there’s a few things going on here, but should be able to tell that I’m colliding with the asteroid on the right side, but it’s passing through the left side. This is because I was drawing the asteroid starting at the top point working my way clockwise through the points, and that line was the first one checked. Once my collision code found a collision, it exits out instead of iterating over and checking for multiples.
This same issue is shown here, where I have 2 asteroids and it’s only colliding with the ‘first’ line I’m drawing for the asteroids.
To remedy this, I simply added an iteration loop over my collision checking. I’m not sure if this is the best way to go about this, but it pretty much worked.
internal line_collision_result
CheckCollision_LineAsteroid(v2 A, v2 B, asteroid Asteroid)
{
// Result CollisionP is first hit
line_collision_result Result = {};
v2 ClosestP = {};
for (u32 Iteration = 0;
Iteration <= 2;
Iteration++)
{
for (u32 PointIndex = 0;
PointIndex < Asteroid.PointCount;
PointIndex++)
{
v2 P1 = {};
v2 P2 = {};
if (PointIndex != Asteroid.PointCount - 1)
{
P1 = GamePointToScreenPoint(Asteroid.CenterP + *(Asteroid.Points + PointIndex));
P2 = GamePointToScreenPoint(Asteroid.CenterP + *(Asteroid.Points + PointIndex + 1));
}
else
{
P1 = GamePointToScreenPoint(Asteroid.CenterP + *(Asteroid.Points + PointIndex));
P2 = GamePointToScreenPoint(Asteroid.CenterP + *Asteroid.Points);
}
line_collision_result LinesResult = CheckCollisionLines(A, B, P1, P2);
if (LinesResult.IsColliding)
{
Result.IsColliding = LinesResult.IsColliding;
real32 CollisionLength = Length(LinesResult.CollisionP);
real32 CurrentClosestLength = Length(ClosestP);
if (CollisionLength <= CurrentClosestLength || ClosestP == V2(0,0))
{
Result.CollisionP = LinesResult.CollisionP;
}
Result.NewEndP = LinesResult.CollisionP; // TODO(Eric): Calculate the new EndP!!!
}
}
}
return (Result);
}
But, there is still an issue! My collision code is only checking for the closest collision to the center of the screen, so technically screen bounces don’t work properly. To fix this (in the future, this is still broken), I’ll just have to base it on the last point of the line, instead of the first point of the first line (which is the center).
There you have it!
That’s the current state of project and where I’m leaving it for now. I feel like it would be easy enough to finish the ‘game’ part of it as I mentioned before, and I really would like to get back to it someday. If you’ve read this far, I truly thank you for taking the time to go through this project with me. It was a great learning experience and I hope you’ve learned something too.