0
\$\begingroup\$

I am working on my first 2D game with Phaser 3. I have set up VS Code with a node.js https server and run it to deploy the game on localhost.

While the game gets compiled successfully and deployed, I get a runtime error when executing the collider and overlap function:

Uncaught TypeError: Cannot read property 'isParent' of undefined
     at World.collideObjects (phaser.js?d4ef:102761)
     ...

For the time being I have a title screen with "welcome to my game" text and it is clickable. Once you click that you are navigating to the play scene. When navigating to the play scene, I am getting that error displayed as you can see here:

Error

I suspect that this is caused by my use of the this keyword and the scope of it. But I have no idea how to fix that. I spent 2-3 hours debugging and playing with 'this.' here and there but I am still clueless as to how I can fix this and what I did wrong.

The comment //Error on "is parent undefined" is added to mark the lines where I get the error.

import Phaser from 'phaser';

class PlayScene extends Phaser.Scene {

constructor() {
    super('PlayScene');
}

create() {
    var player;
    var stars;
    var bombs;
    var bomb;
    var platforms;
    var cursors;
    var score = 0;
    var gameOver = false;
    var gameOverText;
    var scoreText;
    var leftKey;
    var rightKey;
    var upKey;
    var repeatStarCount;
    this.add.image(400, 300, 'sky');

    platforms = this.physics.add.staticGroup();
    platforms.create(390, 568, 'ground1');
  
    platforms.create(750, 200, 'ground2');
    platforms.create(0, 150, 'ground4');
    platforms.create(690, 420, 'ground4');
    platforms.create(100, 320, 'ground4');

    player = this.physics.add.sprite(100, 450, 'dude');
    player.setBounce(0.2);
    player.setCollideWorldBounds(true);
    player.setScale(0.15);
    player.setSize(300, 400, false);
    player.body.offset.y = 0;
    player.body.offset.x = 0;

    //  Input Events
    this.cursors = this.input.keyboard.createCursorKeys();
    this.leftKey = this.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.LEFT);
    this.rightKey = this.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.RIGHT);
    this.upKey = this.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.UP);

    //  player animations
    this.anims.create({
        key: 'LeftRun',
        frames: this.anims.generateFrameNumbers('dude', {
            start: 14,
            end: 17
        }),
        frameRate: 10,
        repeat: -1
    });

    this.anims.create({
        key: 'LeftJump',
        frames: [{
            key: 'dude',
            frame: 13
        }],
        frameRate: 10,
        repeat: -1
    });

    this.anims.create({
        key: 'LeftIdle',
        frames: this.anims.generateFrameNumbers('dude', {
            start: 9,
            end: 10
        }),
        frameRate: 10,
        repeat: -1
    });
    this.anims.create({
        key: 'LeftDizzy',
        frames: this.anims.generateFrameNumbers('dude', {
            start: 10,
            end: 11
        }),
        frameRate: 10,
        repeat: -1
    });

    this.anims.create({
        key: 'RightRun',
        frames: this.anims.generateFrameNumbers('dude', {
            start: 5,
            end: 8
        }),
        frameRate: 10,
        repeat: -1
    });

    this.anims.create({
        key: 'RightJump',
        frames: [{
            key: 'dude',
            frame: 4
        }],
        frameRate: 10,
        repeat: -1
    });

    this.anims.create({
        key: 'RightIdle',
        frames: this.anims.generateFrameNumbers('dude', {
            start: 0,
            end: 1
        }),
        frameRate: 10,
        repeat: -1
    });

    this.anims.create({
        key: 'RighttDizzy',
        frames: this.anims.generateFrameNumbers('dude', {
            start: 2,
            end: 3
        }),
        frameRate: 10,
        repeat: -1
    });

    //  Some stars to collect, 12 in total, evenly spaced 70 pixels apart along the x axis
    repeatStarCount = 10;
    this.stars = this.physics.add.group({
        key: 'star',
        repeat: repeatStarCount,
        setXY: {
            x: 8,
            y: 0,
            stepX: 50
        },
        setScale: {
            x: 0.05,
            y: 0.05
        }
    });

    this.stars.children.iterate(function(child) {

        child.setBounceY(Phaser.Math.FloatBetween(0.4, 0.8));

    });

    this.bombs = this.physics.add.group();

    var style = {
        font: "30px Arial",
        fill: "#ffff00",
        stroke: "#535353",
        align: "center",
        strokeThickness: 15,
    };

    var style1 = {
        font: "40px Arial",
        fill: "#ffff00",
        stroke: "#535353",
        align: "center",
        strokeThickness: 15,
    };

    scoreText = this.add.text(0, 0, "Score: 0", style);
    gameOverText = this.add.text(300, 250, "Game Over", style1);
    gameOverText.visible = false;

    //  Collide the player and the stars with the platforms
    this.physics.add.collider(player, platforms);
    this.physics.add.collider(stars, platforms);
    this.physics.add.collider(bombs, platforms);
  
    //Error on "is parent undefined"
    this.physics.add.overlap(player, stars, () => {

        star.disableBody(true, true);

        //  Add and update the score
        score += 10;
        scoreText.setText('Score: ' + score);

        if (stars.countActive(true) === 0) {
            repeatStarCount = +3;
            //  A new batch of stars to collect
            stars.children.iterate(function(child) {

                child.enableBody(true, child.x, 0, true, true);

            });

            var x = (player.x < 400) ? Phaser.Math.Between(400, 800) : Phaser.Math.Between(0, 400);

            var bomb = bombs.create(x, 16, 'bomb');
            bomb.setBounce(1);
            bomb.setCollideWorldBounds(true);
            bomb.setVelocity(Phaser.Math.Between(-200, 200), 20);
            bomb.allowGravity = false;

        }

    }, null, this);

   //Error on "is parent undefined"
    this.physics.add.collider(player, bombs, () => {
        this.physics.pause();
        if (player.anims.currentAnim.key === 'RightRun' || player.anims.currentAnim.key === 'RightIdle' || player.anims.currentAnim.key === 'RightJump') {
            player.anims.play('RighttDizzy');
        }

        if (player.anims.currentAnim.key === 'LeftRun' || player.anims.currentAnim.key === 'LeftIdle' || player.anims.currentAnim.key === 'LeftJump') {
            player.anims.play('LeftDizzy');
        }
        gameOver = true;
        let timer = this.time.delayedCall(500, () => {
            player.anims.stop();
            gameOverText.visible = true;
        }, [], this);
    }, null, this);
}

