Another infinite loop, this time on scene.restart. Please help!

Please help this JS noob again!

Below is a snippet from my update function. When the player loses all lives the game is over. However the restart goes into an infinite loop, and the game over and the score text just stays there.

If I put all the restart into a separate gameOver function and place the gameOver(); into the if statement, all the stuff in the gameOver function goes undefined. Lives is a global variable btw.

Please help, I’m pulling my hair out!
Thank you so much!

Here’s the code:

// if the object falls to the floor the player loses a life
if (veggie.y > 550 && veggie.x > 150 && veggie.visible === true )    {
    //score = 0;
    veggie.setVisible(false);
    lives -= 1;
}

if (lives <= 0) {
    
//input is from an click event listener
this.input.off('gameobjectup');
this.add.text(400, 300, "Game Over\n\nYour score: " + score + "\n\nWait for the restart",  { fontFamily: 'Helsinki', fontSize: 30, color: '#ff0000'});
 
  // restart game
  this.time.delayedCall(500, function() {
    this.scene.restart();
  }, [], this);

}

The update function runs through all the operations inside it constantly, so if you don’t want the game to constantly restart, remove that from the update function and put it somewhere else.

I suggest making an endgame() function just outside of the update function like this:

update(){
   //whatever your constantly updated things here
   //some sort of condition that triggers endgame()
   if ( this.lives <= 1){
     this.endgame();			  
   }else{
     this.lives--;
   }
}
endgame(){
  this.time.delayedCall(500, function() {
  this.scene.restart();
  }, [], this);
}
1 Like

Thank you for taking the time to reply! When I put it into a separate function, unfortunately it says this.endgame is not a function.:tired_face:

Also, when I change lives to this.lives everything stops working at all. So I have to put lives into a global variable at the top of the code.:dizzy_face:

I’m having a problem with understanding this, I used to study PHP before lol, and I’m running into some difficulties with JS. :blush:

that might be because you’re binding this in the wrong place

you need to define this.lives somewhere

I suggest you take a look at a few more tutorials to get a better understanding of how phaser deals with function calls and scenes.

check out this tutorial series:

1 Like

Thank you! Unfortunately, nothing helps!
Wherever I place this.lives and no matter how I define them I get undefined, and it keeps saying this.endgame is not a function.

I made my code by taking examples from the labs.phaser.io and adapting them for my purpose.
So it must be quite a mess lol.

I’m also having a problem with creating an object pool and spawning game objects.

Some parts of the code work, others don’t. Basically most of it works, except for the object pooling and game over stuff, somehow functions addveggie and endgame bring up undefined variables, even though these variables are set to global. When I change them to this, the screen goes black altogether, I’m unable to define this on veggies and lives.

I’m trying a hands on approach to learning all this stuff but now I’m completely and utterly stuck!

I’m pulling my hair out, I just can’t understand Javascript I guess. :weary::sob::sob:

