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
2D isometric game
2D map editor
2D mission-based shoot 'em up
2D Santa game
2D split screen game
SDL 1 tutorials (outdated)

Latest Updates

SDL2 Versus game tutorial
Wed, 20th March 2024

Download keys for SDL2 tutorials on itch.io
Sat, 16th March 2024

The Legend of Edgar 1.37
Mon, 1st January 2024

SDL2 Santa game tutorial 🎅
Thu, 23rd November 2023

SDL2 Shooter 3 tutorial
Wed, 15th February 2023

All Updates »

Tags

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

Books


Project Starfighter

In his fight back against the ruthless Wade-Ellen Asset Protection Corporation, pilot Chris Bainfield finds himself teaming up with the most unlikely of allies - a sentient starfighter known as Athena.

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 cmake CMakeLists.txt, followed by 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:

Mobile site