0

What is confusing is how this simple script works fine:

function A() {
    this.value = 0;
}
A.prototype.foo = function() {
    console.log(this.value);
};

function B() {
    this.value = 1;
    this.foo();
}
B.prototype = Object.create(A.prototype);
B.prototype.bar = function() {
    console.log(this instanceof A);
}
new B().bar();
// outputs 1, true

However, this larger script gives an error this.loadDimensions is not a function: Basically, there is a Player class, which inherits from a MovingComponent class, which inherits from a VisibleComponent class. They all have methods attached to them.

  const PX_SZ = 4, MAX_HEIGHT = 100, MIN_HEIGHT = 300;
  var resources = {};
  resources.sprites = {};
  resources.sprites.player = new Image();
  resources.sprites.player.src = "resources/sprites/player.png";
  resources.sprites['default'] = new Image();
  resources.sprites['default'].src = "resources/sprites/default.png";
  resources.sprites.items = {};
  resources.sprites.backgroundEntities = {};
  var itemsTemp = ['default', 'coin0'];
  for (var i=0; i<itemsTemp.length; i++) {
      var item = itemsTemp[i];
      resources.sprites.items[item] = new Image();
      resources.sprites.items[item].src = "resources/sprites/items/" + item + ".png";
  }
  var backgroundEntitiesTemp = ['tree0'];
  for (var i=0; i<backgroundEntitiesTemp.length; i++) {
      var ent = backgroundEntitiesTemp[i];
      resources.sprites.backgroundEntities[ent] = new Image();
      resources.sprites.backgroundEntities[ent].src = "resources/sprites/background-entities/" + ent + ".png";
  }

  var canvas, ctx;
  var player = new Player();
  var keys = {};
  var game = new Game(Math.floor(Math.random()*1000000));
  var world = new World();

  /** @class */
  function Game(seed) {
     this.seed = seed;
  }
  /** @class */
  function World() {
     this.gravity = 0.4;
     this.chances = {
         items: {
             coin0: 0.005
         },
         backgroundEntities: {
             tree0: 0.05
         }
     };
     this.itemsFloating = [];
     this.backgroundEntities = [];
     // for spawning
     this.exploredRightBound = 0;
     this.exploredLeftBound = 0;
  }
  World.prototype.generate = function(left, right) {
       if (left >= right) throw "left >= right in World#generate(left,right)";
       for (x = left; x < right; x += PX_SZ) {
           // world generation code here
           // coin0
           var level = getGroundHeightAt(x)
           if (Math.random() <= this.chances.items.coin0) {
              var item = new ItemFloating("coin0", x, level-20);
              this.itemsFloating.push(item);
           }
           if (Math.random() <= this.chances.backgroundEntities.tree0) {
              var ent = new BackgroundEntity("tree0", x, level-resources.sprites.backgroundEntities.tree0.height);
              this.backgroundEntities.push(ent);
           }
       }
 };
  /**
   *    @class
   *    anything that has a sprite attached to it
   */
  function VisibleComponent() {
     this.sprite = resources.sprites['default'];
  }
  VisibleComponent.prototype.loadDimensions = function() {
      console.log('load');
  };
  VisibleComponent.prototype.draw = function() {
     ctx.drawImage(this.sprite, this.x, this.y, this.width, this.height);
  };

  /** @class */
  function Item(name="default") {
   VisibleComponent.call(this);
     this.name = name || "default";
     this.sprite = resources.sprites.items[name];
     this.loadDimensions();
  }
  Item.prototype = Object.create(VisibleComponent.prototype);
  /** @class */
  function ItemFloating(name, x, y) {
   Item.call(this, name);
   this.name = name;
     this.x = x;
     this.y = y;
     this.loadDimensions(); // (when ready of now)
  }
  ItemFloating.prototype = Object.create(Item.prototype);
  /** @class */
  function BackgroundEntity(name="default", x=0, y=0) {
   VisibleComponent.call(this);
     this.name = name;
     this.x = x;
     this.y = y;
     this.width = 1;
     this.height = 1;
     this.sprite = resources.sprites.backgroundEntities[this.name];
     this.loadDimensions();
  }
  BackgroundEntity.prototype = Object.create(VisibleComponent.prototype);
  /** @class */
  function MovingEntity(x=0, y=0) {
   VisibleComponent.call(this);
     this.x = x;
     this.y = y;
     this.width = 1;
     this.height = 1;
  }
  MovingEntity.prototype = Object.create(VisibleComponent.prototype);
  MovingEntity.prototype.collisionWith = function(ent) {
     return ((this.x>=ent.x&&this.x<=ent.x+ent.width) || (ent.x>=this.x&&ent.x<=this.x+this.width))
        &&  ((this.y>=ent.y&&this.y<=ent.y+ent.height) || (ent.y>=this.y&&ent.y<=this.y+this.height));
  };
  /** @class */
  function Player() {
   MovingEntity.call(this);
   this.inventory = {};
   console.log(this instanceof VisibleComponent);
   this.speed = 4;
   this.jumpSpeed = 8;
   this.vspeed = 0;
   this.sprite = resources.sprites.player;
     this.loadDimensions();
     this.direction = "right";
  }
  Player.prototype = Object.create(MovingEntity.prototype);
  Player.prototype.draw = function() {
     ctx.save();
     ctx.translate(this.x, this.y);
     if (this.direction == "left") ctx.scale(-1, 1);    // flip over y-axis
     ctx.translate(-this.sprite.width, 0);
     ctx.drawImage(this.sprite, 0, 0, this.width, this.height);
     ctx.restore();
  }
  Player.prototype.move = function() {
     if (keys['ArrowLeft']) {
        this.x -= this.speed;
        this.direction = "left";
        var leftEdge = this.x-canvas.width/2-this.width/2;
        if (leftEdge < world.exploredLeftBound) {
           world.generate(leftEdge, world.exploredLeftBound);
           world.exploredLeftBound = leftEdge;
        }
     }
     if (keys['ArrowRight']) {
        this.x += this.speed;
        this.direction = "right";
        var rightEdge = this.x+canvas.width/2+this.width/2;
        if (rightEdge > world.exploredRightBound) {
            world.generate(world.exploredRightBound, rightEdge);
            world.exploredRightBound = rightEdge;
        }
     }

     var level = getGroundHeightAt(this.x+this.width/2);

     if (this.y + this.height < level) {
        this.vspeed -= world.gravity;
     } else if (this.y + this.height > level) {
        this.y = level - this.height;
        this.vspeed = 0;
     }
     if (keys[' '] && this.y+this.height == getGroundHeightAt(this.x+this.width/2)) this.vspeed += this.jumpSpeed;

     this.y -= this.vspeed;
     for (var i=0; i<world.itemsFloating.length; i++) {
        var item = world.itemsFloating[i];
        if (this.collisionWith(item)) {
            if (this.inventory.hasOwnProperty(item.name)) this.inventory[item.name]++;
            else this.inventory[item.name] = 1;
            world.itemsFloating.splice(i, 1);
        }
     }
 };

I'm fairly new to javascript inheritance, so I don't understand what I'm doing wrong. Also, since the first script worked, I figured there's something I'm just overlooking in my second script. Any help would be appreciated.

EDIT In the beginning of the file, I declare player as a new Player(). resources contains Image instances that point to various image files. ctx and canvas are pretty self-explanatory globals.

Also, player isn't recognized as an instance of MovingEntity or VisibleComponent, even though Player's prototype is set to Object.create(MovingEntity.prototype), which has its prototype set to Object.create(VisibleComponent.prototype).

One other thing to mention is that in the definition of loadDimensions() in VisibleComponent, either the onload property of this.sprite is set to a function, or addEventListener() is called for 'load', depending on whether this.sprite has loaded (width != 0) or not.

8
  • 1
    "Doesn't work" is not a problem description. What is the error or issue? Commented Jun 20, 2017 at 18:10
  • Your code looks fine. What part works in the basic program but not in the complex one? Commented Jun 20, 2017 at 18:14
  • My bad @DarkFalcon I was in a rush. It's in the question now. And @Bergi, it doesn't give error this.loadDimensions is not a function Commented Jun 20, 2017 at 20:33
  • 1
    I don't get that when I set window.resources to something and call var p = new Player();. We need more information please. Commented Jun 20, 2017 at 20:40
  • 1
    Try new Player() instead of Player()… Otherwise, yes, please post the complete code that is necessary to reproduce the error (but strip away the parts that aren't) Commented Jun 20, 2017 at 20:53

1 Answer 1

1

In the beginning of the file, I declare player as a new Player().

That's the problem, you need to call the constructor after having set up your class. It currently doesn't throw an error about Player not being a function because the declaration is hoisted, but the prototype is not yet initialised with the value you expect so it indeed does not have a .loadDimensions() method yet.

Sign up to request clarification or add additional context in comments.

1 Comment

That makes sense. It works now. I forgot javascript was like this.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.