Here is the complete code:

 let config = {
        type: Phaser.AUTO,
        parent: 'phaser-example',
        width: 800,
        height: 600,
        physics: {
            default: 'arcade',
            arcade: {
                gravity: { y: 100 },
                debug: false
            }
        }, 
        scene: {
            preload: preload,
            create: create,
            update: update
        }
    };

    let info;
    let score = 0;
    let lives = 1;
    let zone;
    let veggie;
    let veggies;
    let scorelabel;
    let scoretween;
    //target for movetoObject on the character's basket to make veggies fall there after the player clicks on them
    let target = new Phaser.Math.Vector2();
    // random veggie type
    let veggieType = Math.floor(Math.random() * 8); 

    let highScore;


    // this is an object catching game, help a cute character catch falling veggies. when a player clicks on a falling veggie it falls into the character's basket and the score adds up, if you miss a veggie and it falls to the ground you lose a life.
    let game = new Phaser.Game(config);


    function preload() {

    // loading assets

        this.load.image('background', 'assets/BG.png');
        this.load.image('plusone', 'assets/plus-one.png')

     this.load.spritesheet('char-idle', 
            'assets/IdleSprite.png',
            { frameWidth: 1080, frameHeight: 1080, endFrame: 3 }
        );
        this.load.spritesheet('char-catch', 
            'assets/CatchSprite.png',
            { frameWidth: 1080, frameHeight: 1080, endFrame: 3 }
        );
        this.load.spritesheet('veg', 
            'assets/veggies.png',
            { frameWidth: 512, frameHeight: 512, endFrame: 8 }
        ); 
        
        }

    function create() {
        
        

        
        
    // adding the overlap zone on the basket
    zone = this.add.zone(150, 459).setSize(20, 20);
        this.physics.world.enable(zone);
        zone.body.setAllowGravity(false);
        zone.body.moves = false; 
        

    // adding background
    let bg = this.add.sprite(400, 300, 'background'); 

    //adding the idle character animation
    let animIdle = {
            key: 'idle',
            frames: this.anims.generateFrameNumbers('char-idle', { frames: [ 0, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 2, 0, 2, 0, 0 ] }),
            frameRate: 4,
            repeat: -1
        };

        this.anims.create(animIdle);


    // adding the catch character animation

    let animCatch = {
            key: 'catch',
            frames: this.anims.generateFrameNumbers('char-catch', { frames: [ 0, 0, 1, 1, 0, 0, 2, 2, 2, 2, 0, 0, 1, 1, 0, 0 ] }),
            frameRate: 16,
            repeat: 0
        };

        this.anims.create(animCatch);
        
        
        // adding the idle sprite
        this.player = this.add.sprite(100, 500,         'char-idle').play('idle');
        
        this.player.setScale(.25);
      
        // adding the catch sprite
        this.catchPlayer = this.add.sprite(100, 500, 'char-catch').play('catch');
        
    this.catchPlayer.setScale(.25);
    this.catchPlayer.setVisible(false);
        


        // area of the "ceiling" where the veggies will fall from
    let x = Phaser.Math.Between(250, 800);
    let y = 0;

            
            // veggies group
    veggies = this.physics.add.group({
        key: 'veg',
        frame: [veggieType],
        setXY: { x: x, y: y },
        maxSize: 32,
            createCallback: function (veggie) {
                veggie.setName('veggie' + this.getLength());
                console.log('Created', veggie.name);
            },
            removeCallback: function (veggie) {
                console.log('Removed', veggie.name);
            }
        
    });


    // random veggie is created, this is supposed to go into the addveggie function, but doing so breaks my game

    veggie = Phaser.Utils.Array.GetRandom(veggies.getChildren());


    //input enabled, scaled and rotating while in air
    veggie.setInteractive();
    veggie.setScale(0.25);
    veggie.setAngularAcceleration(60);
            


    // overlap zone on the basket
    this.physics.add.overlap(zone, veggie);

    // taken from the phaser example on interactive object  listener events:

    //  The images will dispatch a 'clicked' event when they are clicked on
           veggie.on('clicked', clickHandler, this);
            





        //  If a Game Object is clicked on, this event is fired.
        //  We can use it to emit the 'clicked' event on the game object itself.
       this.input.on('gameobjectup', function (pointer, gameObject)
        {
            
            gameObject.emit('clicked', gameObject);
        }, this); 
        

        //  Display the game stats
        info = this.add.text(10, 10, '', { font: '48px Helsinki', fill: '#000000' });
        
        
     
    //this is the problem with object pooling in my code, i commented it out for this example
    /*this.time.addEvent({
            delay: 300,
            loop: true,
            callback: addveggie
        });
    */
            
    }

    function update ()
    {


        veggies.children.iterate(function (veggie) {
            if (veggie.y > 605) {
                veggies.killAndHide(veggie);
            }
        }); 
     


    // score
        info.setText('Score: ' + score + '\nLives: ' + lives);


        
    // overlap zone on the basket, the overlap means the veggie is caught by the character in the basket, so the overlap changes the animation back to idle    
    let touching = zone.body.touching;

      if (!touching.none) {
         
    this.catchPlayer.setVisible(false);  
    this.player.setVisible(true); 
    veggie.setVisible(false);

      }
     
     

    // if the object falls to the floor, to the right of the character's position the player loses a life, I made only one life to test it
    if (veggie.y > 550 && veggie.x > 150 && veggie.visible === true )    {
        //score = 0;
        veggie.setVisible(false);
        lives --;
    }

    if (lives <= 0) {
    this.endgame();
     
    }

        
    }


    //this is what happens after the player clicks on the fallen veggie, "helping" the character to catch the veggie.

    function clickHandler (veggie)
    {
        
        //the score grows by one point
        score++;


    //floating score 
    scorelabel = this.add.image(veggie.x, veggie.y-30, 'plusone').setScale(0.1);

    // text can be used instead of an image
    /*scorelabel = this.add.text(veggie.x, veggie.y-20, '+1', { fontFamily: 'Arial', fontSize: 30, color: '#ff0000'}); */


    scoretween = this.tweens.add({
            targets: scorelabel,
            alpha: 0,
            y: -30,
            delay: 300,
            duration: 600
        });
        


        veggie.off('clicked', clickHandler);
        
        //the target is placed on the character's basket, where the veggie must fly to after being clicked by the player
        target.x = 150;
        target.y = 450;
            
            
            //the veggie falls into the basket
            // Moves at 1000 px/s:
            this.physics.moveToObject(veggie, target, 1000);


    // the idle animation is set to invisible
    this.player.setVisible(false);   

    //the catch animation plays
    this.catchPlayer.setVisible(true);
    this.catchPlayer.play('catch');

    veggie.input.enabled = false;

       

       
    }

    //this stuff is from the object pooling example
    function activateveggie (veggie) {
        veggie
        .setActive(true)
        .setVisible(true)
        
        
    }

    //game over stuff
    function endgame() {
     
    //this.input.off('gameobjectup');
    this.add.text(400, 300, "Game Over\n\nYour score: " + score + "\nBest score: " + localStorage.score + "\n\nTap to restart",  { fontFamily: 'Helsinki', fontSize: 30, color: '#ff0000'});
     
      // restart game
      this.time.delayedCall(500, function() {
        this.scene.restart();
      }, [], this);
    };




    //this is another problem area, everything is undefined when I use this function
    /*
    function addveggie () {

    //veggie = Phaser.Utils.Array.GetRandom(veggies.getChildren());


        if (!veggie) return; // None free

        activateveggie(veggie); 
    }
    */

