Learn how to make a space shooter for the Sega Mega Drive using SGDK! I highly recommend working through the Megapong Tutorial first, as that explains a lot of the basics of MD coding.

Megalaga 4 - Enemy Movement and Input


Now we have enemies, but they’re not moving. That’s not really intimidating, is it? Also, our player can’t move either. Let’s change all that so that we can actually interact with our game!

Moving the Enemies

First a quick bit of setup. At the top of main.c add these two defines:

#define LEFT_EDGE 0
#define RIGHT_EDGE 320

These will define the left and right edges of the screen. It’s good to use defines (or variables) for these values, so that we can easily change the bounds of the play area later on.

Now let’s get those enemies moving. We’ll create a function that will go through all enemy entities in a loop and handle the movement for each of them. So let’s define it:

void positionEnemies(){

}

First up, let’s loop through all the enemies and store the current one in a variable so that we can manipulate its values. That’s gonna look like this:

void positionEnemies(){
    u16 i = 0;
    for(i = 0; i < MAX_ENEMIES; i++){
        Entity* e = &enemies[i];
    }
}

First we initialize our counter variable i (which we can’t reuse this time, because we defined the original one inside the main() function). Then we loop through all the enemies, grab the address of the current one and store it inside the pointer e. Again, it’s good to know how pointers work in C…

We only want to move enemies that are alive, so let’s add that condition:

if(e->health > 0){
    
}

Again we’re using the arrow operator to access items in a struct so we don’t have to type (*e).health. The arrows also make the code look more dynamic, which is always important. Okay, maybe not.

Anyway, let’s finish up our enemy movement. Now that we have our current enemy entity and made sure that it’s alive, we need to apply its velocity to its position and then tell the sprite engine to position the sprite. The velocity of an enemy is set on its creation (see the previous post) so we won’t have to worry about that here.

e->x += e->velx;
SPR_setPosition(e->sprite,e->x,e->y);

We add the value of velx to x. This is all internal inside the current enemy struct. It’s a clean way to handle many variables that are bunched together. Personally I would prefer classes but retrodevs can’t be choosers. We can be glad we can use structs at all!

Okay, so now we have a function that moves enemies… but they would quickly fly out of bounds, which we don’t want. Instead, let’s make them turn around when they hit the edge of the screen. Add the following statements before applying the velocity:

if( (e->x+e->w) > RIGHT_EDGE){
    e->velx = -1;
}
else if(e->x < LEFT_EDGE){
    e->velx = 1;
}

This is where the width (w) of an entity comes in handy, which we also saved inside the struct. If the right edge of an enemy is past the right edge of the screen, we tell it to start moving left. And if the left edge of an enemy leaves the screen on the left side, we tell it to go right.

Now there is only one thing left to do: Actually call the function so that it can do its thing. Do so in while(1) before updating the sprite engine:

while(1){
    positionEnemies();
    SPR_update();
    VDP_waitVSync();
}

There we go! Compile the game and you should see all enemies bouncing back and forth in a neat row. It’s all handled within a single function, which is clean and convenient. It also ensures consistent behavior of all enemies. Of course we could also handle different enemy types within the same loop, but we’re only making a simple game with a single enemy type.

images/enemies_moving.gif

Moving the player

The enemies are moving now, but the player is still stuck. That doesn’t make for a fun game, so let’s change that. As always we’ll use a callback to handle our joypad input. Define it before the main function:

void myJoyHandler( u16 joy, u16 changed, u16 state)
{
    if (joy == JOY_1)
    {
        if (state & BUTTON_RIGHT)
	    {
            player.velx = 2;
        }
        else if (state & BUTTON_LEFT)
        {
            player.velx = -2;
        }
        else{
            if( (changed & BUTTON_RIGHT) | (changed & BUTTON_LEFT) ){
                player.velx = 0;
            }
        }
    }
}

This function isn’t that much different than in my other tutorials. If left or right is pressed on the D-Pad, the velocity of the player struct is set accordingly. If nothing is pressed, movement stops.

But now, just like with the enemies, we need a function that actually moves the player. We’re gonna call it positionPlayer().

void positionPlayer(){

}

This function will do pretty much the same thing as positionEnemies: Apply the velocity of the player to its position, then update the sprite position on screen. And like the enemies, the player won’t be able to leave the screen. In code things look like this:

void positionPlayer(){
    /*Add the player's velocity to its position*/
    player.x += player.velx;

    /*Keep the player within the bounds of the screen*/
    if(player.x < LEFT_EDGE) player.x = LEFT_EDGE;
    if(player.x + player.w > RIGHT_EDGE) player.x = RIGHT_EDGE - player.w;

    /*Let the Sprite engine position the sprite*/
    SPR_setPosition(player.sprite,player.x,player.y);
}

It’s pretty simple overall. By the way: We could get the dimensions of our entities from the sprites themselves, but saving them in separate values inside the struct gives us more flexibility. For example, we can set the hitbox of an entity independently of its actual graphic on screen.

Okay, now we need to tie our functions into the game’s code. First, initialize the controls and tell SGDK to use the joypad callback we defined. At the beginning of main(), before SYS_disableInts(), add these two lines:

