Humble Beginnings

Will Gueble

2017/11/26

Categories: Wasteland Tags: Game Design Game Development

I wanted to mark this moment as my first major milestone on the way to a completed game.

Each unique image displayed on screen belongs to a managed game object, with seperate components to handle input, physics, and graphics. In the future, any sound effects or music will be handled by an audio component attached in a similar fashion. The component design pattern was a tip from the Robert Nystrom’s Game Programming Patterns.

The game’s base logic revolves around a modifed Scene Graph design. The entire world is updated during each loop iteration, changing the positions of any objects that will be rendered to screen at the bottom of the loop. The use of a graph design allows for rudimentary space partioning; nearby objects are related hierarchically for both physical and graphical purposes, with the root node managing ownership of the entire world. I found that QuadTree’s and beyond were not necessary to get my prototype off the ground, but I have left space should they become necessary in the future.

The input component serves as a placeholder for my AI logic. Both the player and any NPCs will be controlled by a set of commands owned by each object’s input component.

Physics simulation is handled with the help of the Separating Axis Theorem (SAT). For this reason, I have restricted my design to include only Axis-Aligned objects and Circles. This tutorial proved most helpful in getting my design off the ground. At the present moment, only Axis-Aligned Bounding Boxes (AABBs) and Axis-Aligned Right Triangles have been implemented - despite the player’s circular appearance, it is coded as an AABB. Collisions are resolved by projection along the axis of least overlap.

The following code snippet features my algorithm for detection a collision between two AABBs. The projection vector is added to the colliding object’s position in order to move it out of penetration. Additional collision algorithms can be found on my github.

bool AABBvsAABB(Manifold& m)
{
   AABB_t* a = m.a->asAABB();
   AABB_t* b = m.b->asAABB();

   float dist_x = m.a_p.x - m.b_p.x;
   float a_proj = scalar_proj(a->xw, X_AXIS);
   float b_proj = scalar_proj(b->xw, X_AXIS);
   float overlap_x = a_proj + b_proj - std::abs(dist_x);
   if (overlap_x > 0) { // test for projection overlap along the x-axis
      float dist_y = m.a_p.y - m.b_p.y;
      a_proj = scalar_proj(a->yw, Y_AXIS);
      b_proj = scalar_proj(b->yw, Y_AXIS);
      float overlap_y = a_proj + b_proj - std::abs(dist_y);
      if (overlap_y > 0) { // test for projection overlap along the y-axis
         // project out of collision along the axis of least overlap
         if (overlap_x < overlap_y) { 
            if (dist_x > 0) { 
               m.proj = vec2_t(overlap_x, 0); // x-axis is min
            }
            else {
               m.proj = vec2_t(-overlap_x, 0);
            }
         }
         else {
            if (dist_y > 0) {
               m.proj = vec2_t(0, overlap_y); // y-axis is min 
            }
            else {
               m.proj = vec2_t(0, -overlap_y);
            }
         }
         return true;
      }
   }
   return false;
}

The graphics module leverages the OpenGL API. At startup, I load each objects vertices into a mesh object, rendering with glDrawElements during each loop’s call to update. The application currently runs at 60FPS, but I have yet to test the limits of the engine. The graphics module contains several avenues for performance improvements, but I have postponed that work until the need arises. My textures are encoded in RGBA format - I use the alpha channel in order to render circular objects using two triangles. The screen positions of each object are updated via matrix transformations during each loop’s call to update.

I included this video to discuss a neat trick I used to describe triangles. Any game object’s position field is stored at the highest level of the entity, allowing for it’s use within each separate component. This meant that my hitboxes could not contain a position, to avoid unnecessary calculation. I decided to represent objects by their halfwidth vectors. For AABB’s, this is quite beneficial, as the halfwidth vectors can be used during generalized SAT collision detection. I wanted to store triangles as a pair of legs, in addition to the precomputed third axis used by the SAT (a normalized direction vector). In order to keep these physics objects as lighweight as possible, I wanted to find a way to avoid using any other data fields, such as a direction. To this end, I use the sign of halfwidth vectors to indicate their direction, accounting for this decision in my collision algorithm. The above video demonstrates collision with triangles from several directions.

The code for this project can be found on my github within the ‘Wasteland’ repository.