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 BONUS - Powerup

Posted April 27, 2020

One of the coolest things in shmups are the powerups. It’s fun to grab them to see what effects you get, and they can make you feel extremely powerful. So let’s put one in!

In part 8 we put a restriction on the amount of shots players can fire at once. This made the game more balanced, but–let’s be honest here–a bit less fun. So temporarily lifting that restriction sounds like a good idea for a powerup to me!

Variables

Let’s define a few things. First of all, we’ll need a new variable that keeps track of how many bullets players can currently shoot. This is the one we’ll be changing when the powerup is active to raise the limit. So add this line below the MAX_PLAYER_BULLETS define:

u16 maxPlayerBullets = MAX_PLAYER_BULLETS;

Next we’ll need some things for our powerup:

#define POWERUP_DURATION 180
u16 powerupTimer = 0;
Entity powerup = { 0,0,8,8,0,0,0, NULL };

The first two will help to keep the powerup active for a limited time only (3 seconds in our case, but feel free to adjust it). And Entity powerup will be the actual powerup that we spawn. I’m only doing one for the purposes of this tutorial, but of course you can extend this if you want to have multiple powerups on the screen at once.

And finally, let’s make our code a bit more flexible for the future with an enum that differentiates between various powerup types. Even though we’ll only have one type here, this will make it a bit easier to add others.

typedef enum
{
    RapidFire
} Powerup;

Functions

Alright, let’s get cracking. First, hop on over to shootBullet(). Replace the line if(shotByPlayer >= MAX_PLAYER_BULLETS) near the beginning with the following line that uses our new variable:

if(shotByPlayer >= maxPlayerBullets){
    //...
}

Now we can set the maximum amount of player bullets on the fly. Next, we’ll need to define a total of four functions to help us deal with our powerup. We’ll fill the bodies out as we go, but go ahead and define all of them now:

void spawnPowerupAt(u16 X, u16 Y){}
void positionPowerup(){}
void activatePowerup(Powerup Type){}
void deactivatePowerup(){}

Looks like we got our work cut out for us!

Let’s start with activatePowerup. As you can see we’re passing in the type of powerup that we want to activate. We will then use a switch statement to decide which effect we want to activate. Again, we’re only dealing with one type of powerup in this tutorial, but by setting up the code this way we can easily add more later on.

void activatePowerup(Powerup Type){
    switch(Type){
        case RapidFire:{
            
        }
        default: return;
    }
}

Okay, so the powerup is supposed to increase the number of bullets players can fire. How could we achieve this? By setting maxPlayerBullets = MAX_BULLETS;! This will allow players to use the entire bullet pool. However, we want to limit the use, so we will also have to set up our powerupTimer. So now our function looks like this:

void activatePowerup(Powerup Type){
    switch(Type){
        case RapidFire:{
            maxPlayerBullets = MAX_BULLETS;
            powerupTimer = POWERUP_DURATION;
        }
        default: return;
    }
}

That would work as it is, but let’s be fancy and change the color of the player ship while the powerup is active. So add the following line as well:

VDP_setPaletteColor(18, RGB24_TO_VDPCOLOR(0xf8fc00));

The index 18 points to the orange color of our ship in PAL1. We did something similar in part 3 and I also have a separate tutorial on swapping colors if you want a bit more detail.

Let’s finish up the counterpart function deactivatePowerup as well while we’re at it. This function will simply revert the changes we just made:

void deactivatePowerup(){
    maxPlayerBullets = MAX_PLAYER_BULLETS;
    VDP_setPaletteColor(18, RGB24_TO_VDPCOLOR(0xf83800));
}

So far so good. You’re probably itching to try things out, so let’s do that with a little debug trick! Head on over to myJoyHandler and add a new if-statement to the end:

if (state & BUTTON_A & changed)
{
    activatePowerup(RapidFire);
}

We’re not using the A button for gameplay, so we can use it for debug purposes. Don’t forget to delete this line before releasing your game though!

images/poweron.gif

Alright, we got a yellow ship that’s shooting like there’s no tomorrow! Works great. Unfortunately now we’ll have to limit the fun a bit. Again.

Go to the main game loop in main(). We’ll now add code to limit the duration of the powerup. It’s rather simple, so add this chunk before SPR_update():

if(powerupTimer > 0){
    powerupTimer--;
    if(powerupTimer == 0){
        deactivatePowerup();
    }
}