update() {
    if (this.gameOver) {
        return;
    }

    if (this.cursors.left.isDown) {

        player.setVelocityX(-160);
        player.anims.play('LeftRun', true);

    } else if (this.cursors.right.isDown) {

        player.setVelocityX(160);
        player.anims.play('RightRun', true);

    } else if (Phaser.Input.Keyboard.JustUp(this.leftKey)) {

        player.setVelocityX(0);
        player.anims.play('LeftIdle', true);

    } else if (Phaser.Input.Keyboard.JustUp(this.rightKey)) {

        player.setVelocityX(0);
        player.anims.play('RightIdle', true);

    } else if (Phaser.Input.Keyboard.JustUp(this.upKey) && player.anims.currentAnim != null) {

        if (player.anims.currentAnim.key === 'LeftJump') {
            player.anims.play('LeftIdle', true);
        }

        if (player.anims.currentAnim.key === 'RightJump') {
            player.anims.play('RightIdle', true);

        }

    } else if (this.cursors.up.isDown && player.anims.currentAnim != null) {

        if (player.anims.currentAnim.key === 'RightIdle') {
            player.anims.play('RightJump', true);
            if (player.body.touching.down) {
                player.setVelocityY(-350);
            }
        }

        if (player.anims.currentAnim.key === 'LeftIdle') {
            player.anims.play('LeftJump', true);
            if (player.body.touching.down) {
                player.setVelocityY(-350);
            }
        }
    }
}}export default PlayScene;
\$\endgroup\$

2 Answers 2

1
\$\begingroup\$

I solved this problem. The issue is actually related to variable declaration. So I moved all the variable declarations in to the constructor and that fixed the problem. I don't know whether that is a common practice or not anyway.

\$\endgroup\$
0
\$\begingroup\$

You mentioned you were able to get it working, but here's more context.

this contains all the properties of the class that you're accessing this from. As you can see, all your code is within the definition of the PlayScene class that extends Phaser.Scene. Class properties are declared in the class, outside of any function, e.g.:

class PlayScene extends Phaser.Scene {
    classProp = 5;

    constructor() {
        super('PlayScene');
    }
    //... more code
}

In the above case, classProp is accessed via this: this.classProp.

In your code, you attempt to access some variables such as stars using this, when you decarded them as local variables to the create function. That means stars is only available to create() and is accessed directly, without using this.

Now here's the tricky part. You can still set this.stars even if you never declared it. this.stars = 5; is valid, and the next time you access this.stars, you will get 5 instead of undefined. But, if you declare var stars; and then set this.stars = 5, these are two different variables! So when you access stars without this, you will get undefined. An example:

class Scene {
  constructor() {}

  create() {
    let stars;
    
    this.stars = 5;
    
    console.log(`stars: ${stars}`);
    console.log(`this.stars: ${this.stars}`);
  }

}

const scene = new Scene()
scene.create();

So, on these lines:

//Error on "is parent undefined"
this.physics.add.overlap(player, stars, () => {

You accessed stars, the local variable you declared. But when you actually created stars, you did so like this:

this.stars = this.physics.add.group({
// ...

stars is undefined, while this.stars points to the object you created.

I hope all of that helps!

For further information, I use TypeScript instead of JavaScript to avoid making mistakes like this, as TypeScript uses a typing system that wouldn't allow me to assign to a variable I haven't explicitly declared yet.

\$\endgroup\$

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .