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

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


The Attribute of the Strong (Battle for the Solar System, #3)

The Pandoran War is nearing its end... and the Senate's Mistake have all but won. Leaving a galaxy in ruin behind them, they set their sights on Sol and prepare to finish their twelve year Mission. All seems lost. But in the final forty-eight hours, while hunting for the elusive Zackaria, the White Knights make a discovery in the former Mitikas Empire that could herald one last chance at victory.

Click here to learn more and read an extract!

« Back to tutorial listing

— 2D Shoot 'Em Up Tutorial —
Part 3: Moving the player

Introduction

Note: this tutorial builds upon the ones that came before it. If you aren't familiar with the previous tutorials in this series you should read those first.

In the first tutorial, we learned how to open a window in SDL2. We're now ready to do something a little more interesting: moving the player. Extract the archive, run cmake CMakeLists.txt, followed by make to build. Once compiling is finished type ./shooter03 to run the code.

A 1280 x 720 window will open, coloured a light shade of blue. A spaceship sprite will also be shown, as in the screenshot above. The ship can now be moved using the arrow keys. Up, down, left, and right will move the ship in the respective directions. Close the window by clicking on the window's close button.

Inspecting the code

There have been a number of changes to the code to allow us to move the player about the screen. Let's start with structs.h.


typedef struct {
	SDL_Renderer *renderer;
	SDL_Window *window;
	int up;
	int down;
	int left;
	int right;
} App;

We've added four new integers: up, down, left, and right. These variables will track our movement requests. When we push the arrow keys on the keyboard we'll set the relevant variable value. In order to facilitate this, we need to make a few changes and additions to input.c:


void doInput(void)
{
	SDL_Event event;

	while (SDL_PollEvent(&event))
	{
		switch (event.type)
		{
			case SDL_QUIT:
				exit(0);
				break;

			case SDL_KEYDOWN:
				doKeyDown(&event.key);
				break;

			case SDL_KEYUP:
				doKeyUp(&event.key);
				break;

			default:
				break;
		}
	}
}

The first change is that we've now added two additional case statements to handle new SDL event types - one to handle SDL_KEYDOWN and the other to handle SDL_KEYUP. These two event types will occur whenever a key on the keyboard is pushed down and whenever one is released. The doKeyDown or doKeyUp functions will be called whenever this happens, passing the SDL_KeyboardEvent that triggered this along. Let's look at the doKeyDown function.


void doKeyDown(SDL_KeyboardEvent *event)
{
	if (event->repeat == 0)
	{
		if (event->keysym.scancode == SDL_SCANCODE_UP)
		{
			app.up = 1;
		}

		if (event->keysym.scancode == SDL_SCANCODE_DOWN)
		{
			app.down = 1;
		}

		if (event->keysym.scancode == SDL_SCANCODE_LEFT)
		{
			app.left = 1;
		}

		if (event->keysym.scancode == SDL_SCANCODE_RIGHT)
		{
			app.right = 1;
		}
	}
}

Glacing at this, we can see that we're testing the scancode of the keyboard event to see which key it was (there are A LOT of scan codes available. A full list can be found here: https://wiki.libsdl.org/SDL_Scancode). Whenever we detect an SDL_SCANCODE_UP scancode, we set the app.up variable to 1. app.down, app.left, and app.right will be set as per the SDL_SCANCODE_DOWN, SDL_SCANCODE_LEFT, and SDL_SCANCODE_RIGHT scancodes. That should be quite easy to understand. One thing we have done is tested to see whether or not the keyboard event that was sent was a result of a keyboard repeat event (event->repeat). In the main, we want to ignore keyboard repeat events; these could queue up and cause some unexpected things to happen, and so we only want to deal with events where the key has been pushed down for the first time. Now let's look at doKeyUp:


void doKeyUp(SDL_KeyboardEvent *event)
{
	if (event->repeat == 0)
	{
		if (event->keysym.scancode == SDL_SCANCODE_UP)
		{
			app.up = 0;
		}

		if (event->keysym.scancode == SDL_SCANCODE_DOWN)
		{
			app.down = 0;
		}

		if (event->keysym.scancode == SDL_SCANCODE_LEFT)
		{
			app.left = 0;
		}

		if (event->keysym.scancode == SDL_SCANCODE_RIGHT)
		{
			app.right = 0;
		}
	}
}

As you can see, it's pretty much the same as doKeyDown, except that we're now setting the app.up, etc. variables to 0. Finally, let's turn to main.c where we'll be dealing with the player movement.


int main(int argc, char *argv[])
{
	memset(&app, 0, sizeof(App));
	memset(&player, 0, sizeof(Entity));

	initSDL();

	atexit(cleanup);

	player.texture = loadTexture("gfx/player.png");
	player.x = 100;
	player.y = 100;

	while (1)
	{
		prepareScene();

		doInput();

		if (app.up)
		{
			player.y -= 4;
		}

		if (app.down)
		{
			player.y += 4;
		}

		if (app.left)
		{
			player.x -= 4;
		}

		if (app.right)
		{
			player.x += 4;
		}

		blit(player.texture, player.x, player.y);

		presentScene();

		SDL_Delay(16);
	}

	return 0;
}

As you can see, we've added a few tests of the app.up et al. variables. When these are set to 1, the player x and y will be updated. app.up and app.down will add and subtract 4 from the player y coordinate. app.left and app.right will do the same to the player x coordinate.

Now, at this point you're probably wondering why we're using these app.up etc. variables, instead of moving the player in the doKeyDown function in input.c. The answer to this is simple: those events fire only once when the key is pushed down (or a few more times if we're considering repeat events). This means that in order to move the player the arrow keys would need to be tapped constantly to make the ship cross the screen; keyboard repeat events are even worse in this case, as it leads to jerky movement all over the place. This isn't what we want. Setting the app.up family to 1 when a key is pushed down and 0 when the key is released means that the relevant key need only be held down to keep the player moving and released when we want the player to stop.

We've ticked off a few essentials on our road to creating a shooter - we can draw graphics and we can respond to player input. The next step will be to add the ability to fire bullets so that the player can defend themselves from the waves of enemies we will also soon add.

Exercises

  • Increase or decrease the speed at which the player moves.
  • Restrict the player to the bounds of the screen.

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