Tutorial 1 – Object Model

Objects in Javascript

Javascript is not a classic object oriented programming language, if you are used to one, you may want to take extra care to keep in mind it’s small pecularities. If you are new to OO programming this shoulnd’t bother you that much. We won’t go too deep into the detail so if you feel the need, there are some great tutorials and documents on the net about this.

Object Oriented programming is a way to regroup your code around the logical entity it relates to and to manage what you do and what you don’t want to show to the user of you code (encapsulation). The description of what the features of an entity are (attributes) and how this entity reacts to the outher world (methods) is called a class. The actual value of a unique entity is called an object. An object is an instanciation of a class. When you instanciate a class you make a call to what’s called a constuctor. To create an instance of a class you use the new keyword, just like you did for animations in step 1.

var myObject = new myClass(some, important, arguments);

In javascript an object is an instance of a function, yes you read right, a function! so when you define a class it look exactly like when you write a function. In fact, when you write a class in javascript you sorte of wite it’s constructor. A very simple class would look like this:

var myClass() = function(var some, var important, var arguments){
           this.someVisilbeAttribute = some + arguments;
           someLessVisiblAttribute = important + arguments;
           this.tickleMe = function(var name){
              alert("hello "+name+" you should now that "+this.someVisibleAttribute);
           };
           return true;
        }

This code defines a class called myClass with two attributes named someVisibleAttribute and someLessVisibleAttribute and a methode named tickleMe. At the same time in this code we defined the way the constructor works, defining the values of the object attributes with a combination of the constructor argument.

As you can see in this example there are many ways to delcare attributes in the class. When you use the var keyword you create a local variable in the object this means that any code that you executes in the object (like methodes) can access the attribute by it’s name. By using the this keyword you make the attribute visble from the outside of the object too. This doesn’t mean that there isn’t any way to access to the attributes defined with the var keyword from the outside, just that it’s harder and less straight forward. The code below show you how to access to the visible attribute from outside of the object:

myObject.someVisibleProperty = "a new value";
        alert(myObject.someVisibleProperty)

Furthermore it may be usefull to know that the variable that you passes to the constructor remains accessible for the code executed in the object like if they have been declared with var.

You can change the object after it has been instanciated to add some new attributes or method. When you do this you have to choose if you want to change every object of the same class or just this object. The following example shows the latter

myObject.lastMinuteMethode = function(){/*Do something here*/};
        myObject.aNewProperty = "aValue";

Sometime you may want to change not just one object but all of it’s “sibling” too. To do this you can use the object’s prototype. The prototype is like the class definition of the object and can be accessed throught the .prototype attribute. Any instance of the same class share the same prototype so when you change something in one object you change it for all instance.

var firstObject = new myClass(1,2,3);
        var secondObject = new myClass(4,5,6);
        firstObject.prototype.aNewMethode = function(){alert("I'm new");}
        secondObject.aNewMethode();

Inheritance in javascript

When a class inherit from another it receives all the methode and attributes from the its parent. Then it can add or rewrite some methode to differentiate itself. Inheritance as many forms and limitaions depending on the language you use. For javascript inheritance is not realy part of the language so many hacks exist to make it happends. Which hack you choose to use depend of which parts of inheritance features you want.

For this tutorial we use a very simple and limited form of javascript inheritance. But I think anything more would be an overkill. This methode consist in injecting an instance of one class into another. Then you can extend the class by modifying the prototype without impacting the parent.

function inheritingClass(){};
        inheritingClass.prototype = new myClass();
        inheritingClass.prototype.soneOnlyMethode = function(){/*Do something here*/};
        inheritingClass.prototype.tickleMe = function(){/*Change the behavior of the parent methode*/};

For what we want to do here that’s all you need to know BUT if you consider writing a more complex programme, or in another OO language I recomend that you look into this more in detail!

Player Object

The player is a simple class that will have only one instance (since it’s a single player game) so using a OO notation here is only usefull to make the code easier to read.

In the player object we will need attributes to describe the player’s spaceship state, for example does it have some sheild left or some life left? We will also have some methode to help us do some basic action on the ship. Since in this game we don’t use a physic engine to manage the player movement you just need to increment / decrement the position, I choosed not to have any methodes to manage the space ship movements.

The first methode, damage() is a way to deal with damage. When a projectile hits the space ship we will call this methode and depending on the returned value we will know if the ship has died or not. The methode hides the management of the sheild from the outside. If you’re wondering about the variable-- notation, it means variable = variable - 1 and works for other opertators too.

