2013-12-27

I am going to walk you through the creation of a Megaman-inspired shooter/platformer game. We will be more focused on the shooting aspects of the gameplay rather than the platforming. In this tutorial I will be using Construct 2 as the tool to make the game, but I will explain the logic using pseudocode so that you can follow this tutorial in any language or engine of your choice.

Related Posts

Build a Two-Player "Small Tactics" Board Game in Construct 2: Part 1

Create a Bomberman-Inspired Game in Construct 2: The Player and the Level

Make a Match-3 Game in Construct 2: The Basics

Common Missteps of Newcomers to the Construct 2 Gamedev Community

In order to focus on the gameplay implementation, I won't explain every Construct 2 feature; I am going to assume that you know the basics such as loading a sprite, basic collision, or playing sounds. With that said, let's start making the game.

Prepare the Artwork

First things first, we need to have sprites for our game. Thankfully, opengameart has us covered with their wonderful collection of legal game art. We need four sets of sprites; one hero, one enemy, and tiles for platforms.

Hero (by Redshrike):
http://opengameart.org/content/xeon-ultimate-smash-friends

Enemy (also by Redshrike):
http://opengameart.org/content/fighting-robot-for-ultimate-smash-friends

Tiles (by robotality):
http://opengameart.org/content/prototyping-2d-pixelart-tilesets

To use them in Construct 2, I crop the hero's sprites into individual frames using GIMP.

Basic Movement

I will be using Construct 2's platform behaviour for the rest of the tutorial so that I can focus on the shooting and AI part of the game, which was the main focus of this tutorial.

Basic Movement Explained

If you are working in another language, or want to implement your own basic platformer movement instead of using the built-in behaviour. You only need to use the code in this section if you aren't going to use Construct 2's built-in behavior.

To begin, we need to consider three ways our hero can move; walking right, walking left, or jumping. Each frame, we update the game simulation.

To update the player's character, we implement basic movement like this:

I use another function to do jumping because jumping isn't just a matter of changing the y value but also calculating the gravity. We will also have a function that listens whether or not a key has just been released, to return our hero animation to a standing animation.

Let's talk about how to make the player jump. The hero will need to know whether he is currently jumping or not, and also whether he is currently falling or not. So we'll declare two new variables: isJumping and isFalling. By default, both are false, which means the hero is standing on a platform.

To perform a jump we must first check whether or not both values are false, and then make the isJump true.

For the hero to be able to jump, we need a variable called jumpPower and gravity. The jumpPower's default value is -20 and gravity is 1. The logic is to add the value of jump power to hero's Y position and to add gravity to jump power's value.

We do this every tick. Maybe this isn't the most realistic gravity physics there is, but games don't need to be realistic, they just need to be believable, that's why some games have super human jump and double jump. The code below belongs in the update function.

Construct 2's built-in platform behaviour replicates the above example code, which is given only the help those working in another language.

Implementing the Shooting

Now comes the shooting part of the game. In the Megaman series there are three types of shots: normal shots, charged shots, and boss energy shots.

Normal shots are self explanatory. Charged shots are shots that are charged first before released, these charged shots come in two types: half charged, and fully charged. These charged attacks are stronger than normal shots, with the fully charged become the strongest.

Boss energy shots are shots with power that the player acquired after defeating each bosses. The damage is the same as normal but they have special properties that normal shots don't have.

Now that we know each shot's type, let's begin to make them. First let's see the logic behind how we use each shot. Here we assume that the Z button on the keyboard is used to fire a shot. We'll implement two different behaviors:

Normal shots: the player presses z and then immediately releases it (tap the button). The bullet will be shot once each tap. The animation will change to shoot animation before immediately switching to the standing animation.

Charged shots: the player presses Z. The first normal bullet will be shot. The animation will change to shoot before immediately switching to the standing animation. If Z continues to be pressed then a charging effect will be added on top of the playing animation (standing, walking). If the Z button is released in less than 5 seconds since first charging, then a half-charged bullet will be shot. If the Z button is released after 5 seconds, a fully charged bullet will be shot.

Boss energy shots: our hero must first equip the bullet he acquired after defeating a boss. After equipping, the player will press another button to shot this bullet. This bullet behaviour varies, and needs to be uniquely coded for every bullet.

Now, let's start to code. Because our hero can shoot left and right we need to know which direction he's currently facing. Let's declare a new variable called facing that stores a string value of whether the hero is facing left or right.

Before we shoot a bullet, we need to look at the properties the bullet has:

Power: attack power of the bullet, the damage it will dealt to the enemy

Speed: how fast the bullet goes

Angle: the shooting angle, determines at which direction the bullet goes.

These properties will differ for each bullet. In particular, the power property will be different. The angle property is normally only one of two values; whether the bullet is shot right or left, unless it's a boss energy bullet that may shot at a unique angle.

Shot variations will be discussed later so now I will only cover basic shots. The following is the piece of code that shoots a bullet.

Charged Shots

Some bullets can be more powerful than others. To create a charged shot we need a variable named chargedTime, which will increment each second the player holds Z down, and will return to zero when the bullet is fired. The changes to the update code are as follows:

Our hero character new moves left, right, and jumps according to our input, and also shoots bullets, whether normal, half charged, or fully charged.

Implementing Enemies

We now have a controllable hero. Let's call it Xeon for simplicity's sake. He can perform some basic movements like walking, jumping, and shooting. That's great! But what good is the ability to shoot without something to shoot at, right? That's why this time we're going to make our first enemy.

Let's design our enemy's attributes before we start to code it.

Health: How many healths our enemy have determines how many shots (and what kind) is needed to destroy it.

Power: Enemy's attack power, how much damage does it deal to our player.

ShotAngle: to which direction the enemy shoots the bullet, it can be left or right or anywhere we want.

That's pretty much what we need for our enemy, now let's make the enemy class/object.

The Enemy class/object is pretty much the same as the player class/object, except the enemy doesn't listen to player input. Because of that we need to replace the parts where the hero listens to player input, to enemy AI/logic.

Enemy Attack AI

For starters, let's handle the enemy's basic shooting AI. The enemy will shoot at the player when it sees the player.

To determine whether the enemy "sees' the player, we will need to define a variable for the enemy object called facing which is a string that stores one of two values, "left" or "right".

The enemy also needs some kind of range of sight, which is why we're going to make another variable called range. If the player is within this range then that means the enemy "sees" the player. The pseudocode is as follows:

checkSees() function

Maybe you've noticed something in this pseudocode: it doesn't consider the hero's y position, so the enemy will still shoot at the hero even if they're at platforms with different heights.

For now this will suffice, because making a line of sight algorithm is outside the scope of this tutorial. In your own game, you might want to add a Y tolerance in that function above that will check whether hero's y position is between two points that define the enemy's heights.

Making Enemies Shoot

The pseudocode for enemy shooting is as following:

As you can see, the enemy shoot() function is similar to that of the player's. It takes the sprite's path, attack power, bullet speed, and shooting angle as parameters.

Enemy Movement AI

When does the enemy switch from facing left to facing right? For our hero, we use player input to change the direction our hero faces. For our enemy we have two options: use some kind of timer to switch facing direction every few seconds while having the enemy to stand still, or have the enemy to walk to a certain spot and then switch its facing direction and then walk to another spot to switch its facing direction again.

This second method can be used as a patrolling AI. Of course, we can just make the enemy walk in one direction and never turn back.

The pseudocode for the first method is as follows:

Enemy Patrolling AI

To make the patrolling AI, we need to make two invisible objects that are at the end of both ways of enemy's patrolling route, and make the enemy move another way if it collides with them.

Now let's write our pseudocode for the enemy's patrolling AI:

After this, the enemy will patrol between two points like we want it to.

To set up which AI the enemy uses, we're going to add one more variable with a string type for our enemy: enemy AI. This will determine what AI to use every frame, like so:

Of course you can add more enemy AI type if you want.

Shot Variation

Let's go on about how we can make shot variations for both the player and the enemy. We're making shot variations by changing two things: the shooting angle, and the number of bullet shot.

This way we can make a simple one bullet shot, or a three directional bullet shot. Before we do this we're going to make another variable to the enemy object/class named shotAI, which is a string. We'll use this in our checkSees() checking if block, where the enemy shoots. The changes to that code block will be like this:

Of course, the name of the AI and what kind of shot the enemy would fire are up to you, this is just an example.

Now, let's delve deeper into what is inside the shootThreeBullets() function.

If you're unsure why 0 goes to right and 180 goes to left, it's because the direction of 0 degrees goes straight to the right side of the screen, 90 degrees goes to the down side of the screen, and so on until it hits 360 degrees. Once you know what value goes where, you can create your own shot variation.

We can also make a shotAI variable for the player, but I prefer we name it selectedShot, because our player will choose the bullet instead of programmed from the beginning. T

he logic in megaman is every time megaman defeats a boss, he gets that boss' power as a new shot. I'm going to try to recreate that logic. To do this we need an array that contains player's shots, including normal shots. The pseudocode is like this:

