Click here to Skip to main content
15,885,546 members
Articles / Web Development / HTML

What is Behind Horizontal Side-scrolling Games (like Super Mario Bros)

Rate me:
Please Sign up or sign in to vote.
4.00/5 (2 votes)
31 Oct 2016CPOL5 min read 11.4K   284   6  
In this article, we want to implement a simple side-scrolling game that we will call Super Nemo Fish.

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.

Image 1

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.

Image 2

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.

Image 3

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.

Image 4

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.

JavaScript
// Load game images
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.

JavaScript
// keyboard mapping (handling space and right button)
var keyboardMap = {39:false, 32: false};

var ctx= null;         //canvas 2d context
var playGame = null;   //timer for redrawing
var feedCatched = 0;   // how many feed was caught
var distance = 0;      // distance from starting point

// variables for FPS and delta counting
var startTime = null;                   
var delta = 0;


// THE LEVEL MANAGER
// Please note: each slot correspond to 2meters 
// Also, each there are tree sublevel (for simplicity)
// E = Enemies
// C = Coin

var Level1 = [["C"],["E"],["C"]];
var LevelManager = [];

// Game Width and height
var cWidth=0;
var cHeight=0;

// Player coordinates
var playerObject = null;

// a canvas effect (ignore it if you want, it is only my test)
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.

JavaScript
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)
    {
      // handle forward speed
      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.forwardSpeed<0.1) this.forwardSpeed=0.1; 
          // { this.currentFrame=0; this.totalForwardAmount=0; }
          // handle jump speed
          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

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer BiTS - Softmining
Italy Italy
Luigi Di Biasi is a PhD student in a computer science @ University of Salerno.

He is sole director of Arrowsoft.IT SRLs and CIO of Softmining SRL. He is founder of dibiasi.it materiale elettrico.

He studied Computer Science at the University of Salerno in 2013. From 2014 to 2016 he is [assegnista di ricerca] for PRIN 2010-2011 Data-Centric Genomic Computing (GenData 2020)).

He collaborate to the GRIMD developing (grid for molecular dynamics), which permits the parallel and distributed computation of many kinds of scientific software, to the YADA developing (Yet Another Docking Approach), a tool that improve and refine the predictions made by VINA Autodock and to the implementation of ProtComp alignment free tools, a new algorithm for sequences analysis that outperforms existing methods. He collaborate on 7 scientific paper.

In 2017 he is responsible developer for the RIRIBOX Project (in collaboration with Acea Pinerolese and Ecofficina SRL) and for energii.dk (in collaboration with Peme IVS). All these projects has involved collaborations between Italian, Danish and American developers.

In 2016 he is responsible developer for the “Una Buona Occasione” Project (in collaboration with Ecofficina SRL). He developed all the edutainment games related to this project (16 WebGL games).

Actually, his research activities range on the AI (algorithms developing, applications) and on the game developing.

Comments and Discussions

 
-- There are no messages in this forum --