Search code examples
timeropacityphaser-framework

Phaser Making a fade to black reaction, having trouble with repeating events


I'm trying to add a fade to black event to my game. It triggers after a certain dialogue is triggered, and causes the screen to fade to black and then switch to another scene. I started by creating a big, black sprite that covers the entire screen, the set its alpha to 0. Here is the code that makes the fade work:

            this.line1.setText('Scally: Its been a long day Peef. Ready to hit the hay?');
            this.line2.setText('Press A for: Yeah I am ready to sleep. or Press D for: Naw, I am gonna stay up a bit more.');
            this.time.addEvent({
                delay: 2500, 
                callback: () => this.blackFade.setAlpha(0.2),
                callback: () => console.log("1"),
                callbackScope: this, 
            });
            this.time.addEvent({
                delay: 4500, 
                callback: () => this.blackFade.setAlpha(0.4),
                callback: () => console.log("2"),
                callbackScope: this, 
            });
            this.time.addEvent({
                delay: 6500, 
                callback: () => this.blackFade.setAlpha(0.6),
                callback: () => console.log("3"),
                callbackScope: this, 
            });
            this.time.addEvent({
                delay: 8500, 
                callback: () => this.blackFade.setAlpha(0.8),
                callback: () => console.log("4"),
                callbackScope: this, 
            });
            this.time.addEvent({
                delay: 10500, 
                callback: () => this.blackFade.setAlpha(1),
                callback: () => console.log("5"),
                callbackScope: this, 
            });
            this.time.addEvent({
                delay: 12500, 
                callback: () => this.scene.switch('bedRoomDay2'),
                callbackScope: this, 
            });

As I have this code in the update method, I'm having an issue where the timer events are triggered multiple times, causing the screen to only darken a little with occasional darker flashes before switching scenes, never becoming completely black. Is there a way to ensure that the timer events only trigger once, preferably without moving the code out of the update method?

If it helps, I'm using Phaser 3 in VSCode employing arcade physics.


Solution

  • Well you would need a helper variable to prevent multiple calls, and instead of the timer, I would recommend using one simply tween call, since the tween would update the alpha property in a smooth fashion.

    Both mentioned concepts are demonstrated in the following example.

    Small fade-out Demo:

    document.body.style = 'margin:0;';
    
    var config = {
        width: 536,
        height: 183,
        scene: {
            create, 
            update,
        }
    }; 
    
    function create () {
        this.label = this.add.text(10,10, 'Use Spacebar, to Fade WhiteBox')
            .setScale(1.5)
            .setOrigin(0)
            .setStyle({fontStyle: 'bold', fontFamily: 'Arial'});
    
        // Fade Status Varible Initialize
        this.isFading = false;
        this.spacebar = this.input.keyboard.addKey('space');
        
        // Game Object to fade
        this.add.rectangle(268, 91, 200, 50, 0xffffff);
        this.blackFade = this.add.rectangle(268, 91, 200, 50, 0);
        this.blackFade.setAlpha(0);
    }
    
    function update(){
        // Only start fading, if fading is not taking place
        if(!this.isFading && Phaser.Input.Keyboard.JustDown(this.spacebar)){
            if(this.blackFade.alpha == 0){
                this.isFading = true;
                this.tweens.add({
                    targets: this.blackFade,
                    alpha: 1,
                    duration: 6000,
                    onComplete: () => {
                      // reset variable
                      this.isFading = false;
                      this.label.setText('Use Spacebar, to show WhiteBox');
                    },
                    repeat: 0,
                });
            } else {
                // fade reset only for demo
                this.blackFade.alpha = 0;
                this.label.setText('Use Spacebar, to Fade WhiteBox');
            }
        }
    }
    
    new Phaser.Game(config);
    <script src="//cdn.jsdelivr.net/npm/phaser/dist/phaser.min.js"></script>

    Update:

    There are several way to let the black screen linger, but the easy one is simply to increase the duration and/or the alpha value of the tween. the alpha value will the

    Small fade-out & linger Demo:

    Setting alpha to 2 this means, that half of the tween duration about ~3000ms the screen will be black. if you need less us can adjust the alpha property.

    document.body.style = 'margin:0;';
    
    var config = {
        width: 536,
        height: 183,
        scene: {
            create, 
            update,
        }
    }; 
    
    function create () {
        this.label = this.add.text(10,10, 'Use Spacebar, to Fade WhiteBox')
            .setScale(1.5)
            .setOrigin(0)
            .setStyle({fontStyle: 'bold', fontFamily: 'Arial'});
    
        // Fade Status Varible Initialize
        this.isFading = false;
        this.spacebar = this.input.keyboard.addKey('space');
        
        // Game Object to fade
        this.add.rectangle(268, 91, 200, 50, 0xffffff);
        this.blackFade1 = this.add.rectangle(218, 91, 100, 50, 0);
        this.blackFade1.setAlpha(0);
        
        this.blackFade2 = this.add.rectangle(318, 91, 100, 50, 0);
        this.blackFade2.setAlpha(0);
    }
    
    function update(){
        // Only start fading, if fading is not taking place
        if(!this.isFading && Phaser.Input.Keyboard.JustDown(this.spacebar)){
            if(this.blackFade1.alpha == 0){
                this.isFading = true;
                this.tweens.add({
                    targets: this.blackFade1,
                    alpha: 2,
                    duration: 8000,
                    onComplete: () => {
                      // reset variable
                      this.isFading = false;
                      this.label.setText('Use Spacebar, to show WhiteBox');
                    },
                    repeat: 0,
                });
                
                 this.tweens.add({
                    targets: this.blackFade2,
                    alpha: 1,
                    duration: 8000,
                    onComplete: () => {
                      // reset variable
                      // this.isFading = false;
                      // this.label.setText('Use Spacebar, to show WhiteBox');
                    },
                    repeat: 0,
                });
            } else {
                // fade reset only for demo
                this.blackFade1.alpha = 0;
                this.blackFade2.alpha = 0;
                this.label.setText('Use Spacebar, to Fade WhiteBox');
            }
        }
    }
    
    new Phaser.Game(config);
    <script src="//cdn.jsdelivr.net/npm/phaser/dist/phaser.min.js"></script>

    Or: simply call a timer from the onComplete callback.