Megalaga 2 - Entities

Posted February 24, 2020

NOTE:
This tutorial is most likely not compatible with versions of SGDK above 1.70. Unfortunately, I simply do not have the capacity or ability to update them right now. Read more about it here. Sorry.

Hold up!

If you like 90s platformers, check out my new game Kid Bubblegum!

Alright, last time we created some scrolling space. But we’re not making a screen saver here! We’ll need entities to populate our game, entities to shoot with and entities to shoot at.

We could define all of those manually, which would look a little something like this:

int player_x;
int player_y;
int player_velx;
int player_vely;
int player_health;

int enemy1_x;
int enemy1_y;
int enemy1_velx;
int enemy1_vely;
int enemy1_health;

int enemy2_x;
int enemy2_y;
int enemy2_velx;
int enemy2_vely;
int enemy2_health;

int enemy3_x;
int enemy3_y;
int enemy3_velx;
int enemy3_vely;
int enemy3_health;

//And so on and so on...

That’s so long and dry that you probably scrolled past without reading all of it. And that’s understandable, obviously: We’re gonna have a lot of entities in our game, which would mean hours of typing, making typos, fixing typos and making new typos. But of course there is a better way!

In a modern programming language (well, even C++ is over 30 years old at this point but you know what I mean) we would use classes to instantiate objects, which would make things a lot easier than typing out all the variables for all entities. But C doesn’t have classes… so we’ll use the next best thing: structs. Structs group items of (potentially) different types into one big type. For example, we will create a struct of type Entity, which will contain our variables for the position, velocity, health etc. of our entities. This makes it a lot easier to handle everything. So let’s define it right now at the top of main.c!

typedef struct {
	int x;
	int y;
	int w;
	int h;
	int velx;
	int vely;
	int health;
	Sprite* sprite;
	char name[6];
} Entity;

This defines a struct of type Entity. Each variable of type Entity that we create will contain its own variables for its position, velocity and health. We’re also storing the width and height of a sprite (w and h respectively) as well as the actual sprite that will be drawn on screen. Finally, we’ll also be storing a name for each entity, which we won’t actually use but which is useful for debug purposes.

Now let’s use this struct to construct a player entity. After the above definition, add this line:

Entity player = {0, 0, 16, 16, 0, 0, 0, NULL, "PLAYER" };

As you can see, we’re creating a new variable called player of type Entity. And we initialize it immediately using the bracket notation. You can type whatever you want for the name (as long as it is six characters long). We point the Sprite pointer to NULL for now as we’ll be adding the sprite in the next step.

Now we need to put the player on screen, but for that we’ll need an actual sprite to draw. And for that, we’ll need an image to import.

Importing the Graphic

Download the image using the link below:

Download player image

If you unzip the file and take a look, you’ll note that the image contains multiple sprites across two rows. That’s because this is a fancy animated sprite! Create a new folder in res called sprites, then put the image in there. Open up your resources.res file and import the image by adding this line:

SPRITE  ship    "sprites/imgship.bmp" 2 2 FAST 6

We’re importing the image under the generic name ship, we’ll find out why later. We’re telling the game that one sprite is 2 tiles wide and 2 tiles high, so that it automatically generates the animations for us. Each row of the image is one animation. We’re using FAST compression here and set the framerate of the animations to 6.

Now that we’ve got our image, let’s put the player on screen!

Finishing the player

In main(), add this chunk of code after the code dealing with our tiles:

SPR_init();

/*Add the player*/
player.x = 152;
player.y = 192;
player.health = 1;
player.sprite = SPR_addSprite(&ship,player.x,player.y,TILE_ATTR(PAL1,0,FALSE,FALSE));

First we initialize the sprite engine. Then we modify the variables in the player struct. To access the items in a struct, we use the dot notation as you can see. We’re setting the starting position of the player (in the horizontal center of the screen near the bottom edge) as well as its health. For this game, we’ll say that a health value of 1 means an entity is alive, and a value of 0 means that it’s dead. In a game with health bars you would of course use more values, but we’re just making a simple shooter.

Finally, we’ll add the sprite graphic to the entity using the usual SPR_addSprite function. We’ll use PAL1, the same palette the background uses. Finally we’ll tell SGDK to update the sprite engine twice: Once directly after adding the player sprite, then again at the end of the game loop before SYS_doVBlankProcess().

//...
player.sprite = SPR_addSprite(&ship,player.x,player.y,TILE_ATTR(PAL1,0,FALSE,FALSE));
SPR_update();

while(1){
	//...
	SPR_update();
	SYS_doVBlankProcess();
}

If you compile the game now you should see our player ship flying across the void of space using its animated booster!

images/player_added.gif

Looks good, doesn’t it? Now let’s prepare one final thing to help us down the line. We’ll write two functions that will help us deal with all the entities we’ll be creating. Add these before main():

void killEntity(Entity* e){
	e->health = 0;
	SPR_setVisibility(e->sprite,HIDDEN);
}

void reviveEntity(Entity* e){
	e->health = 1;
	SPR_setVisibility(e->sprite,VISIBLE);
}

As their names imply, these functions will kill and revive entities, respectively. They do this by setting the health value of the entity to 0 and by hiding their sprite using the SPR_setVisibility function. It’s all rather self-explanatory but will help us to save some time down the road.

Structs are a powerful tool to organize code and they will come in really handy when we’re dealing with multiple enemies and bullets. But that’s a topic for next time! Until then be excellent to each other!

If you've got problems or questions, join the official SGDK Discord! It's full of people a lot smarter and skilled than me. Of course you're also welcome to just hang out and have fun!

Join my Discord Server!

Hang out, get news, be excellent!

Come hang out!

Want To Buy Me a Coffee?

Coffee rules, and it keeps me going! I'll take beer too, though.

Check out the rest of this tutorial series!

  • Megalaga 1 - Space
  • Megalaga 2 - Entities
  • Megalaga 3 - Enemies
  • Megalaga 4 - Enemy Movement and Input
  • Megalaga 5 - Bullets
  • Megalaga 6 - Collision and HUD
  • Megalaga 7 - Enemy Bullets
  • Megalaga 8 - Spam Protection
  • Megalaga 9 - Sound
  • Megalaga BONUS - Powerup
  • By using the Disqus service you confirm that you have read and agreed to the privacy policy.

    comments powered by Disqus