Introduction
This article will be the last of this writing session, about Game Development, on CodeProject! My past working weeks were filled with too game development tasks and I would like to share with you many curiosities that I discovered.
As the article title says, in this article, we will investigate the basis of the horizontal side-scrolling games that you surely know with the names "Super Mario Bros", "Contra", "Kung fu Master" and so forth.
In order to avoid legal issues, we will develop from scratch a horizontal side-scrolling game (from now HSSG) that will be named Super Nemo Fish! In my imagination, Nemo will be a little blue fish that will drive along the sea looking for an ancient treasure. We are going to implement this prototype with jQuery and HTML Canvas in order to make it available on the Internet!
Unfortunately, I am not a great graphic designer, and I will use only pictures that can be found on Google images. So, for the moment, I imagine the first level like the following picture. I don't understand how to embed YT video in the article, please let me know if it is possible.
Background
For the purpose of this article, you need to know the basis of game developing (at least the subdivision between the drawing time from the logic time).
The first thing that characterizes a HSSG is the scrolling: We need to make available to the player an action that can scroll the level between start and end positions. Of course, we can't draw each meter of the level (we are not developing a full 3D game like GTA San Andres) so we will use some tweaks in order to give the impression of a movement in a 2D world.
Our first goal will be the implementation of the infinite horizontal scroll: For this purpose, we need to use an image that can be looped without ugly imperfection when we are merging the right side and left side. Following, you can use a link to a video on youtube with the infinite scroll implemented.
View YT Video
Our second goal will be the implementation of the player runtime, an object that will handle all the player actions in the level. As you can imagine, our player will be a little fish that can go forward (from left to right) and that can jump out of sea in order to catch some things.
Following, again, you can use a Youtube link in order to view the level runtime in action.
View YT Video
Our third goal will be the implementation of the level runtime, an object that will handle all the objects in the level. The level runtime must draw the object like obstacles, enemies and so forth using the current player position. Following, again, you can use a Youtube link in order to view the level runtime in action.
View YT Video
About Player Position and Level Drawing
My idea behind level runtime is naive. First, the player starting position is (0, 100). In the previous video preview, you can view our awesome yellow fish in idle time. As you can see in the previous video, fish can jump, so fish can change it own y coordinate following a jumping curve (thanks to g-force). Finally, fish will be drawn everytime in (0,y) position. The other object will change their (x,y) coordinates, but fish will remain fixed in x=0.
I just divided the level in three layer (sea-top layer, middle water layer and bottom layer). Then, I used 3-dimensional array in order to express the level-object position. I just considered the position x of an object as it array-index*2meter. So, we can have at most 3 objects in each position x, one of them with y coordinate in one of three level layers. I attached a pictures of the object at index 0 (2 mt from start).
The level manager will handle the position of all other object in the scene. We need to know that each object (except player) will scroll from the right to the left of the screen. Also, all objects will appear when the player reaches a distance D (remember array-index*2mt). Then, when we are looking for collision, we just consider the real coordinate of the object drawn on the screen and not the relative object (distance from starting point).
In the last video, you can see the LevelManager
operating on 2 kinds of objects: a coin that can feed the fish and a fish hook that will remove feed from fish. Pay attention to the output console in order to see when the collision appears.
Play the Game
You can play online the prototype here. Also, you can download the prototype by the direct link at the top of the page.
Using the Code
For the first, we need to load all the images that we want to use with Canvas
. This is because we are not able to call the drawImage
function on a partial object. I know, the following code is not a "well of science", but do what it has to do. The following code loads the image in sequential mode.
var levelBackground = new Image();
var levelSky = new Image();
var coinImage = new Image();
var enemyImage = new Image();
var player = new Image();
levelBackground.src='./undersea.png';
levelBackground.onload= function() {
levelSky.src='./sky.jpg';
{
levelSky.onload= function () {
player.src="./fish.png";
player.onload= function() {
coinImage.src="./fish-feed.png";
coinImage.onload=function() {
enemyImage.src="./enemy.png";
enemyImage.onload=function () {
_initGameObject()
}
}
};
};
};
}
Then, we need to set up the game environment.
var keyboardMap = {39:false, 32: false};
var ctx= null;
var playGame = null;
var feedCatched = 0;
var distance = 0;
var startTime = null;
var delta = 0;
var Level1 = [["C"],["E"],["C"]];
var LevelManager = [];
var cWidth=0;
var cHeight=0;
var playerObject = null;
var seaGradient= null;
var sBlueColor = 231;
var sVerse = 0;
function _waveGradientUpdate()
{
seaGradient = ctx.createLinearGradient(0,0,0,cHeight);
seaGradient.addColorStop(0,"rgb(138,212,"+sBlueColor+")");
seaGradient.addColorStop(1,"#ffffff");
if (sVerse==0)
sBlueColor-=3;
else
sBlueColor+=3;
if (sBlueColor<=180) sVerse=1;
if (sBlueColor>=255) sVerse=0;
}
The most important pieces of code are the following: the playerObject
runtime and the gameObject
runtime. In the following function, you will see in which manner I handle the behaviour of the player, but you can choose any other code pattern. Please keep in mind that the drawing time is not related to the logic time. In a real game, you have to write a thread in which all the objects lie and other thread in which all drawing functions will be called.
function createPlayer(sX,sY)
{
return {
x:sX,
y:sY,
currentFrame:0,
currentFrameY:0,
totalForwardAmount:0,
srcImage:null,
state:0,
isJumping:false,
isForwarding:false,
forwardSpeed:0,
jumpSpeed:0,
jump: function()
{
if (this.isJumping) return;
this.jumpSpeed=15;
this.isJumping=true;
},
startForward: function()
{
this.forwardSpeed=0.5;
this.isForwarding=true;
},
stopForward: function()
{
this.totalForwardAmount=0;
this.currentFrame=0;
this.isForwarding=false
},
drawPlayer: function(ctx,delta)
{
if (this.forwardSpeed>0) {
this.x += this.forwardSpeed*delta;
distance += (delta*this.forwardSpeed)/1000;
if (this.x>=cWidth)
this.x = 0;
if (!this.isForwarding)
this.forwardSpeed-= 0.002*delta;
this.totalForwardAmount+=delta;
if (this.totalForwardAmount>=75) {
this.currentFrame=0;
this.totalForwardAmount=0;
}
else if (this.totalForwardAmount>=50)
this.currentFrame=2;
else if (this.totalForwardAmount>=25)
this.currentFrame=1;
}
if (this.isJumping) {
this.currentFrameY=1;
if (this.jumpSpeed > 0)
this.currentFrame=0;
else
this.currentFrame=1;
this.y -= (delta*this.jumpSpeed)/10;
this.jumpSpeed -= (0.05*delta);
if (this.y>=400)
{
this.isJumping=false;
this.jumpSpeed=0;
this.y=400;
this.currentFrameY=0;
}
}
ctx.drawImage(this.srcImage,this.currentFrame*300,
this.currentFrameY*270,300,256,100,this.y,100,100);
}
};
}
I avoid pasting any other pieces of code because you can download the example and analyze the full prototype.
Points of Interest
I need to add element to Level1
array in order to add game object in the level. You only can use C (Coin
) and E (Enemies
).
History
- 31st October, 2016: Published