Megatiler 5 - Coins

Posted May 3, 2021

NOTE:
This tutorial is most likely not compatible with versions of SGDK above 1.70. Unfortunately, I simply do not have the capacity or ability to update them right now. Read more about it here. Sorry.

Hold up!

If you like 90s platformers, check out my new game Kid Bubblegum!

Welcome to part 5 of Megatiler! This time we’ll add some coins for players to collect, because it wouldn’t be an adventure game if you couldn’t find valuables scattered about everywhere, right?

We’ll once again use our tile-based system to spawn coins where we want, by simply setting a specific tile. But before we get to that, we’ll actually need a coin sprite! As usual, download it using this link…

Click to download the coin sprite

…extract the image file to your resources folder, then add it to the resources.res file like this:

SPRITE coin "coin.png" 1 1 NONE 20

As you can see from the image, a coin has two frames of animation, so we have to set the frame size, along with the animation speed.

With that out of the way, let’s dive into code. Define this new tile type at the top of main.c, next to SPAWN_TILE:

#define COIN_TILE 6

This tile number will define where a coin will spawn. There’s a reason we’re using 6 here instead of 5; We’ll soon add an exit to our map, and that will also require a tile number. We’ll give the exit the number 5, so that everything starting at 6 can be collectibles! We won’t actually add anything beyond coins in this tutorial, but if you decide to add other items you can just assign them the numbers after 6.

Next we’ll define a new struct that describes our coin. It’s essentially a cut-down version of our Entity struct and we could simply use Entity to create our coins as well. In a real game, that would probably be the best course of action too. However, I think it’s important to get used to working with structs, so we’ll make another one here just for the coins!

typedef struct
{
    Point pos;
    u8 w;
    u8 h;
    Sprite *sprite;
    u8 health;
} Coin;

You’ll see that the variables related to movement are missing, because the coins are just gonna sit around on the floor.

As we’re gonna have multiple coins per level, it makes sense to put them in an array so that we can deal with them more easily. Also, we should have a constant that specifies how many coins there are in a level. So, define these two outside of main():

#define MAX_COINS 3
Coin coins[MAX_COINS];

We’ll just go with 3 coins for now, as our levels are rather small. But since we’ll always be using MAX_COINS in our code, you’ll be able to easily change that number later on!

And that’s it for the setup. Now we have everything we need to spawn coins on our map!

Spawning the Coins

Obviously, we will handle the spawning in loadLevel(). We already have an if-statement checking for the player spawn tile, so let’s add another condition checking for a coin tile:

if (t == SPAWN_TILE){
//...
}
else if(t == COIN_TILE){
//Spawn a coin
}

Here’s how we’ll spawn the coins. Whenever the loop encounters a coin tile, we first check whether we’ve already reached the total number of coins. If not, we grab the next free slot in the array and spawn a coin entity at the correct position. And…that’s it actually! It’s not that complicated. However, we will need a helper variable to keep track of the coin count; and we’ll also need a pointer for the current coin in the array. So, before we start the for loop, define these new variables (after we define x,y and t):

u8 coinNum = 0;
Coin *c = coins;

Now for the actual spawning. The stuff I’ve just explained looks like this in code:

//...
else if(t == COIN_TILE){
    if(coinNum < MAX_COINS){
        c = &coins[coinNum];
        c->pos.x = x * TILESIZE;
        c->pos.y = y * TILESIZE;
        c->w = 8;
        c->h = 8;
        c->health = 1;
        c->sprite = SPR_addSprite(&coin, c->pos.x, c->pos.y, TILE_ATTR(PAL2, 0, FALSE, FALSE));
        coinNum++;
    }
}

Like with the player, we get the correct position in pixels by multiplying the tile coordinates with TILESIZE. A health value of 1 implies that the coin hasn’t been collected yet. It’s important to increment coinNum at the end, because otherwise we would just overwrite the same coin in the array over and over.

We’re almost done now, but there’s still one thing we need to take care of. Like with the player spawn tile, we have to tell SGDK to draw a regular grass tile below the coin; otherwise we’d get a gap! So put this after the closing bracket of the if(coinNum < MAX_COINS) statement:

VDP_setTileMapXY(BG_B, TILE_ATTR_FULL(PAL1, 0, FALSE, FALSE, 1), x, y);

And there we go! Now place up to 3 coins in your map (using the tile index 6), compile the game and marvel at the beauty of gold:

images/coins.gif

Collecting Coins

But coins are no fun if you can’t collect them, so let’s take care of that. We could handle this similarly to the wall collisions and just compare the tile positions of the player and the coins to see if the player has landed on a tile with a coin. However, we’re going to use a more precise approach and use regular AABB collision detection to check for collisions each frame. This has the advantage that we could theoretically create moving coins, and the collision detection would still work, even if a coin is currently between tiles.

As we want to check for collisions each frame, we’ll add the relevant code in the while(1) loop. However, we’ll need a helper variable that we define directly before the loop starts:

Coin* coinToCheck;

As the name implies, this variable will point to the coin we’re currently checking collisions for.

Now let’s get to the actual collision checks. In while(1), after the code dealing with player movement, add this:

u8 i=0;
//Check for coins
for (i = 0; i < MAX_COINS; i++)
{
    coinToCheck = &coins[i];
    if ((player.pos.x < coinToCheck->pos.x + coinToCheck->w && player.pos.x + player.w > coinToCheck->pos.x && player.pos.y <   coinToCheck->pos.y + coinToCheck->h && player.pos.y + player.h > coinToCheck->pos.y) == TRUE)
	{
		
	}
}

We iterate through the coins array and point coinToCheck to the current coin. Then we check if it overlaps the player sprite by comparing their positions. This is a regular AABB check we’ve also used in Megapong.

But what should happen when there is an overlap? For now, we’ll simply make the coin disappear:

if (coinToCheck->health > 0)
{
    coinToCheck->health = 0;
    SPR_setVisibility(coinToCheck->sprite, HIDDEN);
}

Now try it out! Compile the game and collect some cash:

images/collecting.gif

And there we go! Now our player has something to collect. Of course the coins don’t do anything yet; they don’t even give the player points. In the next part we’ll fix that! Until then, be excellent to each other and party on!

If you've got problems or questions, join the official SGDK Discord! It's full of people a lot smarter and skilled than me. Of course you're also welcome to just hang out and have fun!

Join my Discord Server!

Hang out, get news, be excellent!

Come hang out!

Want To Buy Me a Coffee?

Coffee rules, and it keeps me going! I'll take beer too, though.

Check out the rest of this tutorial series!

  • Megatiler 1 - Tiles
  • Megatiler 2 - Spawning the Player
  • Megatiler 3 - Tile Movement (Part 1)
  • Megatiler 4 - Tile Movement (Part 2)
  • Megatiler 5 - Coins
  • Megatiler 6 - HUD, Sound and Making an Exit
  • Megatiler 7 - Loading Levels
  • By using the Disqus service you confirm that you have read and agreed to the privacy policy.

    comments powered by Disqus