While it’s great to get off and running with Phaser tutorials and samples, it’s very important to understand some of the fundamental aspects of Javascript, especially in regards to variable type, scope, function context, etc. I’d suggest pairing some of those type of learning resources with the example you’re working on.

In regards to your endgame function, you need to a) look where it it is defined, and what scope it belongs to, and b) look where you are calling it from. What does ‘this’ refer to when you are calling this.endgame? What should ‘this’ refer to for the content within the endgame function?

2 Likes

Thank you! The more I’m trying to learn Javascript the less I understand. JS looks pretty illogical and “this” is extremely confusing. I’ve read about it in every popular source and I still don’t get it. Looks like it refers to a whole bunch of different things, and I can’t find a way to define it properly.

With PHP I wrote my own Wordpress theme in just a few weeks after I had begun learning it almost from scratch some years ago, and I successfully used that theme for my blog, I’ve also written some PHP code snippets I use and reuse for my websites, and sometimes I easily rewrite some parts of Wordpress plugins and other themes for my needs. PHP is pretty logical, and has excellent documentation, I could understand it almost instantly. I can fix some JS for a website, if something goes wrong, and do some simple JQuery stuff but I got stuck with Phaser. Maybe JS is just hopeless for me. :sob:

If I’m calling endgame from my if statement then I get the undefined error on the this.add.text and other stuff inside of the endgame function. So any way I try to use it leads me to a dead end.

And when I’m implementing the example of object pooling from labs my click listener functions stop working and the score disappears.

I edited your code to have the preload() create() and update() functions (along with others used in your code) to be inside a scene. I also assigned a few this bindings of variables to the scene. Hopefully it works, though I can’t test it without the assets. Try it out and let me know what errors you get.

I saw a few unusual implementations in your code, but I don’t know if they will make or break the code. I still thoroughly recommend you go through that tutorial series I posted earlier, since it walks you through the process quite clearly, and the reset function for the spaceships is somewhat similar to what you’re trying to do with the falling objects (it greatly boosts performance, since you’ll be reusing objects instead of creating new one each time)

