0

I'm currently working on a platform game engine using javascript and the HTML5 canvas.

I have an object, "platform" which looks something like this...

var platform = function(pid,px,py,pw,ph) {
  //Some variables here... and then we have some functions    
  this.step = function() {
    //Update / step events here
  }
  this.draw = function() {
    //Drawing events here
  }
  //etc.
}

The step() function has all of the calculations for collision detection while the draw() function draws the platform.

What I want to do is make another object called movingPlatform. This will be almost identical to the current platform except for the fact this one moves.

Rather than copying all of the collision detection code I'd like to be able to extend movingPlatform from platform... and then be able to add some additional code into the step() function to the moving platform can... well... move.

Some additional information...

When the game loads, it generates the level using data from a CSV file. I have an array, platforms[] that stores all of the platforms within it.

So to create a platform it looks like this...

platforms.push(new platform(i,data[1],data[2],data[3],data[4]));

I then make the platforms perform their step and draw events during the game's main step and draw events.

i.e.

for(var i=0; i<platforms.length; i++) {
platforms[i].step();
}   

Any help would be awesome. Thanks!

  • 1
    Read about "prototype inheritance" in JavaScript. – elclanrs Sep 20 '13 at 03:42
  • By convention, constructor functions (ie- those that you call with `new`) should be capitalized. – Andrew Eisenberg Sep 20 '13 at 03:44
  • @elclanrs Prototyping this might not be the way to go. At least, not in the true JS fashion: `var platform_instance = new Platform(texture, x, y, width, height); MovingPlatform.prototype = platform_instance; var mPlatform = new MovingPlatform();`. They'll all have the same textures, coordinates, et cetera. Extending an object-constructor's prototype, if you can ensure that all relevant object-properties are public, sure, that's a great memory-saver. But "inheriting" via making all child instances share the prototype of a single parent instance doesn't help, here. – Norguard Sep 20 '13 at 03:45
  • @elclanrs prototype is for functions as they are the same for all instances or for default values that are later shadowed by the instance. To inherit instance specific values you can do: `function Child(){Parent.apply(this,arguments);` About prototype, inheritance overriding methods and calling super: http://stackoverflow.com/a/16063711/1641941 – HMR Sep 20 '13 at 14:52

4 Answers4

2

I would use the platform class as a "base" object for the moving platform object. I would do this via the prototype which is JavaScript's implementation of object oriented programming.

More info here How does JavaScript .prototype work? + many more articles on the web

Community
  • 1
  • 1
TGH
  • 38,769
  • 12
  • 102
  • 135
0

You can use Javascript prototype inheritance functionality:

var baseItem = {
    push: function(){alert('push');},
    pull: function(){alert('pull')}
}

var childItem = {}
childItem.prototype = baseItem;

childItem.push = function(){
    //call base function
    childItem.prototype.push.call(this);

    //do your custom stuff.
    alert('I did it again.');
}

childItem.push();

Fiddle

bluetoft
  • 5,373
  • 2
  • 23
  • 26
  • Aha, thanks for the great example, it clears prototyping up quite well. – Andrew Natoli Sep 20 '13 at 04:33
  • Or just use prototype that comes with constructor functions: http://stackoverflow.com/a/16063711/1641941 Object literals don't have prototype so they don't automatically inherit methods that you don't want to override, causing you to declare all these methods again in the Child just to manually call it's parent. Prototype for constructor functions is also optimized for method lookup in most JS engines. – HMR Sep 20 '13 at 14:57
  • @AndrewNatoli No, it does not clear up prototyping, you might as well define the inheritance as `childItem.santaclaus=baseItem` as a matter of fact; it has nothing to do with the prototype object that javascript functions have. – HMR Sep 20 '13 at 14:59
0

Getting inheritance right in Javascript is somewhat tricky if you're used to class-based languages.

If you're not sharing a lot of behaviours, you might find it easier to just create some shared methods, then make them available to objects of each platform type.

//Create constructors for each type
var Platform = function(pid,px,py,pw,ph) { //By convention, constructors should start with an uppercase character
    ...
}

var MovingPlatform = function() {
    ...
}

//Create some reuseable methods
var step = function() {
    ...
}
var draw = function() {
    ...
}
var move = function() {
    ...
}

//Attach your methods to the prototypes for each constructor
Platform.prototype.step = step;
Platform.prototype.draw = draw;

MovingPlatform.prototype.step = step;
MovingPlatform.prototype.draw = draw;
MovingPlatform.prototype.move = move;

...etc

That said, if you do want to build up a proper inheritance chain, there are plenty of articles available to help you: 1 2 3 4

ThinTim
  • 565
  • 3
  • 8
  • This looks like it may be the best method for what I'm trying to do... I'll give it a try tomorrow, thanks! – Andrew Natoli Sep 20 '13 at 04:25
  • Note that this approach is generally more suited to situations where you want to add a basic utility method to a group of object types. In situations such as yours where you have a hierarchy of types, it's cleaner to use a proper inheritance chain, so you'd be well served by taking to time to read up on how it works. – ThinTim Sep 20 '13 at 05:11
  • In fact I would encourage you to ignore my answer for now, and read the links provided in the other answers here, and at the bottom of my answer before making your decision. The solution I gave will work and is easy to grasp, but it's harder to extend and maintain in the future, and doesn't reflect the logical hierarchy of your objects. – ThinTim Sep 20 '13 at 05:31
0

Rather than pure inheritance, here, I'd go with prototype-extension, unless you build some big, ugly factory, just for the sake of saying that "MovingPlatform" inherited from "Platform" in a pure sense, it's not really what you'd expect it to be.

There are a few concerns (cheating, for one), but if your objects are all based wholly around this, and you're okay with people potentially hacking away in the console, then you don't really have much to worry about.

First, understand what you're doing inside of Platform:

var MyObject = function (a) {
    this.property = a;
    this.method   = function (b) { this.property += b; };
};

Every time you make a new MyObject, you're creating a brand new version of the .method function.
That is to say, if you make 10,000 of these, there will be 10,000 copies of that function, as well.
Sometimes that's a very good and safe thing.
It can also be a very slow thing.

The problem is, because everything in your object is using this, and because nothing inside of the function changes, there's no benefit to creating new copies -- just extra memory used.

...so:

MyObject = function (a) {
    this.property = a;
};
MyObject.prototype.method = function (b) { this.property += b; };

var o = new MyObject(1);
o.method(2);
o.property; //3

When you call new X, where X has properties/methods on its prototype, those properties/methods get copied onto the object, during its construction.

It would be the same as going:

var method = function (b) { this.property += b; },
    o = new MyObject(1);

o.method = method;
o.method(2);
o.property; // 3

Except without the extra work of doing it yourself, by hand.
The benefit here is that each object uses the same function.
They basically hand the function access to their whole this, and the function can do whatever it wants with it.

There's a catch:

var OtherObj = function (a, b) {
    var private_property = b,
        private_method = function () { return private_property; };

    this.public_property = a;
    this.unshared_method = function () { var private_value = private_method(); return private_value; };
};

OtherObj.prototype.public_method = function () {
    return private_property;
};

var obj = new OtherObj(1, "hidden");

obj.public_property;    // 1
obj.unshared_method(); // "hidden"
obj.public_method();  // err -- private_property doesn't exist

So assuming you don't have much you care about staying private, the easiest way of doing this would be to make reusable function, which rely on this, which you then give to multiple prototypes, through extension.

// collision-handling
var testCollision   = function (target) { this./*...*/ },
    handleCollision = function (obj) { this./* ... */ };

// movement-handling
var movePlatform = function (x, y, elapsed) { this.x += this.speed.x*elapsed; /*...*/ };
// not really the cleanest timestep implementation, but it'll do for examples


var Platform = function (texture, x, y, w, h) {
        this.x = x;
        // ...
    },
    MovingPlatform = function (texture, x, y, w, h, speedX, speedY, etc) {
         this.etc = etc;//...
    }; 


Platform.prototype.testCollision   = testCollision;
Platform.prototype.handleCollision = handleCollision;

MovingPlatform.prototype. // both of the above, plus the movePlatform method

This is a lot by hand.
That's why functions in different libraries will clone or extend objects.

var bunchOfComponents = {
    a : function () { },
    b : 32,
    c : { }
},

myObj = {};

copy(myObj, bunchOfComponents);

myObj.a();
myObj.b; //32

Your function-reuse goes up, while the horror of writing proper Class-based, hierarchical inheritance, with virtual-overrides, abstracts, and shared-private properties, by hand, goes down.

Norguard
  • 26,167
  • 5
  • 41
  • 49