Enemy handling and interrupts

Like I said last time, I focused on enemy and bullet handling this time. I explained that mixing background enemies and sprite enemies was my goal, and I found out this was not the greatest idea in the world. But more on that in just a bit!

Enemy handling

The enemy handling works as the following.

0:00
/0:05

Each time the background scrolls:

  • a tile containing an enemy is detected;
  • depending on the tile, an array of enemy structure is filled:
struct enemy
{
    uint8_t posX;
    uint8_t posY;
    uint8_t lastShotFrameCounter;
    uint8_t life;
    uint8_t type;
    bool active;
};

At every frame:

  • we loop through the array of enemies, and for each active enemy:
    • if the background has scrolled this frame, the enemy position is changed accordingly;
    • if it has gone out of bounds, it is deleted (the array has a fixed size for simplicity’s sake, so this just changes the variable active to false to be available to be written over);
    • if enough frames have passed since the last shot, a new shot is requested.
  • we loop through the array of bullets:
    • the type of the enemy determines the shot(s) needed;
    • the bullet array will be filled with the necessary information;
    • the bullet’s position will be determined, with some rules (out of bounds, etc.).

Basically, each enemy has a type that determines the pattern of bullets that will be shot. The bullets themselves also have a type (which is not the same) that determines the behavior of the bullet inside the pattern. Of course, an enemy type could call different patterns at random if needed, but I’m not sure if I want any kind of RNG in the game yet.

With this system in place, I could test different enemies shooting patterns, however… I still need to make a flexible and consistent pipeline of map and asset creation. So for the time being, I have only one kind of enemy in the map. That’s what you’ll see in the following video (the previous one had a different pattern though, demonstrating the system).

0:00
/0:11

And you can see it doesn’t look very good! Indeed, I’m very limited as to how I can place enemies, since backgrounds are based on tiles, as you can see on the screenshot.

On the left is the map, on the right is the tileset used to build the map.

So my solution will be to make every enemy sprite based (which can be placed anywhere on the screen). I don’t think this will cause major issue, especially since I found out something very interesting about sprites that I completely missed before…

Indeed, there are two sizes available for sprites: 8×8 and 8×16. For some obscure reason, I always thought that by going 8×16, we had half fewer sprites, but that’s absolutely not the case! So, if you remember what I said in the previous post, I wanted to reserve 8 sprite slots. This makes four 16×16 enemies, eight 8×16 enemies, three 16×16 enemies and two 8×16 enemies, etc. This gives a lot of possibilities, especially if I keep the idea of background collisions stopping the player from going anywhere he pleases. For such a small screen, I think it’s more than enough.

Interrupts

Something you may have noticed when comparing to the video of the last post, is that bullets are no longer displaying over the bottom HUD (which is a window layer in the Game Boy terminology). This is done with the power of interrupts, which you can read about here and here.

Here is the code doing that:

void interrupt_window()
{
    switch(LYC_REG)
    {
        case 136u:
            HIDE_SPRITES;
            LYC_REG = 144u;
        break;

        case 144u:
            SHOW_SPRITES;
            LYC_REG = 136u;
        break;
    }
}

void main()
{
    STAT_REG |= STATF_LYC;
    LYC_REG = 136u;

    CRITICAL
    {
        add_LCD(interrupt_window);
    }

    set_interrupts(LCD_IFLAG | VBL_IFLAG);
}

Basically, we set an interrupt when the screen refreshes at position 136, disabling the drawing of sprites from this line up to position 144, where we activate them again, looping through it by changing the LYC_REG register value.

Truth is, I have different results (one line of difference for sprite deletion) with two emulators that should be hardware accurate (bgb and Emulicious). Good thing is that I can test on real hardware, but there are some things I don’t yet understand about interrupts that I’m still investigating.

A bit of optimization

I’m still super early in the development and there are a lot of things I’m wondering as to which direction I should take, so I worry more on trying different things than optimizing them. However, I still couldn’t help but notice that there were big frame drops.

I suspected that was caused by my new bullets, but it actually was the score display at the bottom right. You see, there’s no way to display texts or numbers directly on the Game Boy. The way I have done it is by extracting the digits of the score number and changing the tiles accordingly. This is pretty intensive for the hardware, and I was doing it every frame! Now I change it only when the score changes. It may still need some optimization, but that’s a start.

Anyway, it really demonstrates how it’s different to program for such a different console to what I’m used to.


My next goal, after a bit of code organization and clean-up (by making the code more modular), will be to make a level system, probably something that reads where we are in the map and calls the adequate event. This way, sprite enemies spawning won’t be very far, and the enemy system I designed will be effective with no consequent changes. See you soon!