Here’s the code:

var config = {
        type: Phaser.AUTO,
        parent: 'phaser-example',
        width: 800,
        height: 600,
        physics: {
            default: 'arcade',
            arcade: {
                gravity: { y: 100 },
                debug: false
            }
        }, 
        scene: {
            preload: preload,
            create: create,
            update: update
        }
    };

/*this is an object catching game, help a cute character catch falling veggies. 
//when a player clicks on a falling veggie it falls into the character's basket and the score adds up.
If you miss a veggie and it falls to the ground you lose a life*/
var game = new Phaser.Game(config);

//here I have added your level as a scene
class CatchingGame extends Phaser.Scene {
	constructor() {
		super("catchingGame");
    }
    preload() {
        // loading assets
        this.load.image('background', 'assets/BG.png');
        this.load.image('plusone', 'assets/plus-one.png')
        this.load.spritesheet('char-idle', 
            'assets/IdleSprite.png',
            {frameWidth: 1080, frameHeight: 1080, endFrame: 3}
        );
        this.load.spritesheet('char-catch', 
            'assets/CatchSprite.png',
            { frameWidth: 1080, frameHeight: 1080, endFrame: 3 }
        );
        this.load.spritesheet('veg', 
            'assets/veggies.png',
            { frameWidth: 512, frameHeight: 512, endFrame: 8 }
        );
        /*variables - here 'this' means that 
        the variables will only be accessible in this scene*/
        this.info;
        this.score = 0;
        this.lives = 1;
        this.zone;
        this.veggie;
        this.veggies;
        this.scorelabel;
        this.scoretween;
        //target for movetoObject on the character's basket to make veggies fall there after the player clicks on them
        this.target = new Phaser.Math.Vector2();
        // random veggie type
        this.veggieType = Math.floor(Math.random() * 8); 
        this.highScore;   
    }
    create() {
        // adding the overlap zone on the basket
        this.zone = this.add.zone(150, 459).setSize(20, 20);
        this.physics.world.enable(this.zone);
        this.zone.body.setAllowGravity(false);
        this.zone.body.moves = false; 

    // adding background
    this.bg = this.add.sprite(400, 300, 'background'); 

    //animations
    //adding the idle character animation
    this.anims.create({
        key: 'idle',
        frames: this.anims.generateFrameNumbers('char-idle', { frames: [ 0, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 2, 0, 2, 0, 0 ] }),
        frameRate: 4,
        repeat: -1
    });
    // adding the catch character animation
    this.anims.create({
        key: 'catch',
        frames: this.anims.generateFrameNumbers('char-catch', { frames: [ 0, 0, 1, 1, 0, 0, 2, 2, 2, 2, 0, 0, 1, 1, 0, 0 ] }),
        frameRate: 16,
        repeat: 0
    });
    //sprites

    // adding the idle sprite
    this.player = this.add.sprite(100, 500,'char-idle').play('idle');
    /*you set scale here, 
    though you might need to change the hitbox size using
    .setSize() and .setOffset()*/
    this.player.setScale(.25);

    // adding the catch sprite
    this.catchPlayer = this.add.sprite(100, 500, 'char-catch').play('catch');
    this.catchPlayer.setScale(.25);
    this.catchPlayer.setVisible(false);

    // area of the "ceiling" where the veggies will fall from
    this.ceilingX = Phaser.Math.Between(250, 800);
    this.ceilingY = 0;

    // veggies group
    this.veggies = this.physics.add.group({
        key: 'veg',
        frame: [veggieType],
        setXY: { x: ceilingX, y: ceilingY },
        maxSize: 32,
            createCallback: function (veggie) {
                veggie.setName('veggie' + this.getLength());
                console.log('Created', veggie.name);
            },
            removeCallback: function (veggie) {
                console.log('Removed', veggie.name);
            }
        
    });

    // random veggie is created, this is supposed to go into the addveggie function, but doing so breaks my game
    this.veggie = Phaser.Utils.Array.GetRandom(this.veggies.getChildren());
    //input enabled, scaled and rotating while in air
    this.veggie.setInteractive();
    this.veggie.setScale(0.25);
    this.veggie.setAngularAcceleration(60);

    // overlap zone on the basket
    this.physics.add.overlap(this.zone, this.veggie);

    // taken from the phaser example on interactive object  listener events:

    //  The images will dispatch a 'clicked' event when they are clicked on
    this.veggie.on('clicked', this.clickHandler, this);
            
    //  If a Game Object is clicked on, this event is fired.
    //  We can use it to emit the 'clicked' event on the game object itself.
    this.input.on('gameobjectup', function (pointer, gameObject){
        gameObject.emit('clicked', gameObject);
    }, this); 
    

    //  Display the game stats
    this.info = this.add.text(10, 10, 'Score: ' + this.score + '\nLives: ' + this.lives, { font: '48px Helsinki', fill: '#000000' });
        
    //this is the problem with object pooling in my code, i commented it out for this example
    /*this.time.addEvent({
            delay: 300,
            loop: true,
            callback: addveggie
        });
    */
}
update (){
    this.veggies.children.iterate(function (veggie) {
        if (veggie.y > 605) {
            this.veggies.killAndHide(veggie);
        }
    }); 
// score
    this.info.setText('Score: ' + this.score + '\nLives: ' + this.lives);


    
    // overlap zone on the basket, the overlap means the veggie is caught by the character in the basket, so the overlap changes the animation back to idle    
    let touching = zone.body.touching;

    if (!touching.none) {
        this.catchPlayer.setVisible(false);  
        this.player.setVisible(true); 
        this.veggie.setVisible(false);
    }
    // if the object falls to the floor, to the right of the character's position the player loses a life, I made only one life to test it
    if (this.veggie.y > 550 && this.veggie.x > 150 && this.veggie.visible === true )    {
        //score = 0;
        this.veggie.setVisible(false);
        this.lives --;
    }
    if (this.lives <= 0) {
        this.endgame();
    }
}
clickHandler (veggie)
{
    //the score grows by one point
    this.score++;

    //floating score 
    this.scorelabel = this.add.image(veggie.x, veggie.y-30, 'plusone').setScale(0.1);

    // text can be used instead of an image
    /*scorelabel = this.add.text(veggie.x, veggie.y-20, '+1', { fontFamily: 'Arial', fontSize: 30, color: '#ff0000'}); */


    this.scoretween = this.tweens.add({
            targets: scorelabel,
            alpha: 0,
            y: -30,
            delay: 300,
            duration: 600
        });
        


        veggie.off('clicked', this.clickHandler);
        
        //the target is placed on the character's basket, where the veggie must fly to after being clicked by the player
        this.target.x = 150;
        this.target.y = 450;
            
            
            //the veggie falls into the basket
            // Moves at 1000 px/s:
            this.physics.moveToObject(veggie, this.target, 1000);


    // the idle animation is set to invisible
    this.player.setVisible(false);   

    //the catch animation plays
    this.catchPlayer.setVisible(true);
    this.catchPlayer.play('catch');

    veggie.input.enabled = false;

   

   
}
//this stuff is from the object pooling example
activateveggie (veggie) {
    veggie
    .setActive(true)
    .setVisible(true)
    
    
}

//game over stuff
endgame() {
 
//this.input.off('gameobjectup');
this.add.text(400, 300, "Game Over\n\nYour score: " + score + "\nBest score: " + localStorage.score + "\n\nTap to restart",  { fontFamily: 'Helsinki', fontSize: 30, color: '#ff0000'});
 
  // restart game
  this.time.delayedCall(500, function() {
    this.scene.restart();
  }, [], this);
};




//this is another problem area, everything is undefined when I use this function
/*
addveggie () {

//let veggie = Phaser.Utils.Array.GetRandom(this.veggies.getChildren());


    if (!veggie) return; // None free

    activateveggie(veggie); 
}
*/

}

let me know if you get any errors and what they are.

P.S. Next time, try not to blame the language for being “illogical”, because if that were the case, the language would be dead years ago. Have patience when it comes to learning something new. It’s worth it. :slight_smile: