Timer function error

Hi, I’m new to phaser and trying to make a undertale-like text system.
I can’t understand why it gives error “Uncaught TypeError: Cannot read properties of undefined (reading ‘delayedCall’)”

            var config = {
                type:Phaser.AUTO,
                width:1600,
                height:900,
                scene:{
                    preload: preload,
                    create: create,
                    update: update
                }
            };

            var game = new Phaser.Game(config);
            var image;
            var speechText;
            var speechAudio;
            
            function preload ()
            {
                this.sound.unlock();
                speechAudio = this.load.audio('speak','assets/audios/speak_1.mp3')
            }

            function create ()
            {
                speechText = this.add.text(800, 450, 'text', { font: '"Arial"' });
            }

            function update ()
            {
                speakSet('lorem ipsum dolor sit amet');
            }

            function speakSet(speech)
            {
                for (let i = 0; i < speech.length; i++) {
                    var pitch = Phaser.Math.Between(200, -200);
                    speechAudio.detune = pitch;
                    speechAudio.play;
                    speechText+=speech[i];
                    var timer = this.time.delayedCall(speechAudio.duration, returner(), [], this)
                }
            }

            function returner()
            {
                return 0;
            }

:waving_hand:

In JS the calling context (this value) is dynamic.

Here speakSet() is called in the global context (this === globalThis === window). window.time is undefined. You want to call speakSet() in the context of the scene instead, which has time.delayedCall.

There are a few ways to fix this:

  • Rewrite your scene as a class and call the method as this.speakSet('…')
  • Add speakSet to extend in the scene config and call the method as this.speakSet('…')
  • Call speakSet as speakSet.call(this, '…')

Change to

var timer = this.time.delayedCall(speechAudio.duration, returner, [], this)

as you want to pass the function returner not invoke it.

Remove

this.sound.unlock();

as it won’t do anything.

Remove speechAudio = in preload() because load.audio() returns the loader. The audio hasn’t downloaded until create().