0

I got the following "Enemy" Class in JavaScript:

function Enemy(position, rotation) {
    this.name = null;

    this.enemyForm = new Kinetic.Rect({
        x: 0,
        y: 0,
        width: 20,
        height: 20,
        fill: 'red',
        stroke: 'black',
        strokeWidth: 1
    });
    this.setX(position.posX);
    this.setY(position.posY);
    this.setRotation(rotation);
    this.setOffset(10, 10);
    this.add(this.enemyForm);
}

Enemy.prototype = new Kinetic.Group();

As you can see, i extend a Kinetic.Group for that because the Enemies will have some more Kinetic Elements and not just a Rectangle.

Now i create some Instances of that "Class" and add them to the game layer:

 var enemy1 = new Enemy({posX: 50, posY: 50}, 0);
 this.layer.add(enemy1);
 var enemy2 = new Enemy({posX: 100, posY: 100}, 0);
 this.layer.add(enemy2);
 var enemy3 = new Enemy({posX: 200, posY: 200}, 0);
 this.layer.add(enemy3);

The problem: Every Enemy gets the position of "enemy3", and not their own. So, every Enemy will be drawn at position "200, 200". Now if i try this without inheritance, it works fine:

function Enemy(position, rotation) {
    this.name = null;
    this.enemyForm = new Kinetic.Group();

    var rect = new Kinetic.Rect({
        x: 0,
        y: 0,
        width: 20,
        height: 20,
        fill: 'red',
        stroke: 'black',
        strokeWidth: 1
    });
    this.enemyForm.setX(position.posX);
    this.enemyForm.setY(position.posY);
    this.enemyForm.setRotation(rotation);
    this.enemyForm.setOffset(10, 10);
    this.enemyForm.add(rect);
}

Can anyone tell me what i am missing, and why i don´t get separate objects with the first method?

andyrandy
  • 72,880
  • 8
  • 113
  • 130
  • could you show what you do inside `setX`, `setY`? – Khanh TO Sep 14 '13 at 12:46
  • that is a kinetic function, not my own. this.setX > call "setX" of the kinetic group. should not be a problem in this case anyway, because the second one works fine with the same functions... – andyrandy Sep 14 '13 at 12:51
  • Better not use `Enemy.prototype = new Kinetic.Group()`, you can inherit without creating an instance of the parent (see link at the end) in Enemy body can do 'Kinetic.group.call(this)` that will set any variable 'this.something' to be the Enemy instance property Prototype is shared among instances but anything in the function body is instance unique. How Parent.call(this) works is explained here: http://stackoverflow.com/a/16063711/1641941 – HMR Sep 15 '13 at 08:00
  • why the downvote? please tell me if there is something i whould do to improve my question. – andyrandy Nov 15 '16 at 13:36

3 Answers3

2

No problem subclassing the Kinetic.Group:

// a bunch of code from 
// http://d3lp1msu2r81bx.cloudfront.net/kjs/js/lib/kinetic-v4.6.0.min.js

//optionally a config object can be passed to the constructor
//http://kineticjs.com/docs/Kinetic.Group.html
var Test = function(config){
  Kinetic.Group.call(this,config);
};
Test.prototype=Object.create(Kinetic.Group.prototype);

var test = new Test();

for(s in test){
  console.log(s);
}
HMR
  • 37,593
  • 24
  • 91
  • 160
  • Object.create, nice solution. Is there any fallback/polyfill for the IE8? – andyrandy Sep 15 '13 at 09:26
  • 1
    @luschn I usually use inherits from the closure library http://stackoverflow.com/questions/16063394/prototypical-inheritance-writing-up/16063711#16063711 (under Setting prototype without calling the constructor) but there is a polyfill for it on MDN: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/create#Polyfill – HMR Sep 15 '13 at 11:11
1

I never look at the code of kinetic group. But this problem could happen if the code uses closure to create private variables like this:

Kinetic.Group = function(){
    var setX, getX;
    (function() {
       var X = 0;
       getX = function(){
          return X ;
       };
       setX = function(v){
          X = v;
       };
     })();

    this.setX = setX;
    this.getX = getX;
}

When you declare Enemy.prototype = new Kinetic.Group();, there is only 1 Kinetic.Group created. When you call this.setX(position.posX); inside function Enemy(position, rotation), because this function does not exist in the current instance, it will look up that function in the prototype property (the same Kinetic.Group for all your Enemy). All your instances created by the constructor function share the same variable var X = 0; captured by the closure.

In your second case, this does not happen because you create a new Kinetic.Group for each Enemy.

Update:(after taking a look at the code of Kinetic.Group

In the code, I see the case is different. Each Kinetic.Group maintains a this.attrs = {}; for all your properties and the setX,getX are methods defined on Kinetic.Group.prototype => all your Enemy instances share the same attrs of a Kinetic.Group instance when you use Enemy.prototype = new Kinetic.Group();

I'm afraid that you cannot use inheritance with this code.

Khanh TO
  • 48,509
  • 13
  • 99
  • 115
  • Well, if that is the case, then there is no way to use inheritance with kinetic groups anyway, right? the code is here btw: http://d3lp1msu2r81bx.cloudfront.net/kjs/js/lib/kinetic-v4.6.0.js And shouldn´t there be created a new Kinetic.Group for each Enemy in any case? After all, i just extend the Enemy Class from Kinetic.Group. – andyrandy Sep 14 '13 at 13:08
  • @luschn:`shouldn´t there be created a new Kinetic.Group for each Enemy in any case`. No, when you use `Enemy.prototype = new Kinetic.Group();`, there is only 1 created. – Khanh TO Sep 14 '13 at 13:37
  • alright, then i got it wrong, damn. thank you for your super-sophisticated answer :) - upvoted and marked as correct answer. – andyrandy Sep 14 '13 at 14:08
0

I think if you want to have inheritance in javascript you should also call Group constructor in Enemy constructor. Write it like this:

function Enemy(position, rotation) {

    // it create properties(and probably functions) on with Kinetic.Group depends
    // you could also pass x, y, rotation and offset in argument of this constructor
    Kinetic.Group.apply(this, [{}]);        

    this.name = null;
    this.enemyForm = new Kinetic.Rect({
        x: 0,
        y: 0,
        width: 20,
        height: 20,
        fill: 'red',
        stroke: 'black',
        strokeWidth: 1
    });
    this.setX(position.posX);
    this.setY(position.posY);
    this.setRotation(rotation);
    this.setOffset(10, 10);
    this.add(this.enemyForm);
}

Enemy.prototype = new Kinetic.Group();
  • It may be better to use Object.create or a helper function to set the inheritance (prototype): http://stackoverflow.com/a/16063711/1641941 – HMR Sep 15 '13 at 08:17