Quantcast
Channel: Envato Tuts+ Game Development
Viewing all articles
Browse latest Browse all 728

Make a Megaman-Inspired Game in Construct 2

$
0
0

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.


Click the game to give it focus, then use the arrow keys to move and jump, and Z to shoot.

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.

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.

Construct 2's platform behaviour
Construct 2's platform behaviour

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.

number moveSpeed = 50;

function update()
{
 moveHero();
}

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

function moveHero()
{
 // player is pressing this key down
 if (keyDown(KEY_LEFT))
 {
 hero.x -= moveSpeed * deltaTime;
 hero.animate("walkLeft");
 }

if (keyDown(KEY_RIGHT))
 {
 hero.x += moveSpeed * deltaTime;
 hero.animate("walkRight");
 }

if (keyDown(KEY_UP))
 {
 hero.jump();
 hero.animate("jump");
 }

// a key that was just unpressed
 if (keyReleased(KEY_LEFT))
 {
 hero.animate("standLeft");
 }

if (keyReleased(KEY_RIGHT))
 {
 hero.animate("standRight");
 }
}

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.

Function jump()
{
 if (!isJumping && !isFalling)
 {
 isJumping = 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.

If (isJumping || isFalling)
{
 hero.y += hero.jumpPower;
 hero.jumpPower += hero.gravity;
}

// eventually jump power will be greater than zero, and that means the hero is falling

if (hero.jumpPower >= 0)
{
 isJumping = False;
 isFalling = True;
}

// to make a stop to the fall, do something when the hero overlaps the platform

if (hero.isOverlapping(platform1))
{
 // platform1 is the platform that our hero can step on
 // resets the variable to their default value
 isJumping = False;
 isFalling = False;
 hero.jumpPower = -20;
}

// and then there's the free fall, when player falls over the edge of a platform
if (!hero.isOverlapping(platform1) && hero.jumpPower < 0 && !isJumping)
{
 // !hero.isOverlapping(platform1) checks whether or not our hero is standing on a platform
 // and if jumpPower is less than zero and the player is not currently jumping, then that means
 // he's falling

// setting these two values like this will make the player fall.
 hero.jumpPower = 0;
 isFalling = true;
}

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.

String facing = "right"; // which direction the hero is currently facing
function moveHero()
{
 // player is pressing this key down
 if (keyDown(KEY_LEFT))
 {
 hero.x -= moveSpeed * deltaTime;
 hero.animate("walkLeft");

 facing = "left";
 }

if (keyDown(KEY_RIGHT))
 {
 hero.x += moveSpeed * deltaTime;
 hero.animate("walkRight");

facing = "right";
 }

// ... the continuation of moveHero() function goes here
}

function update()
{
 // ...the update code that we previously wrote goes here...

// player press this key once
 if (keyPressed(KEY_Z))
 {
 if (facing == "right")
 {
 hero.animate("Shoot");

// we will add shooting function here later
 }
 else if (facing == "left")
 {
 hero.animate("Shoot");
 hero.mirrorSprite(); // this function flips the sprite horizontally
 }
 }

if (keyReleased(KEY_Z))
 {
 if (facing == "right")
 {
 hero.animate("standRight");
 }
 else if (facing == "left")
 {
 hero.animate("standLeft");
 hero.mirrorSprite(); // we need to call this again because the sprite was mirrored
 // if we don't mirror the sprite again, standLeft will look like standRight
 }
 }
}
move events in construct 2
Move events in Construct 2

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.

// first, we create a function that creates a new bullet
Function shoot(string pathToSprite, number bulletPower, number bulletSpeed, number bulletAngle)
{
 myBullet = new Bullet(pathToSprite);
 myBullet.power = bulletPower;

// the bullet class or object has two private variables that moves it according to its angle
 // more explanation to these two lines need more math, so I choose not to explain
 // I assume your engine of choice have a way to move an object according to its angle
 ySpeed = Math.sin(bulletAngle) * bulletSpeed;
 xSpeed = Math.cos(bulletAngle) * bulletSpeed;
}

// this is Bullet class' function that's called every frame, this moves the bullet according to its angle
function moveBullet()
{
 x += xSpeed * deltaTime;
 y += ySpeed * deltaTime;
}

// and this is the modification to our previous update() function
function update()
{
 // ...the update code that we previously wrote goes here...

// player press this key once
 if (keyPressed(KEY_Z))
 {
 if (facing == "right")
 {
 hero.animate("Shoot");
 hero.shoot("path/to/sprite.png", 10, 400, 0);

}
 else if (facing == "left")
 {
 hero.animate("Shoot");
 hero.mirrorSprite(); // this function flips the sprite horizontally
 hero.shoot("path/to/sprite.png", 10, 400, 180); // the angle is 180 so that the bullet goes left
 }
 }

// .. the continuation of update code goes here...
}
add bullet behaviour to the bullets
Adding the bullet behavior to bullet sprites
shooting code, spawn new bullet object
The shooting code where we spawn a new bullet object

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:

// player just released z key
if (keyReleased(KEY_Z))
{
 if (chargedTime > 0 && chargedTime <= 5)
 {
 if (facing == "right")
 {
 hero.animate("Shoot");
 hero.shoot("path/to/halfChargedBullet.png", 20, 400, 0);
 chargedTime = 0;

}
 else if (facing == "left")
 {
 hero.animate("Shoot");
 hero.mirrorSprite(); // this function flips the sprite horizontally
 hero.shoot("path/to/halfChargedBullet.png", 20, 400, 180);
 chargedTime = 0;

}
 }
 else if (chargedTime > 5)
 {
 if (facing == "right")
 {
 hero.animate("Shoot");
 hero.shoot("path/to/fullChargedBullet.png", 40, 400, 0);
 chargedTime = 0;

}
 else if (facing == "left")
 {
 hero.animate("Shoot");
 hero.mirrorSprite(); // this function flips the sprite horizontally
 hero.shoot("path/to/fullChargedBullet.png", 40, 400, 180);
 chargedTime = 0;

}
 }

if (facing == "right")
 {
 hero.animate("standRight");
 }
 else if (facing == "left")
 {
 hero.animate("standLeft");
 hero.mirrorSprite(); // we need to call this again because the sprite was mirrored
 // if we don't mirror the sprite again, standLeft will look like standRight
 }
}

// player is pressing this key down
if (keyDown(KEY_Z))
{
 // this is the function that adds the value of chargedTime every second
 // this keyDown block of code will be run every frame, which is less than a second
 // your engine of choice should have a way to tell whether a second has passed or not
 addChargedTime();
}
chapter 1 - add charged time
chapter 1 - shoot full charged bullet
chapter 1 - shoot half charged bullet

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:

function boolean checkSees()
{
 if (facing == "left" && hero.x >= enemy.x -- range)
 {
 return true;
 }

if (facing == "right" && hero.x <= enemy.x + range)
 {
 return true;
 }

return false;
}
checkSees() function

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:

// can be in update() or somewhere else that's executed every frame
function update()
{
 if (checkSees())
 {
 shoot("path/to/bulletSprite.png", enemyPower, 400, shotAngle);
 }
}

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:

function switchingAI()
{
 // elapsedTime() is a function that counts how many seconds has passed since its value is reset
 // I assume your engine of choice have this kind of functionality
 if (elapsedTime() > 4.0)
 {
 if (facing == "left")
 {
 facing = "right";
 shotAngle = 0;
 }

if (facing == "right")
 {
 facing = "left";
 shotAngle = 180;
 }

enemy.mirrorSprite(); // also flip the sprite horizontally
 resetTime(); // resets the time that counts in elapsedTime()
 }
}
switching AI function
Switching AI functions

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.

this is how it looks in Construct 2
Patrolling AI

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

function patrollingAI()
{
 if (facing == "right")
 {
 walkRight(); // the same as the one in player object / class

if (collidesWith(rightPatrolBorder))
 {
 facing = "left";
 enemy.mirrorSprite();
 }
 }

 if (facing == "left")
 {
 walkLeft();

if (collidesWith(leftPatrolBorder))
 {
 facing = "right";
 enemy.mirrorSprite();
 }
 }
}
chapter 2 - 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:

if (enemyAI == "switching")
{
 switchingAI();
}
else if (enemyAI == "patrolling")
{
 patrollingAI();
}

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:

// can be in update() or somewhere else that's executed every frame
function update()
{
 if (checkSees())
 {
 if (shotAI == "simple")
 {
 shoot("path/to/bulletSprite.png", enemyPower, 400, shotAngle);
 }

if (shotAI == "threeBullets")
 {
 shootThreeBullets();
 }
 }
}
chapter 2 - enemy shooting

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.

Function shootThreeBullets()
{
 if (facing == "right")
 {
 shoot("path/to/bulletSprite.png", enemyPower, 400, 0); // this bullet goes straight to the right
 shoot("path/to/bulletSprite.png", enemyPower, 400, 330); // this goes up by 30 degrees
 shoot("path/to/bulletSprite.png", enemyPower, 400, 30); // this goes down by 30 degrees
 }

if (facing == "left")
 {
 shoot("path/to/bulletSprite.png", enemyPower, 400, 180); // this bullet goes straight to the left
 shoot("path/to/bulletSprite.png", enemyPower, 400, 210); // this goes up by 30 degrees
 shoot("path/to/bulletSprite.png", enemyPower, 400, 150); // this goes down by 30 degrees
 }
}

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:

var shotArr = ["normalShot", "boss1", "boss2"];
var shotIndex = 0;
var selectedShot = "normalShot";

function update()
{
 // this is the block of code in player's update function where the player shoots a bullet

// this function changes the bullet that the player shoots
 changeBullet();

// player press this key once
 if (keyPressed(KEY_Z))
 {
 if (facing == "right")
 {
 hero.animate("Shoot");
 if ( selectedShot == "normalShot")
 {
 hero.shoot("path/to/sprite.png", 10, 400, 0);
 }
 else if ( selectedShot == "boss1")
 {
 // add codes to shoot the kind of shot that the player received after defeating boss 1
 }
 }
 }
}

function changeBullet()
{
 // changes shotIndex based on button pressed
 if (keyPressed(KEY_E))
 {
 shotIndex += 1;
 }

if (keyPressed(KEY_Q))
 {
 shotIndex -= 1;
 }

// fix shotIndex if it's out of array's length
 if (shotIndex == shotArr.length)
 {
 shotIndex = 0;
 }

if (shotIndex < 0)
 {
 shotIndex = shotArr.length -- 1;
 }

selectedShot = shotArr[shotIndex];
}
two new variables

We need to keep track of two new variables:

how to add an array to construct 2
How to add an array in Construct 2
chapter 2 - function change bullet

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:

if (bullet.collidesWith(hero))
{
 hero.health -= bullet.power;
 createExplosion(); // for now we don't have an explosion sprite, so this will act as a reminder
}

// check if the hero is dead
if (hero.health <= 0)
{
 hero.lives -= 1; // decreases hero's total number of lives.
 destroyHero();
}

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

if (bullet.collidesWith(enemy))
{
 enemy.health -= bullet.power;
 createExplosion();
}

if (enemy.health <= 0)
{
 destroyEnemy();
}
bullet collides with player and enemy
Handling bullet collisions between player and enemy

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:

// this is in the update function
healthBar.width = (hero.health / hero.maxHealth) * 100;
chapter 2 - health bar

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.

sample level design
sample level design

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:

// this is inside game's update function
if (hero.collidesWith(greenDoor))
{
 changeLevelTo(greenDoor.nextLevel);
}
change level when touches green door
Change levels when we touch the green door

Initializing A New Level

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

// this is the function that's triggered when the new level is loaded
function onStart()
{
 hero.currentLevel = scene.LevelName;
 hero.x = startPos.x;
 hero.y = startPos.y;
}
chapter 3 - start of layout

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:

// the function that's triggered when the hero is destroyed
Function onDestroyed()
{
 // revives hero if the hero still have lives.
 If (hero.lives > 0)
 {
 var newHero = new Hero();
 newHero.x = startPos.x;
 newHero.y = startPos.y;
 }
}

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:

// this code is inside the boss update function, so it's executed every frame
if (elapsedTime() > 2.0)
{
 // this if block is executed for 3 seconds, because the difference in time with the if block below
 // is three seconds.
 BossShot1(); // shot variation to be executed this time
}
else if (elapsedTime() > 5.0)
{
 bossShot2();
}
else if (elapsedTime() > 6.0)
{
 bossShot3();
}

if (elapsedTime() > 7.0)
{
 // reset the time so the boss executes the first action again
 resetsTime();
}
chapter 3 - boss AI

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

function bossShot1()
{
 // a simple straight shot
 bossEnemy.shoot("path/to/bullet/sprite.png", bossPower, 400, shotAngle); // shotAngle is 180
}

function bossShot2()
{
 // a three direction bullets shot
 bossEnemy.shoot("path/to/bullet/sprite.png", bossPower, 400, shotAngle);
 bossEnemy.shoot("path/to/bullet/sprite.png", bossPower, 400, shotAngle + 30);
 bossEnemy.shoot("path/to/bullet/sprite.png", bossPower, 400, shotAngle - 30);
}

function bossShot3()
{
 // for this one, I'm going to make a circle shot, so the bullets will form a circle
 for (var i = 0; i <= 9; i++)
 {
 bossEnemy.shoot("path/to/bullet/sprite.png", bossPower, 400, 36 * i); // change the shotAngle
 }
}

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:

function bossShot1()
{
    // a simple straight shot
    bossEnemy.shoot("path/to/bullet/sprite.png", bossPower, 400, shotAngle); // shotAngle is 180
}

function bossShot2()
{
    // a three direction bullets shot
    bossEnemy.shoot("path/to/bullet/sprite.png", bossPower, 400, shotAngle);
    bossEnemy.shoot("path/to/bullet/sprite.png", bossPower, 400, shotAngle + 30);
    bossEnemy.shoot("path/to/bullet/sprite.png", bossPower, 400, shotAngle - 30);
}

function bossShot3()
{
    // for this one, I'm going to make a circle shot, so the bullets will form a circle
    for (var i = 0; i <= 9; i++)
    {
        bossEnemy.shoot("path/to/bullet/sprite.png", bossPower, 400, 36 * i); // change the shotAngle
    }
}

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.


Viewing all articles
Browse latest Browse all 728

Trending Articles