Skip to content

Enemies

Zalo edited this page Jul 12, 2017 · 9 revisions

Time to add a little bit of action to our game by adding an enemy.

  • Open the Game Boy Tile Designer and create a new 16x16 Sprite for our enemy (View->Tile Size->16x16). This is what I came up with:

enter image description here

Save the file into the res/ folder and Set Export settings (File -> Export To...) as: * FileName: src/enemy.b3.c * Type: GBDK file * Label: enemy * From 0 to 1

Save again

  • Create a new file SpriteEnemy.h in the include folder with the next content:
#ifndef SPRITE_ENEMY_H
#define SPRITE_ENEMY_H

#include "main.h"

DECLARE_SPRITE(SPRITE_ENEMY);

#endif

the macro DECLARE_SPRITE is just a shortcut for including the declaration of Start, Update and Destroy followed by the type of the Sprite (in this case Start_SPRITE_ENEMY, Update_SPRITE_ENEMY and Destroy_SPRITE_ENEMY)

  • Create another file SpriteEnemy.c inside de src folder with the next content:
#pragma bank 2
#include "SpriteEnemy.h"
UINT8 bank_SPRITE_ENEMY = 2;

void Start_SPRITE_ENEMY() {
}

void Update_SPRITE_ENEMY() {
}

void Destroy_SPRITE_ENEMY() {
}
  • In ZGB_Main.h add the new type for the Sprite
typedef enum {
	SPRITE_PLAYER,
	SPRITE_ENEMY,

	N_SPRITE_TYPES
} SPRITE_TYPE;
  • In ZGBMain_Init.c
    • Include "SpriteEnemy.h" and "../res/src/enemy.h"
    • Add its initialization, we stored it on bank 3, saved as 16x16 and created only 1 frame so it should look like this
void InitSprites() {
	...
	INIT_SPRITE(SPRITE_ENEMY,  enemy,  3, FRAME_16x16, 1);
}
  • In StateGame.c create an instance in 70, 50
void Start_STATE_GAME() {
	...
	SpriteManagerAdd(SPRITE_ENEMY, 70, 50);
}
  • If you build the game now you'll see your player and the new enemy. It does nothing and there aren't any collisions yet

enter image description here

  • Write the next code on SpriteEnemy.c
#pragma bank 2
#include "SpriteEnemy.h"
UINT8 bank_SPRITE_ENEMY = 2;

#include "SpriteManager.h"

struct EnemyInfo {
	INT8 vy;
};

void Start_SPRITE_ENEMY() {
	struct EnemyInfo* data = THIS->custom_data;
	data->vy = 1;
}

void Update_SPRITE_ENEMY() {
	struct EnemyInfo* data = THIS->custom_data;
	if(TranslateSprite(THIS, 0, data->vy)) {
		data->vy = -data->vy;
	}
}

void Destroy_SPRITE_ENEMY() {
}

Here you can see how the field custom_data of the Sprites is mean to be used. Also TranslateSprite will return the index of the tile when a collision happens or 0 otherwise. In this case if there is a collision with the background the velocity of the sprite is negated making it changing its direction

  • In order to collide with the enemy you have to add the next code at the end of the method Update of SpritePlayer.c
#include "ZGBMain.h"
...

void Update_SPRITE_PLAYER() {
	UINT8 i;
	struct Sprite* spr;
	...

	SPRITEMANAGER_ITERATE(i, spr) {
		if(spr->type == SPRITE_ENEMY) {
			if(CheckCollision(THIS, spr)) {
				SetState(STATE_GAME);
			}
		}
	}
}

"ZGBMain.h" needs to be included in order to get access to all the sprite types. SPRITEMANAGER_ITERATE is a macro that iterates through all the active sprites, if the sprite is of type SPRITE_ENEMY then we use the function CheckCollision to check if we are colliding with it (do it in this order). If that happens then SetState is called unloading the current state and loading the state passed as paramenter (even if it is the current one, like in this case)

Clone this wiki locally