PC Games

Orb
Lasagne Monsters
Three Guys Apocalypse
Water Closet
Blob Wars : Attrition
The Legend of Edgar
TBFTSS: The Pandoran War
Three Guys
Blob Wars : Blob and Conquer
Blob Wars : Metal Blob Solid
Project: Starfighter
TANX Squadron

Android Games

DDDDD
Number Blocks
Match 3 Warriors

Tutorials

2D shoot 'em up
2D top-down shooter
2D platform game
Sprite atlas tutorial
Working with TTF fonts
2D adventure game
Widget tutorial
2D shoot 'em up sequel
2D run and gun
Roguelike
Medals (Achievements)
2D turn-based strategy game
SDL 1 tutorials (outdated)

Latest Updates

SDL2 turn-based strategy tutorial
Thu, 14th April 2022

Water Closet ported to PlayStation Vita
Tue, 4th January 2022

The Legend of Edgar 1.35
Sat, 1st January 2022

Achievements tutorial
Thu, 2nd December 2021

SDL2 Rogue tutorial
Thu, 30th September 2021

All Updates »

Tags

android (3)
battle-for-the-solar-system (9)
blob-wars (9)
brexit (1)
code (6)
edgar (7)
games (39)
lasagne-monsters (1)
making-of (5)
match3 (1)
numberblocksonline (1)
orb (2)
site (1)
tanx (4)
three-guys (3)
three-guys-apocalypse (3)
tutorials (10)
water-closet (4)

Books


The Third Side (Battle for the Solar System, #2)

The White Knights have had their wings clipped. Shot down and stranded on a planet in independent space, the five pilots find themselves sitting directly in the path of the Pandoran war machine as it prepares to advance The Mission. But if they can somehow survive and find a way home, they might just discover something far more worrisome than that which destroyed an empire.

Click here to learn more and read an extract!

« Back to tutorial listing

— Creating an in-game achievement system —
Part 2: Integrating with gameplay

Note: this tutorial assumes knowledge of C, as well as prior tutorials.

Introduction

Now that we have a system whereby we can award Medals to players for performing certain tasks, we should take a brief look at how to incorporate it into gameplay. It's really quite simple, as we'll see. In this part, we won't be looking at all the gameplay code (entity management, collisions, etc), but only those aspects that touch on the Medals integration.

Extract the archive, run make, and then use ./medals02 to run the code. You will see a window open like the one above, showing a little blue alien and a host of floating batteries. The goal is to collect all the batteries. Guide the little alien around using the WASD control scheme. Medals will be unlocked as you pickup the batteries, for lots of 1, 10, 20, and 25. The Ruby medal is awarded for collecting all the other medals. Close the window to exit.

Inspecting the code

Incorporating the Medal code into our game is very straightforward; we saw in the first part how unlocking a Medal is simply a case of calling one function, which is largely what we'll be doing here. We'll be tracking the number of batteries collected by use of a stat.

Starting with defs.h:


enum {
	STAT_BATTERIES_COLLECTED,
	STAT_MAX
};

We've added an enum to handle our stats. We've only got the one - STAT_BATTERIES_COLLECTED, which will be used to track the number of batteries picked up.

Moving onto structs.h now, we've updated the Game struct:


typedef struct {
	int stats[STAT_MAX];
	Medal medalsHead;
} Game;

We've added a field called `stats`, which is an array of ints, of STAT_MAX size. This array will be used to track our stat values (of which we only have one right now).

There are quite a few files in this project now: entities.c, entityFactory.c, player.c, etc. However, these are irrelevent to our Medal and stat tracking, so we won't be talking about them (if you're not familiar with the other tutorials where the game engine style is created, it is recommended you go back and read all those). Instead, we will focus on batteries.c, and the `touch` function in particlar:


static void touch(Entity *self, Entity *other)
{
	if (other == stage.player)
	{
		game.stats[STAT_BATTERIES_COLLECTED]++;

		self->dead = 1;

		playSound(SND_BATTERY, 0);

		if (game.stats[STAT_BATTERIES_COLLECTED] == 1)
		{
			awardMedal("batteries1");
		}
		else if (game.stats[STAT_BATTERIES_COLLECTED] == 10)
		{
			awardMedal("batteries10");
		}
		else if (game.stats[STAT_BATTERIES_COLLECTED] == 20)
		{
			awardMedal("batteries20");
		}
		else if (game.stats[STAT_BATTERIES_COLLECTED] == 25)
		{
			awardMedal("batteriesAll");
		}
	}
}

We're checking to see what (`other`) has touched the battery (`self`). If it's the player, we're going to increase the value of our STAT_BATTERIES_COLLECTED stat, flag the batttery as dead, and play a sound. After that, we're going to test the value of the STAT_BATTERIES_COLLECTED stat. If it's 1, we'll call awardMedal, passing over "batteries1", to unlock that Medal. If it's 10, we'll call awardMedal and pass over "batteries10". For 20 and 25, we'll pass over "batteries20" and "batteriesAll" to the function. So, as we collect more batteries, we're unlocking more Medals.

That's it for batteries.c. As you can see, it's very simple to unlock the Medals.

If we quickly turn to stage.c, we see how we setup the gameplay demonstration.

Starting with initStage:


void initStage(void)
{
	memset(&stage, 0, sizeof(Stage));

	initEntities();

	addEntities();

	app.delegate.logic = logic;

	app.delegate.draw = draw;
}

We're memsetting `stage`, calling initEntities, addEntities, and assigning the logic and draw delegates to the `logic` and `draw` functions found in this file.

addEntities is next:


static void addEntities(void)
{
	int i;
	Entity *e;

	e = initEntity("Player");
	e->x = SCREEN_WIDTH / 2;
	e->y = 32;

	for (i = 0 ; i < 25 ; i++)
	{
		switch (rand() % 3)
		{
			case 0:
				e = initEntity("BatteryFull");
				break;

			case 1:
				e = initEntity("BatteryMedium");
				break;

			default:
				e = initEntity("BatteryLow");
				break;
		}

		e->x = rand() % (SCREEN_WIDTH - 64);
		e->y = 64 + rand() % (SCREEN_HEIGHT - 128);
	}
}

Here, we're creating a Player and placing them at the top of the screen, in the middle. We're then creating 25 batteries, of random type, and placing them in random locations about the screen.

Our `logic` function is simple:


static void logic(void)
{
	doEntities();
}

We're just calling doEntities, to process our entities (which can be found in entities.c).

Likewise, our `draw` function makes one single call:


static void draw(void)
{
	drawEntities();
}

We're calling drawEntities, to draw our entities (again, found in entities.c).

And that's it for this part. As you can see, incorporating our Medals into gameplay is very easy. What we'll look at in our next part is saving our progress, so that we can return to a game in progress, and continue with collecting our Medals.

Purchase

The source code for all parts of this tutorial (including assets) is available for purchase:

From itch.io

It is also available as part of the SDL2 tutorial bundle:

Directly

If you do not wish to create an itch.io account, you can also purchase the tutorial bundle using PayPal, and then download the tutorials directly from the main tutorials page.

SDL2_Tutorials.tar.gz 56.76MB 23rd April 2022

Click here to see the list of files in the archive

Mobile site