Jump to content

Timer Countdown then Change State


Recommended Posts

So typically you have a function that switch to another state that call be call via a button or sprites

this.btn_gameOver = this.game.add.button(this.game.width/2, 500, 'btn_play', this.start_gameOver, this);this.btn_gameOver.anchor.setTo(0.5,0.5);},start_gameOver: function(){        this.game.state.start('gameover', true, false, GameScore);    } 

However when I use it within another function (updateTimer), once the countdown reached 0 and triggered, it return an error Uncaught TypeError: Cannot read property 'game' of undefined

 updateTimer: function(){        GameTimer--;        txt_timer.text = GameTimer;        if(GameTimer != 0){            console.log('it works!');        }else{            console.log('dingding');            //start_gameOver();            this.game.state.start('gameover', true, false, GameScore);        }    },

What am I missing?

Link to post
Share on other sites

Hi, 

I think that is the "this" scope.  This referes to function scope while you should use the global scope. 

Try passing the game as a parameter to you function to confirm this issue

 updateTimer: function(game){        GameTimer--;        txt_timer.text = GameTimer;        if(GameTimer != 0){            console.log('it works!');        }else{            console.log('dingding');            //start_gameOver();            game.state.start('gameover', true, false, GameScore);        }    },
Link to post
Share on other sites

oh, i did and tried it again, got the state's error this time

 

"Uncaught TypeError: Cannot read property 'state' of undefined"

 

Im using Yeoman's phaser-generator, this is how the state written in the beginning, should there be any missing parameter there

'use strict';function Play() {}  Play.prototype = {    create: function(){......
Link to post
Share on other sites
 updateTimer: function(game){        GameTimer--;        txt_timer.text = GameTimer;        if(GameTimer != 0){            console.log('it works!');        }else{            console.log('dingding');            //start_gameOver();            game.state.start('gameover', true, false, GameScore);        }    },

this one, follow your instruction and it gave me ""Uncaught TypeError: Cannot read property 'state' of undefined"

 

However it seem to be working by using a boolean as a trigger, but for education purposes, I would really want to know how this work  :P

Link to post
Share on other sites

this one, follow your instruction and it gave me ""Uncaught TypeError: Cannot read property 'state' of undefined"

 

However it seem to be working by using a boolean as a trigger, but for education purposes, I would really want to know how this work  :P

 

So the error is telling me that the variable GAME is not defined.  So of course you cannot access "state"

 

So for whatever reason, your function is calling without game being defined.

 

Can you tell me why game is not defined right now?  Can you explain where it is defined and if it is being defined in a function?  is game scoped to the window?  or is game scoped to a function you have?  remember that javascript is functionally scoped.

 

If you do NOT dlecare the game variable with the var keyword, then the js engine will create a var game as undefined at the top of your function that declares it.

 

JS is functionally scoped as well, so if you declare game with var keyword inside a function then it will not be accessible unless in that function.  A hack could be "window.game =" but better would be to properly declare th game variable.

 

NOW if it is being declared properly, then perhaps your function that depends on it is running prematurely?  If that's the case, you can either trigger an event to fire that function after game is declared, or you can require the game or load it first.. idk.. I'd have to see more of your setup to be honest.

 

I hope this helps

Link to post
Share on other sites
'use strict';var GameTimer = 3,GameTimerEvent;function Play() {}  Play.prototype = {    create: function() {      var style = { font: '42px Arial', fill: '#ffffff', align: 'right'};      GameTimerEvent = this.game.time.events.loop(Phaser.Timer.SECOND, this.updateTimer);      this.btn_replay = this.game.add.button(10, 8, 'btn_replay', this.start_gameOver, this);      this.btn_replay.anchor.setTo(0,0);    },    updateTimer: function(game){        GameTimer--;        if(GameTimer != 0){            console.log('it works!');        }else if (GameTimer == 0){            console.log('dingding, game over!');            this.start_gameOver();        }    },    start_gameOver: function(){        this.game.state.start('game_over');    },};module.exports = Play;

Sorry, here the whole game.js to avoid confusion

Link to post
Share on other sites

try function Play(game) {}

 

that should let you use this.game

 

if you need to access the game variable outside of anywhere else.

 

hack would be window.game

 

if you want to pass it as a dependency

 

exports.game = game

exports.play = play

 

some other file:

var game = require(game);

var play = require(play);

Link to post
Share on other sites

The key is this line in "create":

GameTimerEvent = this.game.time.events.loop(Phaser.Timer.SECOND, this.updateTimer);

You need to pass a third parameter, "this". That is the callback context. It's what sets the value of "this" within your "updateTimer" function. You really want "this" in "updateTimer" to be the "this" in your "create" method. That way you'll have access to "this.game" (among other things).

 

JavaScript can be annoying about "this". Everyone runs into this problem at some point.

Link to post
Share on other sites

The key is this line in "create":

GameTimerEvent = this.game.time.events.loop(Phaser.Timer.SECOND, this.updateTimer);

You need to pass a third parameter, "this". That is the callback context. It's what sets the value of "this" within your "updateTimer" function. You really want "this" in "updateTimer" to be the "this" in your "create" method. That way you'll have access to "this.game" (among other things).

 

JavaScript can be annoying about "this". Everyone runs into this problem at some point.

omg, this.works!! I knew I missing something but this.? this.just brilliant

 

thanks again for the help everyone /o/

Link to post
Share on other sites

if you are trying to access this.game you would need to do it that way. 

 

Javascript can be annoying about this, because anytime you create a new function the keyword THIS === that function.

 

But anyway, phaser I guess uses a system where you pass "this" through other functions.

 

I would rather just scope what I need to access properly instead of passing "this" around - I think that's too hacky

 

It would be very beneficial for you to learn about how javascript scopes things, it has helped me tremendously and it has allowed me to save a lot of headache.  IF you read the following article, you should have a full understanding of "this" 

 

Here:

 

https://github.com/getify/You-Dont-Know-JS/blob/master/scope%20&%20closures/README.md#you-dont-know-js-scope--closures

Link to post
Share on other sites

if you are trying to access this.game you would need to do it that way. 

 

Javascript can be annoying about this, because anytime you create a new function the keyword THIS === that function.

 

But anyway, phaser I guess uses a system where you pass "this" through other functions.

 

I would rather just scope what I need to access properly instead of passing "this" around - I think that's too hacky

 

It would be very beneficial for you to learn about how javascript scopes things, it has helped me tremendously and it has allowed me to save a lot of headache.  IF you read the following article, you should have a full understanding of "this" 

 

Here:

 

https://github.com/getify/You-Dont-Know-JS/blob/master/scope%20&%20closures/README.md#you-dont-know-js-scope--closures

Still pretty new at this, pretty much clueless how class and constructor works in JS or phaser, so accessing anything directly probably not for a noob like me. I tried some pure javascript to pass some global.variable for simple score keeping, but all it seem to do is nag at me on the console on how it was not defined.

 

That will be next thread if i couldnt figure it out lol, thanks Matt! :)

 

EDIT: hey! thanks again for the resource! :DD

Link to post
Share on other sites

You're welcome and I'm happy to help!  I'm sort of an evangelist when it comes to Javascript best practices and I'm also still learning and I love to be corrected and challenged.  

 

It's ok we're all new at something!  I'm very new to phaser!  Which is why I didnt know about passing "this" through the last parameter, but I probably would have got it working without needing to.  

 

I see you're using CommonJS, may I ask what you're using to bundle it?  

 

Anyway I'm glad you got it working.

Link to post
Share on other sites

Losing a function's "this" is very common in JS. Anytime you use a function handler in the regular ol' making webpages world (i.e. addEventListener) you can lose the this if your code looks like this:

button.addEventListener('click', this.onClick);

So you might write this, instead:

button.addEventListener('click', this.onClick.bind(this));

"bind" is a method on Function that "creates a new function that, when called, has its this keyword set to the provided value, with a given sequence of arguments preceding any provided when the new function is called." (from the Mozilla Developer Network)

 

That's great, but there are performance implications to binding your functions vs. using other Function methods like "call" or "apply". Specifically, in most JS implementations, "bind" is slow vs. "call" or "apply". For a webpage that might not matter as much, but it definitely matters for a game engine.

 

I bet that's the main reason "this" gets passed as the callbackContext a lot in Phaser.

 

Another solution might be to do this:

var that = this;button.addEventListener('click', function(e) { that.onClick(e); });

That would get around this problem without using "call" or "apply", but now we're making an entirely new function. In the boring ol' webpages world this can lead to circular scope references between the closure (your new function) and any DOM elements it references. Because the closure references the DOM node and the DOM node references the closure (via the event callback) they will both hang around forever, never claimed by the GC. This will still probably perform better than "bind", though.

 

Just when you thought function invocations were boring and easy... welcome to JS!

Link to post
Share on other sites

drhayes that is a well thoughtout post that really does good here for all of us, I've liked it and thank you for sharing.

 

The "this" thing is a gotcha for everyone new to JS.

 

I personally try to avoid passing context unless absolutely necessary, I try to "closure in" context instead, but I think that is just a matter of style.  I also may change my mind :)  IT really just depends on what is the most optimal and what is the most clear and concise code.  I strive for clear and concise code as much as possible, but not to sacrifice performance too much.. It's such a balance!

Link to post
Share on other sites

jmp909, anonymous functions should be discarded unless they are used in a certain way that would require the lexical scope to keep them in something else's closure. Also  https://github.com/getify/You-Dont-Know-JS/blob/master/scope%20&%20closures/ch5.md (closures are all around you) - also object observation can be slow too (but maybe it is faster in canvas)

 

I prefer to have clear concise code over performance, but performance is still very important, I think there is a balance.  Maybe we can test this on jsperf.com ????

 

But maybe you can share what you mean?  Could you show an example how you use listeners? Or if this topic is being derailed, maybe we can discuss this via PM?  I'm really curious and it looks like a learning opportunity and I love those 

Link to post
Share on other sites

I just use listeners as per the original example with the additional required 'this' listenerContext. To me that's more clear code, especially if you're creating a bunch of event listeners. (onInputUp, onInputDown, onInputOver etc). I'd rather have the callback implementations separated out from that block of code. Personal preference I guess.

 

I just mean like this

upButton.onDown.add(this.moveUp, this);downButton.onDown.add(this.moveDown, this);leftButton.onDown.add(this.moveLeft, this);rightButton.onDown.add(this.moveRight, this);fireButton.onDown.add(this.shoveDown, this);

I'm not sure how object walking vs temporary function creation performs either

This might be worth a read though
http://neversaw.us/2013/09/04/on-the-performance-of-closures-in-v8/

The performance example here only relates to the creation of the functions rather than the execution
http://stackoverflow.com/questions/80802/does-use-of-anonymous-functions-affect-performance

But yes we are slightly off topic now ;)

Link to post
Share on other sites

Hey jmp909, another thing you said earlier, which I really liked, was that you make your functions re-usable.  That's really important as well.

 

To me, I would only use anonymous functions for three things:

 

1. Event listeners only created once and listened to throughout the state

2. Fire and Forget type functions (possibly wouldn't even do it there)

3. Faux Block Scoping (a la functional scope)

 

Ok so let me address your response here

 

I just use listeners as per the original example with the additional required 'this' listenerContext. To me that's more clear code, especially if you're creating a bunch of event listeners. (onInputUp, onInputDown, onInputOver etc). I'd rather have the callback implementations separated out from that block of code. Personal preference I guess.

 

I just mean like this

upButton.onDown.add(this.moveUp, this);downButton.onDown.add(this.moveDown, this);leftButton.onDown.add(this.moveLeft, this);rightButton.onDown.add(this.moveRight, this);fireButton.onDown.add(this.shoveDown, this);

 

 

Ok, so now onto the next portion of style you brought here!  I have to admit, I have swung back and fourth on handling callbacks.  IT really depends on the overall architecture of the state or object I am working on, and that really might not be helpful, but it does just trust me.

 

More or less, the best practice would be to separate the callback out as you have simply because you follow a single responsibility principal.  What would be even better in my opinion is some sort of JSON like object (or POJO - plain ole JS object) - that contained a map or hash of common events.

 

But I still don't like passing "this" to another object, especially if you do it for every...single...handler - essentially you end up wasting resources to pass references to "this" around where honestly you could just have it reachable in scope just one time across all of the objects.  I think it would be cleaner and more performant but I haven't tested it.

 

 

I'm confident that it is much more cost-effective to avoid creating new references to an object that you can already access if it is scoped to say, the window.  So if you already did something like "var game = Phaser.xxxx" then just use game and not pass this around, only pass this around if you absolutely need to, like if you're nested in deep or something.

 

Overall, this is a great discussion.  Offtopic maybe, but so very rewarding.  Thank you for sharing.

Link to post
Share on other sites

yup i've come from AS3 too

 

just to clarify for anyone still reading .. these 2 appear to do the same thing on the surface

tween.onComplete.add(this.shoveDownComplete.bind(this));
tween.onComplete.add(this.shoveDownComplete, this);

I don't know if there is actually any difference when they're processed.. looking at the source for Signals, I don't see bind() ever being called internally, unless I'm looking in the wrong place

https://github.com/photonstorm/phaser/blob/db641ca82e608470bd7902de3447d048d9715ab5/src/core/SignalBinding.js

Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Loading...
  • Recently Browsing   0 members

    No registered users viewing this page.

×
×
  • Create New...