function Player(node){

           this.node = node;
          //this.animations = animations;

          this.grace = false;
          this.replay = 3; 
          this.shield = 3; 
          this.respawnTime = -1;
            
          // This function damage the ship and return true if this causes the ship to die 
          this.damage = function(){
            if(!this.grace){
              this.shield--;
              if (this.shield == 0){
                return true;
              }
              return false;
            } 
            return false;
          };
        //...

The second methode, respawn() takes care of what happens when the space ships dies (the previous methodes returned true) and the player has some life left. In this situation the shield must be replenished and the player is reapearing in a grace mode where enemies can not hit him. The grace periode last only for 3 seconds so we need to mesure the time when it as been activated in order to desactivate it after the right time, that’s what the this.respawnTime = (new Date()).getTime(); line does.

To give the player a feedback that he is in the grace periode we make the spaceship slightly transparent with jQuery’s fateTo() methode. This methode take as first argument the time it should take to reach that opacity (here imediatly so 0ms) and the second argument is the opacity we want to optaine (where 1 means not transparent at all and 0 totaly invisible).

//...
          // this try to respawn the ship after a death and return true if the game is over
          this.respawn = function(){
            this.replay--;
            if(this.replay==0){
              return true;
            }

            this.grace 	= true;
            this.shield	= 3;

            this.respawnTime = (new Date()).getTime();
            $(this.node).fadeTo(0, 0.5); 
            return false;
          };
        //...

The last part of the class take care of checking if the grace is over. This methode should be called at every timestep. The return true is the only way to end porperly an object in javascript.

//...
          this.update = function(){
            if((this.respawnTime > 0) && (((new Date()).getTime()-this.respawnTime) > 3000)){
              this.grace = false;
              $(this.node).fadeTo(0, 1); 
              this.respawnTime = -1;
            }
          }
          
          return true;
        }

Enemies Objects

We have three kind of enemy and they will all have common ancestor. We will write in this class all the default behaviour and specialise them for each inheriting class the method we want. Since we want latter to call each method without having to think which class the object is an instance of, we will only overwrite existing method in the child classes. Like the player the enemies need a method to manage the damage they can take. You will notice that’s it exactly the same method that the one from Player except for the grace check.

function Enemy(node){
          this.shield	= 2;
          this.speedx	= -5;
          this.speedy	= 0;
          this.node = $(node);
          
          // deals with damage endured by an enemy
          this.damage = function(){
            this.shield--;
            if(this.shield == 0){
              return true;
            }
            return false;
          };
        //...

The enemies have an automated movement control with some very basic logic. We will then need some method to make the enemies move. Here we have done a main update method that calls a method for the movement on each axis. This allows a sub-class to overwrite only one of them easily. You can see that the default behaviour is to move at a constant speed along each axis. If you look at the updateX and updateY method you will recognise the same kind of statement we use to make the background move.

//...
          // updates the position of the enemy
          this.update = function(playerNode){
            this.updateX(playerNode);
            this.updateY(playerNode);
          };	
          
          this.updateX = function(playerNode){
            var newpos = parseInt(this.node.css("left"))+this.speedx;
            this.node.css("left",""+newpos+"px");
          };
          this.updateY= function(playerNode){
            var newpos = parseInt(this.node.css("top"))+this.speedy;
            this.node.css("top",""+newpos+"px");
          };
        }
        

The family tree of the enemies is shown in the figure bellow. When you use inheritance the aim is to reduce code redundancy. So when two object have a behaviour in common it make sense to make one inherit from the other.

Minion

The minion is not very different from the default enemy, it only tries to stay on the screen but that’s all. So the code is pretty straight forward:

function Minion(node){
          this.node = $(node);
        }
        Minion.prototype = new Enemy();

        Minion.prototype.updateY = function(playerNode){
          var pos = parseInt(this.node.css("top"));
          if(pos > (PLAYGROUND_HEIGHT - 50)){
            this.node.css("top",""+(pos - 2)+"px");
          }
        }

Brainy

For the Brainies we want more shield, to do this we simply redefine the value in the constructor. Values defined in the sub class overwrite values from the super class and values given into the constructor overwrite them both. This category of enemies are a little bit smarter, they will try to aligne themself with the player. To allow this we overwirte the updateY function so that the vertical speed is added or substracted depending of the relative position of the player. Nothing new here. Notice that the Brainy class doesn’t inherite from the Minion but from the Enemy class.

function Brainy(node){
          this.node   = $(node);
          this.shield = 5;
          this.speedy = 1;
          this.alignmentOffset = 5;
        }
        Brainy.prototype = new Enemy();
        Brainy.prototype.updateY = function(playerNode){
          if((this.node[0].gameQuery.posy+this.alignmentOffset) > $(playerNode)[0].gameQuery.posy){
            var newpos = parseInt(this.node.css("top"))-this.speedy;
            this.node.css("top",""+newpos+"px");
          } else if((this.node[0].gameQuery.posy+this.alignmentOffset) < $(playerNode)[0].gameQuery.posy){
            var newpos = parseInt(this.node.css("top"))+this.speedy;
            this.node.css("top",""+newpos+"px");
          }
        }

Bossy

The Bosses will have the same kind of behaviour as the brainy, they will aligne with the player. But we change something: they will stop to move toward the player at some point on the screen to let time to the player to kill them (since they have much more shield).

function Bossy(node){
          this.node   = $(node);
          this.shield = 20;
          this.speedx = -1;
          this.alignmentOffset = 35;
        }
        Bossy.prototype = new Brainy();
        Bossy.prototype.updateX = function(){
          var pos = parseInt(this.node.css("left"));
          if(pos > (PLAYGROUND_WIDTH - 200)){
            this.node.css("left",""+(pos+this.speedx)+"px");
          }
        }

The enemies generation, destruction and missile fireing are taken care of outside of the objects, look at the next step to see how!

next step: Game Logic and Control

Fork me on GitHub