Empty Your Mind Tutorial 9

From SwinGame

This tutorial will teach you how to use the SwinGameSDK to develop a simple danmaku game which looks cool. At the end of this tutorial you should be able to use pascal to implement your own space invaders or scrolling shooter style game which makes use of Vectors, animated Sprites, SoundEffects, and Music.

This page contains a Tutorial. Tutorials are designed to walk you through the development of a small game.
Warning: You must have completed the previous tutorial(s) to go through this tutorial

Contents

Preparing A Data Required

In danmaku game, the "hit box" is much smaller than the actual ship. Therefore, we will need an image that defines the player's ship's collision box. The example image can be found here. This data should be stored in the ShipData structure. The new implementation of the ShipData structure follows.

ShipData = record
	theSprite : Sprite;
	speed : Single;
	health : Integer;
	alive : Boolean;
	shootDelay, currentDelay : Integer;
	maxMagazine, currentMagazine, reloadDelay: Integer;
	//Enemy data
	time, offset : Integer;
	direction : EnemyMovement;
	//The sprite data used for the player collision
	theSpriteC : Sprite;
end;
Note: The theSpriteC data will not be used by any enemies.

Do not forget to initialise the theSpriteC in the LoadGame procedure. We will need to synchronise the position of the collision box with the actual ship's position to use the collision box. This should be done after moving the ship's position (i.e. after MovePlayerShip). The code to synchronise the position should be:

player.theSpriteC.xPos := player.theSprite.xPos;
player.theSpriteC.yPos := player.theSprite.yPos;

Collision Detection With Bullets

We will be writing a single procedure that will check a collision with bullets for both the player and the enemies. The following steps must be takes to check a collision between a bullet and a ship.

  1. Find a bullet that is alive
  2. Check if the bullet is against the ship specified. Go back to the step 1 if not.
  3. Initialise the sprite used to check the collision depending on the side of the ship.
  4. Check if the ship and the enemy have collided. Go back to the first step if not.
  5. Subtract the ship's health by the amount of damage the bullet should do.
  6. Kill the ship and play a sound effect if the health is lower than 0.
  7. Kill the bullet collided.

The steps should be repeated until it checks all bullets. The implementation of the steps follows.

procedure ProcessShipCollision(var shipToProcess : ShipData; var game : GameData; shipSide : BulletSide);
var
	i : Integer;
	tempSprite : Sprite;
begin
	for i := 0 to High(game.bullets) do begin
		if game.bullets[i].alive  and not (game.bullets[i].side = shipSide) then begin
			//Initialise the sprite used to check the collision
			if shipSide = Enemy then
				tempSprite := shipToProcess.theSprite
			else
				tempSprite := shipToProcess.theSpriteC;
			if HaveSpritesCollided(game.bullets[i].theSprite, tempSprite) then begin
				shipToProcess.health := shipToProcess.health - game.bullets[i].damage;
				//Kill and play a sound effect if the health is lower than 0
				if shipToProcess.health <= 0 then begin
					shipToProcess.alive := false;
					PlaySoundEffect(game.sounds[1]);
				end;
				//Kill the bullet collided
				game.bullets[i].alive := false;
			end;
		end;
	end;
end;
Note: Do not forget to initialise the player's health and the alive entry or it will not work


This method to check and process a collision needs to know which side the ship is on because the procedure must check if the bullet is not fired by the ship's own side. The sound effect played in the procedure is played when a ship gets destroyed. The sound effect I have used in my game can be found here. The ProcessShipCollision routine should be called after processing the ship and from the UpdateEnemies procedure.

To update the player's ship:

ProcessShipCollision(game.player, game, Player);

To update a enemy's ship, you will need to add a new line after checking if the enemy has gone out of the screen. The new implementation of the UpdateEnemies procedure:

procedure UpdateEnemies(var game : GameData);
var
	i : Integer;
begin
	for i := 0 to High(game.enemies) do begin
		if (game.enemies[i].time <= game.gameTimer) and game.enemies[i].alive then begin
			game.enemies[i].theSprite.xPos := game.enemies[i].theSprite.xPos + game.enemies[i].speed;
			if IsSpriteOffscreen(game.enemies[i].theSprite) then begin
				game.enemies[i].alive := false;
				continue;
			end;
			ProcessShipCollision(game.enemies[i], game, Enemy);
			ShootEnemyBullet(game.enemies[i], game);
			DrawSprite(game.enemies[i].theSprite);
			UpdateSpriteAnimation(game.enemies[i].theSprite);
		end;
	end;
end;

The final thing you need to do to kill the player is to end the game if the player is not alive. Insert the following line into your game loop to quit the game when the player gets hit.

if not game.player.alive then break;

Summary

In this tutorial, I have gone through:

  • Collision detection

The current project files can be downloaded from here.

List Of Tutorials

  1. Introduction
  2. Player Ship
  3. Background
  4. Bullet pt.1
  5. Bullet pt.2
  6. Music and SoundEffect
  7. Enemies pt.1
  8. Enemies pt.2
  9. Collision Detection
  10. Further Extensions