You’ll recall that we’re setting powerupTimer to POWERUP_DURATION when we’re activating a powerup, so as long as powerupTimer is bigger than 0, we can assume that a powerup is active. So each frame we will decrease the timer until it hits 0: That’s when we deactivate the powerup. And since powerupTimer > 0 is now false, the timer won’t keep ticking down. Which is good, because it’s a u16 and therefore couldn’t go lower anyway.

Adding The Item

So we have the functionality, but now let’s add the powerup proper. First, grab the sprite we’ll be using:

Download the powerup sprite

And then import it like so:

SPRITE  spr_powerup "sprites/powerup.bmp"   1   1   FAST    0

Now go to main and create the powerup sprite after you’ve created all the bullet sprites:

/*Create the powerup*/
powerup.sprite = SPR_addSprite(&spr_powerup, powerup.x, powerup.y, TILE_ATTR(PAL1, 0, FALSE, FALSE));
killEntity(&powerup);

Note that we’re killing the powerup entity immediately. This is because we don’t want it to show up from the start, we want to spawn it later. Also note that killEntity expects a pointer, so we have to pass in the address of our entity.

We have a powerup entity on standby now, so let’s get to spawning it by returning to one of our 4 functions. It’s pretty simple:

void spawnPowerupAt(u16 X, u16 Y){
    powerup.x = X;
    powerup.y = Y;
    reviveEntity(&powerup);
}

As you can see we’re passing in the coordinates where we want it to spawn. Since we already have a powerup created we can simply revive it. If you had multiple powerups you’d be taking one out of the pool here.

And while we’re fiddling with our functions again, let’s finish up the last one as well. Our powerup sprite needs to be positioned on screen and we want it to move downwards so that players can collect it. But we also don’t want it to loop around, so we have to kill it once it leaves the bottom edge of the screen. This is what that looks like in code:

void positionPowerup(){
    if(powerup.health > 0){
        powerup.y++;

        if(powerup.y > BOTTOM_EDGE){
            killEntity(&powerup);
        }
    }

    SPR_setPosition(powerup.sprite,powerup.x,powerup.y);
}

We’re almost done! But before we spawn it, let’s implement the collision with our player so that the powerup item actually has an effect. Instead of creating a new function for it, we’ll simply stick the code in our current one. So the whole function now looks like this:

void positionPowerup(){
    if(powerup.health > 0){
        powerup.y++;

        if(powerup.y > BOTTOM_EDGE){
            killEntity(&powerup);
        }

        if(collideEntities(&player,&powerup)){
            activatePowerup(RapidFire);
            killEntity(&powerup);
        }
    }

    SPR_setPosition(powerup.sprite,powerup.x,powerup.y);
}

And before we forget, add a call to positionPowerup() inside of your main game loop! I put mine right after the calls to all the other positioning functions.

We are so close! We now have the functionality and the actual item implemented, so all that’s left is to actually spawn a powerup. We will be doing this inside of our handleCollisions() function, more specifically after a collision between a bullet and an enemy. Add this right before the break; at the end:

if (enemiesLeft % 5 == 0)
{
	spawnPowerupAt(e->x, e->y);
}

We’re using the modulo operator % here to spawn a powerup whenever enemiesLeft is cleanly divisible by 5. You can of course add a random element to it or use more specific criteria; I’m just keeping it simple here.

And that’s all we need! Compile the game and try it out. Once you’ve shot the 5th-to-last enemy a powerup should pop up, float downwards and give you rapid fire if you manage to collect it!

images/fullpower.gif

And that wraps up the Megalaga tutorial. Thank you for reading, thank you very much for the support and 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!

Download the Project Files!

All patrons on Patreon get the complete source code for this tutorial, as well as other perks such as early access! And Patreon support also ensures that I can keep working on tutorials like this one. 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

    HaxeFlixel Tutorials!

    If you’ve popped over to the tutorial section recently you might have noticed that I’ve added my very first HaxeFlixel tutorial! It shows how to implement a simple, pixel-perfect 2D water shader which I used for Go! Go! PogoGirl. But a few of you might be wondering what a HaxeFlixel is. Well, it’s a 2D game framework that is as powerful as it is underrated! It runs on the (also underrated) Haxe language, is extremely well documented, open source, and has built-in functions for almost anything you’d need.
    Read More

    Streets of Was

    As I’m sure many of you will remember, the original Streets of Rage for the Mega Drive had multiple endings. The real canonical ending has you beat the crap out of Mr. X, thereby ending his reign of terror forever (yeah, right). However, if you confronted Mr. X with a buddy in tow, a new possible path unlocked. A quick refresher is in order. When you confront Mr. X he will ask you to join his organization.
    Read More

    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