Jaspery Posted October 14, 2015 Share Posted October 14, 2015 So typically you have a function that switch to another state that call be call via a button or spritesthis.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 comment Share on other sites More sharing options...
geros Posted October 14, 2015 Share Posted October 14, 2015 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 comment Share on other sites More sharing options...
Jaspery Posted October 14, 2015 Author Share Posted October 14, 2015 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 comment Share on other sites More sharing options...
geros Posted October 14, 2015 Share Posted October 14, 2015 Which one? Link to comment Share on other sites More sharing options...
Jaspery Posted October 14, 2015 Author Share Posted October 14, 2015 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 Link to comment Share on other sites More sharing options...
geros Posted October 14, 2015 Share Posted October 14, 2015 I ma sorry for not stating my question correctly. I rephrase which particular generator have you used for creating the project? Link to comment Share on other sites More sharing options...
Jaspery Posted October 14, 2015 Author Share Posted October 14, 2015 Yeoman Generator https://github.com/codevinsky/generator-phaser-official Link to comment Share on other sites More sharing options...
drhayes Posted October 14, 2015 Share Posted October 14, 2015 Sorry, I'm a little confused. You mentioned a button, but that's not where your error is, right? What's calling updateTimer? What does the line that invokes it say? What method is that in? Link to comment Share on other sites More sharing options...
MattMcFarland Posted October 14, 2015 Share Posted October 14, 2015 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 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 comment Share on other sites More sharing options...
Jaspery Posted October 14, 2015 Author Share Posted October 14, 2015 '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 comment Share on other sites More sharing options...
MattMcFarland Posted October 14, 2015 Share Posted October 14, 2015 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 = gameexports.play = play some other file:var game = require(game);var play = require(play); Link to comment Share on other sites More sharing options...
drhayes Posted October 14, 2015 Share Posted October 14, 2015 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 comment Share on other sites More sharing options...
Jaspery Posted October 14, 2015 Author Share Posted October 14, 2015 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 comment Share on other sites More sharing options...
MattMcFarland Posted October 14, 2015 Share Posted October 14, 2015 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 comment Share on other sites More sharing options...
Jaspery Posted October 14, 2015 Author Share Posted October 14, 2015 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--closuresStill 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 comment Share on other sites More sharing options...
MattMcFarland Posted October 14, 2015 Share Posted October 14, 2015 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 comment Share on other sites More sharing options...
drhayes Posted October 14, 2015 Share Posted October 14, 2015 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! MattMcFarland 1 Link to comment Share on other sites More sharing options...
MattMcFarland Posted October 14, 2015 Share Posted October 14, 2015 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 comment Share on other sites More sharing options...
jmp909 Posted October 14, 2015 Share Posted October 14, 2015 closures in a loop would create a new anonymous function on each pass though wouldn't they? I prefer to have my listener function defined separately. I imagine it would be better for performance. but i've not done any tests to prove that also it means the listener is reusable by other objects. Link to comment Share on other sites More sharing options...
MattMcFarland Posted October 14, 2015 Share Posted October 14, 2015 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 comment Share on other sites More sharing options...
jmp909 Posted October 15, 2015 Share Posted October 15, 2015 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 thisupButton.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 eitherThis might be worth a read thoughhttp://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 executionhttp://stackoverflow.com/questions/80802/does-use-of-anonymous-functions-affect-performanceBut yes we are slightly off topic now MattMcFarland 1 Link to comment Share on other sites More sharing options...
MattMcFarland Posted October 15, 2015 Share Posted October 15, 2015 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 state2. 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 thisupButton.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 comment Share on other sites More sharing options...
jmp909 Posted October 15, 2015 Share Posted October 15, 2015 i'm using TypeScript Classes. you have to add 'this' ! MattMcFarland 1 Link to comment Share on other sites More sharing options...
MattMcFarland Posted October 15, 2015 Share Posted October 15, 2015 Hmm I haven't used TypeScript (I've thoughh about it because I do love typing, I used to use superstrict mode in as3) - I am thinking of using flowtype as well.. But I'm just not sure I havent taken the plunge yet. Link to comment Share on other sites More sharing options...
jmp909 Posted October 15, 2015 Share Posted October 15, 2015 yup i've come from AS3 too just to clarify for anyone still reading .. these 2 appear to do the same thing on the surfacetween.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 placehttps://github.com/photonstorm/phaser/blob/db641ca82e608470bd7902de3447d048d9715ab5/src/core/SignalBinding.js Link to comment Share on other sites More sharing options...
Recommended Posts