Using C-Structs [enemies.h / enemies.c]

📘 FULL TUTORIAL: How Your Enemy System Works

Your enemies.c and enemies.h files form a basic enemy manager:

Let’s break everything down clearly and logically.


🧩 1. The Header File (enemies.h)

The header establishes what the rest of the game can access from this enemy system.

1.1 Including SGDK and resources

#include <genesis.h>

#include "res_enemies.h"

You import:


1.2 Header guard

#ifndef ENEMIES_H

#define ENEMIES_H

Prevents double-including this file.


1.3 Enemy structure definition

typedef struct Enemy

{

    u8 status;

    fix32 x;

    fix32 y;

    fix32 vel_x;

    Sprite* sprite;

    s8 health;

    const SpriteDefinition* graphic;

};

This struct describes one enemy:

🏷 Structure fields

Field

Meaning

u8 status

0 = inactive, 1 = active

fix32 x / y

fixed-point position of the enemy

fix32 vel_x

horizontal movement speed

Sprite* sprite

pointer to the SGDK sprite instance

s8 health

hit points

const SpriteDefinition* graphic

which sprite graphic to use

This means every enemy has:


1.4 Declaring global variables

extern struct Enemy current_enemies[10];

 

extern Sprite* fly;

extern Sprite* snail;

extern Sprite* bee;

extern Sprite* crab;

 

extern fix32 enemy_x[4];

extern fix32 enemy_velx[4];

extern fix32 enemy_y[4];

These declarations mean:

The arrays (enemy_x, enemy_velx, enemy_y) seem to be your earlier system—possibly left over—but still declared.


1.5 Declaring enemy functions

extern void createEnemies();

extern void manageEnemies();

Other parts of your game can call these functions to:


📘 2. The Source File (enemies.c)

This is where all variables are actually defined and all logic is implemented.


🐞 2.1 Enemy sprite pointers

Sprite* fly;

Sprite* snail;

Sprite* bee;

Sprite* crab;

These appear leftover—they are not currently used for anything.
(Your actual sprites live inside current_enemies[i].sprite.)


📍 2.2 Initial arrays of positions and velocities

fix32 enemy_x[4] = {...};

fix32 enemy_velx[4] = {...};

fix32 enemy_y[4] = {...};

These also seem unused now—replaced by the new Enemy struct system.


👾 2.3 Defining the enemy pool

struct Enemy current_enemies[10] =

{

    {1, FIX32(50),  FIX32(80),  FIX32(1.5), NULL, 1, &img_fly },

    {1, FIX32(30),  FIX32(40),  FIX32(-0.5),NULL, 1, &img_snail },

    {1, FIX32(70),  FIX32(120), FIX32(2.0), NULL, 1, &img_bee },

    {1, FIX32(90),  FIX32(180), FIX32(-0.5),NULL, 1, &img_crab },

   

    // Six inactive "blank" enemies

    {0, FIX32(50), FIX32(80), FIX32(1.5), NULL, 1, &img_fly },

    ...

};

This creates a pool of 10 enemies:

Each enemy has:

This is a classic "object pool".


🛠 3. Enemy Creation (createEnemies())

This function:


3.1 Load palettes

PAL_setPalette(PAL2, img_snail.palette->data, DMA);

PAL_setPalette(PAL1, img_bee.palette->data, DMA);

You assign color palettes for:

Fly and crab use whatever default palette is assigned.


3.2 Loop through all 10 enemy slots

for (u8 i = 0; i < 10; i++)

{

    if (current_enemies[i].status != 0)

    {

        ...

    }

}

Only active enemies get sprites.


3.3 Automatically pick a palette

u8 pal = (i <= 1) ? PAL2

        : (i <= 3) ? PAL1

        : PAL0;

This means:

A quick-and-dirty way of sorting palettes.


3.4 Create the sprite

current_enemies[i].sprite = SPR_addSprite(

    current_enemies[i].graphic,

    fix32ToInt(current_enemies[i].x),

    fix32ToInt(current_enemies[i].y),

    TILE_ATTR(pal, FALSE, FALSE, FALSE)

);

This does the real work:

Now the enemies are fully visible on screen.


🚶 4. Enemy Updating (manageEnemies())

This function is meant to be called every frame.


4.1 Move and update every active enemy

for (u8 i = 0; i < 10; i++)

{

    if (current_enemies[i].status != 0)

    {

        current_enemies[i].x += current_enemies[i].vel_x;

 

        SPR_setPosition(

            current_enemies[i].sprite,

            fix32ToInt(current_enemies[i].x),

            fix32ToInt(current_enemies[i].y)

        );

    }

}

What happens here:

  1. X position increases by velocity
  2. Sprite’s screen position is updated
  3. Y stays constant (unless you later add vertical movement)

This is the core movement logic of your enemies.


🎯 Summary: What Your Enemy System Does

Defines an Enemy struct to hold:

Creates a pool of 10 enemies

Initializes each active enemy by:

Updates enemies every frame by:

This creates a modular, expandable enemy system suitable for any Genesis game.