We need to keep track of two new variables:

We will push new elements to shotArr when the player defeats a boss.

Upgrading the Player Bullets

Just like the enemy's shootThreeBullet(), you can be creative and create your own shot variations. Since this is the hero's bullet, let's give it something special.

Let's make one type of shot to be effective against a specific boss, so that it deals more damage. To do this, we'll create a variable for the bullet object named strongAgainst that is another string type variable that contains the name of the boss that this bullet is effective against. We will add this deals more damage functionality when we discuss the boss part of the game.

Health and Death

This is where all the shot variations we make really start to matter. This is where our hero damage and kill the enemy, and the other way around.

To begin, let's make a variable for both the hero and enemy object, named health which is an int, and another variable just for the hero named lives. Let's take a look at the pseudocode:

We will make the same pseudocode for damaging enemies, like so:

The Health Bar GUI

Now, if I leave it at that then it would not be interesting. So I'll make a rectangle sprite on the top left corner of the screen that acts as our hero's health bar.

This health bar's length is going to change depending on our hero's current health. The formula for changing health bar's length is this:

We need one more variable for our hero called maxHealth; our hero's full health value. For now this value cannot be changed but maybe in the future we can create an item that increases the amount of hero's maxHealth.

Create The Game World

Now that we have created our hero, enemy, and shot variations, we need to make multiple levels and bosses.

To have multiple levels means that at some point in the game the player is going to reach one or more checkpoints that switches the game from level 1-1 to level 1-2 to level 1-3 and so on until they reach the boss.

When the player dies somewhere in level 1-2 he or she doesn't need to replay all the way back from the beginning of level 1-1. How do me do this? First we're going to make the level, I'm not going to explain much about level designing, but here is the example level in Construct 2.

The image in the top left corner is the HUD layer. It will scroll, following the hero when the game is played.

Doors and Checkpoints

One sprite you should pay attention to is the green sprite in the upper right part of the level. It is the checkpoint in this level when the hero collides with it we will transfer the game on to level 1-2.

To handle multiple levels we need three variables: currentLevel, levelName, and nextLevel.

The currentLevel variable is created in the hero object/class. The levelName is created in the game scene (level) object for each level. The nextLevel variable is created in the green sprite object.

The logic is as follows: when the hero collides with the green sprite (I call it greenDoor), we will change our level to the game scene in which levelName is the same as the nextLevel variable. After we change the level we will change the value of hero's currentLevel variable to the same as game scene's levelName. Here's the pseudocode:

Initializing A New Level

Here is the pseudocode for dealing with when the next level is loaded and ready to play.

The Player Start Positions

Now that we have changed to a new level I will explain the orange sprite behind our hero in the level design image above. That orange sprite is an object that I call startPos. It is used to mark the starting position of each level.

We refer to this object when the hero just changed levels or died, so that we know where to spawn him.

Here is the pseudocode for handling when the hero dies:

Now we can have multiple levels and we can also respawn the hero after he dies.

You can create as many levels as you want, or maybe even create two greenDoor objects in a level which one of them goes back to level 1-1 if the player solve a puzzle in a wrong way.

Boss Battles

It is finally time to implement the boss itself. To make a boss level is as simple as making another level which will spawn a boss instead of regular enemies.

The hard part is creating AI for the boss because each boss will have a unique AI. So to make it easy to understand and duplicate for a lot of bosses, I'm going to make the boss AI be dependent on the time after they're spawned. Which means they're going to do A for x seconds, then change to B for y seconds then doing C for z seconds before returning back to A. The pseudocode will look something like this:

The definition of the boss shot functons is below. Feel free to change it to fit what you want for a boss fight:

It is up to you to add shot variations to the boss' routine. The variables used by the boss enemy object are the same as enemy object, except that bosses don't use the enemyAI and shotAI variables, since both will be handled in the if block above depending on the elapsed time.

Boss enemies also have a variable that enemy objects don't. It is called rewardShot, which is a string. This variable holds the name of a boss shot that the player will obtain after defeating the boss (the ones in shotArr array variable).

This will allow the player to "learn" the boss attack as explained earlier. To add this shot type to the array of player shots we will need to add the following code after the enemy dies:

To implement additional bullet types for your players to enjoy, all you need to do is add the appropriate code for each rewardShot your create, just like we did earlier.

Congratulations!

You have completed my tutorial on how to create a Megaman-like metroidvania platformer game in Construct 2. The rest is just building up your game based on what you have learned here to make it your own. Add more enemy types, more levels, powerups and more. Good luck and have fun.

Show more