Jump to content

Variable problem in function


CharlesCraft50
 Share

Recommended Posts

I don't know why this error is happening: 

Quote

Uncaught TypeError: Cannot read property 'name' of undefined
    at StickmansDatas.js:222
    at Phaser.Physics.P2.impactHandler (phaser.js:89211)
    at World.emit (phaser.js:6407)
    at World.internalStep (phaser.js:13152)
    at World.step (phaser.js:12890)
    at Phaser.Physics.P2.update (phaser.js:89494)
    at Phaser.Physics.update (phaser.js:84565)
    at Phaser.Game.updateLogic (phaser.js:36342)
    at Phaser.Game.update (phaser.js:36280)
    at Phaser.RequestAnimationFrame.updateRAF (phaser.js:61979)
    at _onLoop (phaser.js:61962)

This code has a problem and I don't know why: this.enemy.body.collides(objectsCollisionGroup, function(){enemies[this.enemy.name].jump();});
My full code:

Enemy_1 = function (index, game, player) {

    var x = game.world.randomX;
    var y = game.world.randomY-50;

    this.game = game;
    this.health = Math.floor((Math.random() * 20) + 5);;
    this.player = player;
    this.alive = true;
    this.punchedCount = 0;

    this.enemy = game.add.sprite(x, y, 'stickman_2');

    this.enemy.name = index.toString();
    this.enemy.punching = false;
    game.physics.p2.enable(this.enemy);
    this.enemy.animations.add('left', Phaser.Animation.generateFrameNames('walk/left_', 1, 8), 10, true);
    this.enemy.animations.add('right', Phaser.Animation.generateFrameNames('walk/right_', 1, 8), 10, true);
    this.enemy.animations.add('sprint_left', Phaser.Animation.generateFrameNames('walk/left_', 1, 8), 20, true);
    this.enemy.animations.add('sprint_right', Phaser.Animation.generateFrameNames('walk/right_', 1, 8), 20, true);

    this.enemy.animations.add('punch_1_left', Phaser.Animation.generateFrameNames('punch_1/left_', 1, 3), 20, true);

    this.enemy.animations.add('punch_1_right', Phaser.Animation.generateFrameNames('punch_1/right_', 1, 3), 20, true);

    this.enemy.checkWorldBounds = true;
    this.enemy.body.collideWorldBounds = true;

    this.enemy.body.fixedRotation = true;
    this.enemy.body.damping = 0.5;
    this.enemy.body.setCollisionGroup(enemies_1ColisionGroup);
    this.enemy.body.collides(blockCollisionGroup);
    this.enemy.body.collides(objectsCollisionGroup, function(){enemies[this.enemy.name].jump();});
    this.enemy.healthBar = game.add.text(0, 0, 'HP: ' + this.health + '%', {font: "10px Arial", fill: "#ffffff"});
    this.enemy.addChild(this.enemy.healthBar);
    this.enemy.healthBar.position.y = this.enemy.healthBar.position.y-80;
};

Enemy_1.prototype.damage = function() {
    this.health -= 1;

    if (this.health <= 0)
    {
        this.alive = false;
        this.enemy.kill();
        return true;
    }

    return false;
};

Enemy_1.prototype.punch = function () {
  if(this.enemy.facing == 'left') {
      this.enemy.animations.play('punch_1_left');
      this.enemy.punching = true;
      if(this.player.facing == 'left') {
        this.player.body.x += 1;
      } else {
        this.player.body.x -= 1;
      }
  } else {
      this.enemy.animations.play('punch_1_right');
      this.enemy.punching = true;
  }
};

Enemy_1.prototype.jump = function () {
  if(checkIfCanJump(this.enemy)) {
    this.enemy.body.moveUp(300);
  }
};

Enemy_1.prototype.update = function() {
  this.enemyMoves = false;
  //<Enemy_Punch>
  if(checkOverlap(this.enemy, player)) {
    if(playerPunching == true){
      if(this.enemy.body.velocity.x < 0 && this.enemy.body.velocity.y < 150 && this.enemy.body.velocity.y > 0) {
        this.enemy.body.x += 30;
      } else if(this.enemy.body.velocity.x > 0 && this.enemy.body.velocity.y < 150 && this.enemy.body.velocity.y > 0) {
        this.enemy.body.x -= 30;
      }
      enemies[this.enemy.name].damage();
    } else {
      playerPunching = false;
    }
    game.time.events.add(Phaser.Timer.SECOND * 0.5, function(){enemies[this.enemy.name].punch();}, this);
  } else {
    this.enemy.punching = false;
    this.enemyMoves = true;
    if(this.enemyMoves != false) {
      accelerateToObject(this.enemy, this.player, 200);
    }
    if(this.enemy.body.velocity.x < 0 && this.enemy.body.velocity.y < 150 && this.enemy.body.velocity.y > 0) {
      this.enemy.animations.play('left');
      this.enemy.facing = 'left';
    } else if(this.enemy.body.velocity.x > 0 && this.enemy.body.velocity.y < 150 && this.enemy.body.velocity.y > 0) {
      this.enemy.animations.play('right');
      this.enemy.facing = 'right';
    }
  }
  //</Enemy_Punch>
  if(this.enemy.punching != false) {
  playerSubtractHealthBar(0.5);
  }
  this.enemy.healthBar.setText('HP: ' + this.health + '%');
};

Add the enemies in the game:

Quote

enemies = [];
    enemiesTotal = 4;

    for (var i = 0; i < enemiesTotal; i++)
    {
        enemies.push(new Enemy_1(i, game, player));
    }

Thanks in advance :D

Link to comment
Share on other sites

2 hours ago, CharlesCraft50 said:
Quote

this.enemy.body.collides(objectsCollisionGroup, function(){enemies[this].jump();}, this.enemy.name);

 

That is probably a really bad way to fix it.

You're fixing the context of the function to `this.enemy.name`, which isn't a great idea.

The issue is that the function gets invoked in a different context, likely `window`, so, `this` references the window, which is standard JS behaviour (in the browser, it defaults to root, which in the case of executing in the browser is usually `window`) for invoking non-explicitly scoped functions. Yes, JS scoping can be confusing.

A better way would be to bind the function to the context you want, .bind is your friend here (if you don't know how bind, call and apply work, learn, immediately, they are absolutely necessary for attempting class-based programming in JS):

this.enemy.body.collides(objectsCollisionGroup, function () {
  enemies[this.enemy.name].jump()
}.bind(this))

Alternatively it looks like you can pass in a context, I expect under the hood Phaser will invoke it using .bind to bind the context (again, this is fairly common for stuff like event emitters, which this is):

this.enemy.body.collides(objectsCollisionGroup, function () {
  enemies[this.enemy.name].jump()
}, this)

Not that the above simply references how you would bind a function in JS, and thus is general advice, in your case you don't have to go through the jump of performing a lookup, you're already in the scope of the `enemy` object you want so I think the following would work:

this.enemy.body.collides(objectsCollisionGroup, function () {
  this.enemy.jump()
}.bind(this))

This does throw up other concerns, like why your enemy class has a member enemy??? If you're going for a classical style of programming that isn't right, although it might look a little better just by renaming `enemy` to `sprite`, still, I guess thats up to you, there are few rules written in stone.

All this horror reminds me why I write in a functional style rather than classical OOP.

Link to comment
Share on other sites

 Share

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...