Skip to main content

On Sale: GamesAssetsToolsTabletopComics
Indie game storeFree gamesFun gamesHorror games
Game developmentAssetsComics
SalesBundles
Jobs
TagsGame Engines
(1 edit) (+1)

UPDATE 3:

I'm back for another update! Just as expected, yesterday had a lot of other things I had to finish and wasn't able to do much for the game. Today though, I was able to do a lot of stuff. Not everything I hoped to achieve from the last post. But I'm well setup to be able to start implementing those.


So here's the run down:

Today's work was mainly on collision detection. But, given where I left off last time, I didn't have very much to go with. There are a few things I had to consider before I started working on it. 

  • The game is top-down and somewhere down the road, I wanted to put procedural generation of the rooms/homes/neighborhood. but for that to happen, I would need to build the levels by tiles to achieve most of what I want. This may mean that i can have information on tiles where I can say if i'm about to land on this one tile, i can check if that tile is allowed for walking or not. else i revert back to my previous position.
  • If it weren't for tiles, collision detection could probably just be putting boxes around things in the game to which if i intersected with one of them, we can revert to the previous location again.
  • I didn't have a proper level to test it on.

Bottom line, I had to build a suitable level where I can test all my collision. So let's build a level!

Building My First Level

For this game, I've decided that it would be best that my level will be using a tile map. So we need to make a few things for this to work:

  • While this is somewhat optional (since you could technical just hardcode these things inside your code, but it's going to eat so much space in your files, that it's going to be hard for you to read your own code), but I opted to create a file format for my level. I've established in my code that I have a level of some width and height, and that i'm expecting a fixed size for the tiles (64x64 is what i used), and that would help me compute how many tiles I am expecting for the level size.
  • Each of the tiles will have a numerical representation which simplifies my level file to something like this:


01s represent walkable tiles

04 are non-walkable tiles

  • Together with this, I needed some placeholder tiles to represent these. I figured when I was drawing a bunch of squares, I ought to just make some extra ones in case I could use them for something while testing.


  • I decided that when i'm parsing the Tileset, I would go Top-Bottom, Left-Right. With this, you could guess already that 01 which is a walkable tile is going to be brown, and 04 is red for not walkable. 

I now had all the components i need to make a level, So the next I needed to do is to render this on screen. Loading the tileset is simple since i've already been able to load pngs, so nothing new there. loading a text file however, we don't have that functionality yet. Thankfully, C++ has some libraries we can use (fstream). using the library we can call ifstream which conveniently allows to loop over our text file on chunk at a time, so for every ready i do, I takes one tile information at a time, left to right, top to bottom. Rendering just required that we loop as well through the entire list of tiles and place them into their respective positions.


We now have the level loaded in, and we can walk around the level, but it isn't as interesting yet since we're walking through walls. So our next task is to be able to stop our player from going through walls. 

Implementing Collision Detection:

I had several iterations for the collision detection code, I'll go through each of them and explain how I got to what I now have as a working collision detection for my player. 

As mentioned in the previous part, I've defined some properties for the tiles and in this case only 1, which is walkable. so 01 (brown) tiles are walkable and 04 (red) are not.  I needed to know though if i'm at a tile that's walkable or not. We can represent a tile as Tile(x,y) where x is its column number and y is its row number. so if my player is at (0,4), Tile(0,4) is a 04 Tile, which means you cannot move into that space, cancel the movement update and revert back to your previous position. Since my Tiles are represented in a 2 dimensional array, this was easy for me to check. But this inherently has a problem. How do I move smoothly from one tile to the other? noticed that we're using whole numbers (i.e. (0,0) (0,1)) to represent the tile location. If i make my movement like that as well I'll have a grid-based movement which would jump from one box to the other.


I can probably smoothen it out by some lerp (linear interpolation) function so that it moves nicely from one tile to the other. but it feels clunky. For a game that you may be racing against the boogeyman, you want crisp and responsive controls. So this won't do at all! Thankfully there is a way to still know where you are in the tile map but still have free movement. In order for us to do this we need to understand that SDL2's origin point is not at the center. it's at the upper left corner, meaning to say if I say an object is at position x, y what that  means is that the upper left tip is at position x,y


We know that each tile has an area of 64x64. All we need to figure out is whether the point is in a walkable tile or not. After some trial and error, I found a way to compute this to allow me to freely move around the level:


Dividing the target position i wanted to move into by the scale ( in our case its 64 because tile size is 64) allows us to get our whole number notation of what tile we fall into. so it's a matter of now just querying our array if it's walkable or not. Problem solved!


WELL! almost! turns out i forgot to account for the origin. so i also have to check the other corner depending on the direction. 


But I ran into another problem. what if i was in the middle of two tiles and i moved in half with an unwalkable tile and the other walkable? what do you know... It goes through! This was not acceptable! I tried debugging and couldn't get it to work. I needed some help. I consulted the web and some books. I learned of something called AABB collision detection (Axis aligned bounding box). and its method is pretty simple. Given two rectangles that are aligned in both x and y axes, we just check if any of their axis is not intersecting, if both are intersecting, then we have a hit! this wasn't all that hard to do so i went to work at it and got my desired result!


I have my free movement and i'm not going through any walls! finally! While my solution is working, it's worth noting that I had a trade-off here for anyone that may choose to do it the way I did. For this method, I had to iterate through the whole tileset and check whether i get a collision or not. 

int Collided = 0;
      for(int i = 0; i < TOTAL_TILES; ++i)
      {
        if(!Pieces[i].IsWalkable)
        {
          SDL_Rect A = {(int)Player.X, (int)Player.Y, Player.Width, Player.Height};
          SDL_Rect B = {Pieces[i].Location.x * TILE_SIZE, Pieces[i].Location.y * TILE_SIZE, TILE_SIZE, TILE_SIZE};
          if(TestCollision(&A, &B))
          {
            Collided = 1;
            break;
          }
        }
      }
int TestCollision(SDL_Rect *A, SDL_Rect *B)
{
  if((A->y + A->h) < B->y)
  {
    return 0;
  }
  if(A->y > (B->y + B->h))
  {
    return 0;
  }
  if((A->x + A->w) < B->x)
  {
    return 0;
  }
  if(A->x > (B->x + B->w))
  {
    return 0;
  }
  return 1;
}

I kind of made it efficient in the sense i only dealt with unwalkable tiles. but If i was able to make the first method work, I only would have to worry about 1,2, or 3 tiles only. But for my case for this game, and the two week period i only have to make this happen, I think it's an acceptable trade-off. Also another undesirable effect of this method is well you get snagged at corners, you'll have to fully slide of the corners. Again, a fine trade-off for this jam. But if i have more time, I can continue to improve this. We shall see. there's still more to do. Lastly, as the method suggests, I can't do any rotations. It's fine though, because i can just swap a diagonally moving sprite image. So in my case, an okay trade-off.


Now that i have some form of collision detection, I can move on to working on interactions with other things in the level.


Till the next update! Good night!