JOY_init();
JOY_setEventHandler( &myJoyHandler );

And to finally get our player sprite moving, call the respective function in the main game loop before positionEnemies():

positionPlayer();
positionEnemies();
//...

Now compile the game and fly around space for a bit! Our little game is slowly coming together. But let’s kick things up a notch. As you might remember, the player sprite had two animations defined (meaning it had two rows of sprite graphics). If you look at the spritesheet, you’ll see that the second row shows the ship rolling to the side. Let’s implement this animation to make things look cooler and more dynamic!

Rolling

First of all we should define our animations to make things clearer in the code. At the top of main.c add these two defines:

#define ANIM_STRAIGHT 0
#define ANIM_MOVE 1

Animation index 0 will refer to our ship flying straight (the first row of the spritesheet), while index 1 is our ship rolling (row 2 in the sheet).

Now to actually implement these animations, we’ll need to go back to our joypad callback. When we press left/right we want to use ANIM_MOVE, if we press nothing the ship should revert to ANIM_STRAIGHT. Let’s translate that into code:

if (state & BUTTON_RIGHT)
{
    player.velx = 2;
    SPR_setAnim(player.sprite,ANIM_MOVE);
}
else if (state & BUTTON_LEFT)
{
    player.velx = -2;
    SPR_setAnim(player.sprite,ANIM_MOVE);
}
else{
    if( (changed & BUTTON_RIGHT) | (changed & BUTTON_LEFT) ){
        player.velx = 0;
        SPR_setAnim(player.sprite,ANIM_STRAIGHT);
    }
}

The function SPR_setAnim sets the animation for a sprite. Remember that our actual sprite is stored inside the sprite item of our Entity structs! SPR_setAnim(player,ANIM_MOVE) would not work because the function only works on sprites, and player is of type Entity.

If you compile and test the game now you’ll see that the ship only rolls in one direction, no matter which way you move. We could of course create another animation for moving the other way, but there is a far easier (and more efficient) solution: flipping the sprite! SGDK offers the SPR_setHFlip() function to accomplish that.

A look at our ship spritesheet shows that the second row shows the ship rolling left. That means that if we’re going right, we want to flip the sprite so that it’s rolling right instead. Add SPR_setAnim to the right if-statement of our joypad callback so that it looks like this:

if (state & BUTTON_RIGHT)
{
    player.velx = 2;
    SPR_setAnim(player.sprite,ANIM_MOVE);
    SPR_setHFlip(player.sprite,TRUE);
}

However, if the ship is moving left, we don’t need to flip the sprite. So in the statement dealing with moving to the left, add this line:

SPR_setHFlip(player.sprite,FALSE);

The function itself is self-explanatory, it simply tells SGDK whether to horizontally flip a sprite or not.

And we are done! Compile the game and you should see our ship rolling and moving around.

images/player_moving.gif

It already looks a lot better, doesn’t it? Little things like this can make a big difference.

Now that we’ve got enemies and the player moving around, we can get to the fun part: Shooting stuff! I’ll see you then, be excellent to each other!

If you have any questions, comments or criticism, post them in the comments below or reach out to me on Twitter @ohsat_games! Special thanks to Stephane Dallongeville for creating SGDK and everyone in the SGDK Discord for their help and keeping the dream alive!

Download the source code

All patrons on Patreon get the complete source code for this tutorial, as well as other perks such as early access! Become a Patron!
Just Want to Buy Me a Coffee?

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
  • Get Words in Your Inbox!

    Be oldschool and sign up for my newsletter to get updates! Just enter your email address, prove you're not part of Skynet and you're good to go!



    Powered by CleverReach. I will not send you spam or sell/give your email address to someone else.  You can of course unsubscribe at any time. By clicking the subscribe button above, you confirm that you have read and agreed to our privacy policy.

    By using the Disqus service you confirm that you have read and agreed to the privacy policy.

    comments powered by Disqus

    Related Posts

    Streets of Rage 2 Design Docs

    A few years ago, Yuzo Koshiro posted a pile of old game design documents for Bare Knuckle 2 aka Streets of Rage 2 on the Ancient blog to commemorate the release of Streets of Rage 2 3D on the Nintendo 3DS. These documents gave a deep insight into the game’s inner workings, technical aspects, designs and even some cut content. They were an awesome resource for one of the most awesome games ever created.

    Read More

    Make a Space Shooter for the Mega Drive!

    It’s time for another SGDK tutorial series! After doing a single player Pong game and an endless runner, it’s time to reach for the stars… and make a space shooter! Apart from things like scrolling and animating sprites, this new series will show you how to deal with multiple entities and their collisions, how to randomly generate backgrounds and more! This project builds upon the previous tutorials, so if you’re new to SGDK programming and have not done those yet, I highly recommend starting with Megapong.
    Read More

    Patreon Revamps

    Hey there, what’s up? Things are continuing to evolve, as I’ve now updated my Patreon to give patrons more perks! Apart from early access to new tutorials and posts, the biggest one is probably the ability to peek behind the scenes…and there will be a lot to peek at in the coming months! This year I’m writing and submitting my MA thesis, meaning that I will have to do more stuff to make ends meet.
    Read More