History of Losharik

Foreword


imageIt was Thursday evening when my colleague spiff got the idea to write an OpenSource game using HTML5, which is progressing nowadays, as they say, from scratch and just for fun.

Since we were working in the field of system programming and we had very little experience in developing web applications, it was decided to implement a fairly simple clone of the world famous and popular game for the first Nokia phones - RapidRoll .

A week later, we released the first stable release , and are ready to share our first experience.



Step 1. Design document


Any game that will conquer the world, in our opinion, should begin with a design document. It usually consists of the following items:
  • scheme of the game (gameplay, the purpose of the game and what hinders its achievement);
  • interface (main elements of graphics);
  • game mechanics (device of the game world, physics, interaction of objects, etc.);
  • software algorithms (high-level description of the main game algorithms);
  • sounds and music;
  • game world (game characters and their interaction);
  • participants and dates;

The meaning of the game is to keep Losharik in the visible part of the screen, by moving it on rising platforms, preventing it from falling or leaving the upper border of the screen.

Link to design document

Step 2. Architecture


The architecture of the application is shown in the following diagram:

image

As you can see, the diagram resembles an MVC model. The following main classes are available:
  • Game The main class of the game, is responsible for the interaction of other classes;
  • GameModel The game model is responsible for the formation of game scenes;
  • KeyboardController One of the possible controllers to control the game from the keyboard;
  • HTML5View. One of the possible representations of the game. Draws scenes created by an HTML5 canvas model based on resources.
  • Resources. Stores game resources such as sounds, sprites, etc.

The implementation of the architecture skeleton is presented below:

  function GameModel() {
  this.setView = function(v) { view = v; };
  this.getView = function() { return view; };
  this.moveLeft = function() { ... };
  this.moveRight = function() { ... };
  this.next = function() {
    // build next/new frame
    ...
    view.update(); //update view            
  };
}
function HTML5View(canvas) {
  var model;
  this.setModel = function(m) { model = m; };
  this.getModel = function() { return model; };
  var context = canvas.getContext("2d");
  this.update = function() {
    // clear view
    ...
    // drawing model to canvas
    ...
  };
}
function KeyboardController() {
  var model = null;
  this.setModel = function(m) { model = m; };
  this.getModel = function() { return model; };
  this.keydown = function(event) {
    if (event.keyCode == LEFT_KEY_CODE) {
      model.moveLeft();
    } else if (event.keyCode == RIGHT_KEY_CODE) {
      model.moveRight();
    }
  };
  document.onkeydown = this.keydown;
}
function Game(model, view, ctrl) {
  view.setModel(model);
  ctrl.setModel(model);
  model.setView(view);
  this.run = function() {
    setInterval(model.next, 1000 / DEFAULT_FPS);
  };
}
function main(canvas) {
  var view = new HTML5View(canvas);
  var model = new GameModel();
  var ctrl = new KeyboardController();
  var game = new Game(model, view, ctrl);
  game.run();
}


Step 3. The first prototype


A day later, the first working prototype was received, with a size of 150 lines of HTML5 code.

image

Algorithms for generating platforms, moving Losharik and the world were added to the code of the framework. And it was already possible to play, although there were problems with physics and checking for hits on the platforms.

The main “chip” of the prototype was the platform generation algorithm - new platforms are generated after the destruction of the top one and this provides the ability to dynamically change the number of platforms on the screen and the distance between them. The algorithm code is presented below:

var generatePlatforms = function() {
  do {
    var type = (Math.random() 0.6) ? PLATFORM_TYPE.SOLID :
      PLATFORM_TYPE.KILLER;
    var baseline = pCounter > 0 ? platforms[pCounter - 1].y : 0;
    platforms[pCounter++] = {
      x: Math.floor(Math.random() * (WIDTH - DEFAULT_PLATFORM_WIDTH)),
      y: Math.floor(baseline + (Math.random() *
        (DEFAULT_MAX_PLATFORM_INTERVAL - DEFAULT_MIN_PLATFORM_INTERVAL + 1)) +
        DEFAULT_MIN_PLATFORM_INTERVAL),
      w: DEFAULT_PLATFORM_WIDTH,
      h: DEFAULT_PLATFORM_HEIGHT,
      type: type
    };
  } while (platforms[pCounter - 1].y < (HEIGHT + DEFAULT_PLATFORM_HEIGHT));
};


Step 4. The second prototype


In the second prototype, the algorithm for checking hits on platforms was revised and rewritten to the method for checking the intersection of segments. First, based on the previous Losharik, a new one is built, taking into account the alleged displacement in space. The coordinates of the new, current Losharik and the platform under test are transferred to a function that searches for the intersection points of P1 and P2 and, if they are found, returns true, taking this into account, the decision is made whether to attach Losharik to the platform or not.

image

Later, the function was optimized and performed only for those platforms that may fall into the intersection.

As a result, such a code appeared:

var isRollCrossPlatform = function(platform, rollPrev, rollNext) {
  // we should to check only platforms between prev roll and next roll
  if (platform.y <= rollPrev.y || platform.y > rollNext.y + rollNext.h) return false;
  var s1 = { x1: platform.x, y1: platform.y,
  x2: platform.x + platform.w, y2: platform.y };
  var s2 = { 
    x1: rollPrev.x, y1: rollPrev.y + rollPrev.h - worldSpeed, 
    x2: rollNext.x, y2: rollNext.y + rollNext.h 
  };
  var s3 = { 
    x1: rollPrev.x + rollPrev.w, y1: rollPrev.y + rollPrev.h - worldSpeed, 
    x2: rollNext.x + rollNext.w, y2: rollNext.y + rollNext.h 
  };
  var zn1 = (s2.y2 - s2.y1) * (s1.x2 - s1.x1) -
    (s2.x2 - s2.x1) * (s1.y2 - s1.y1);
  var zn2 = (s3.y2 - s3.y1) * (s1.x2 - s1.x1) -
    (s3.x2 - s3.x1) * (s1.y2 - s1.y1);
  if (Math.abs(zn1) < Math.EPS &&
    Math.abs(zn2) < Math.EPS) return false;
  var ch11 = (s2.x2 - s2.x1) * (s1.y1 - s2.y1) -
    (s2.y2 - s2.y1) * (s1.x1 - s2.x1);
  var ch21 = (s1.x2 - s1.x1) * (s1.y1 - s2.y1) -
    (s1.y2 - s1.y1) * (s1.x1 - s2.x1);
  if ((ch11/zn1 <= 1.0 && ch11/zn1 >= 0.0) &&
     (ch21/zn1 <= 1.0 && ch21/zn1 >= 0.0)) {
    return true;
  } 
  var ch12 = (s3.x2 - s3.x1) * (s1.y1 - s3.y1) -
    (s3.y2 - s3.y1) * (s1.x1 - s3.x1);
  var ch22 = (s1.x2 - s1.x1) * (s1.y1 - s3.y1) -
    (s1.y2 - s1.y1) * (s1.x1 - s3.x1);
  if ((ch12/zn2 <= 1.0 && ch12/zn2 >= 0.0) &&
     (ch22/zn2 <= 1.0 && ch22/zn2 >= 0.0)) {
    return true;
  }
  return false;
};


In the second prototype various types of platforms were added: moving, killing and melting and the ability to move beyond the side borders of the screen.

Step 4. Third prototype


An algorithm for calculating and drawing the number of points that are multiples of the distance traveled in free fall was implemented, as well as a life counter and the ability to lose this very life.

image

Step 5. Release!


Almost a week has passed since the start of the project, and we already had a fully functional working prototype, which until the release needed to change only one class - HTML5View and add a few additional features:
  • The background of the world has been replaced by a full-fledged relief moving uniformly to the platforms;
  • High score table;
  • Like button for integration with VK;
  • Implemented minimal animation for Losharik in the form of two sprites (“Losharik moves to the left”, “Losharik moves to the right”);
  • Added FPS counter;

All drawing graphics in the form of canvas shapes, has been replaced by rendering sprites (images).

context.fillRect(x, y, width, height) -> context.drawImage(roll, x, y, width, height)


It was decided to get the value of the FPS counter based on the last 10 frames and the total time they were drawn. Ultimately, the counter code looks like this:

// draw current fps
var fps = (frameCounter / totalTime) * 1000.0;
context.fillText(fps.toFixed(2) + " fps", 4, HEIGHT - 6);
// calc FPS
if (frameCounter > FPS_REFRESH_INTERVAL) { frameCounter = 0; totalTime  = 0; }
frameCounter++;
var currentTime = new Date().getTime();
totalTime += (currentTime - lastTime);
lastTime = currentTime;


In addition, the release worked out the coefficients of the physics of the game - gravity, X acceleration for more confident control and changed the moments of difficulty switching (levels) for a longer game.

image

Statistics


In one week, a working Losharik release was received containing 1130 lines of code:
  • 163 in PHP for the server side;
  • 50 on CSS for menus and buttons;
  • 71 on HTML5 for display;
  • 846 on JS for the game engine.

Plans for Losharik 2.0


  • Recognizable logo;
  • New bonuses (parachute, core, body armor, ...);
  • Improved physics and performance;
  • Monsters
  • New types of platforms (wooden / fragile, ...);
  • Completely redesigned design and graphics;
  • Sounds (HTML5 audio);
  • Social component (Twitter / FB / Google+);
  • Build for Google web-store (Chrome app);
  • Builds for: Andriod app / WAC app / IPhone app (WebGL implementation);

Instead of a conclusion


We would like to make a small announcement. As you can see, Losharik needs a good designer in the team to conquer the world. If someone has a desire to participate in an interesting OpenSource project and pump their skills, you are welcome to leave comments or contact spiff .

PS Once again, the link to PLAY!
PPS Losharik on Googlecode .

UPDATE: thanks to the qmax hub user for a patch that adds rotation to Losharik!

Also popular now: