GaryS Posted March 9, 2016 Share Posted March 9, 2016 Hi guys, I'm trying to extend the .kill() function in a prefab that extends Phaser.Sprite. I have the following code: Block.prototype.kill = function() { var killTween = this.game.add.tween(this.scale); killTween.to({x:0,y:0}, 200, Phaser.Easing.Linear.None); killTween.onComplete.addOnce(function(){ Object.getPrototypeOf(Block.prototype).kill(this); }, this); killTween.start(); }; This actually seems to work - in that the tween is run and the block is 'killed', but then all hell breaks loose! The physics of the rest of the game start to go a bit nuts. Blocks disappear randomly, collisions occur with nothing... it just generally messes the whole game world up! Can anyone shed any light on this? Link to comment Share on other sites More sharing options...
Pryme8 Posted March 9, 2016 Share Posted March 9, 2016 make sure when you create the new object (the block) you are doing https://api.jquery.com/jquery.extend/ I know nothing of phaser bit I have a sneaking suspicion your just copying your object... when you make your block it should look something like this: var newBlock = $.extend(true, {}, Block); dont quote me on this... but yea might fix it... im not sure how anything in Phaser works though so I may be way off. Link to comment Share on other sites More sharing options...
GaryS Posted March 9, 2016 Author Share Posted March 9, 2016 I'm not actually using jQuery in this project... My Javascript skillz are limited, but as far as I can tell I'm using the standard prototypal inheritance that most people are using for prefabs. I'm also making using of codevinsky's generator and thus I'm using his modular methodology. Here's the complete code: 'use strict'; var Block = function (game, x, y, frame, type) { Phaser.Sprite.call(this, game, x, y, 'block'); game.physics.arcade.enableBody(this); this.outOfBoundsKill = true; this.checkWorldBounds = true; }; Block.prototype = Object.create(Phaser.Sprite.prototype); Block.prototype.constructor = Block; Block.prototype.kill = function() { var killTween = this.game.add.tween(this.scale); killTween.to({x:0,y:0}, 200, Phaser.Easing.Linear.None); killTween.onComplete.addOnce(function(){ Object.getPrototypeOf(Block.prototype).kill(this); }, this); killTween.start(); }; module.exports = Block; Also, I've been wondering - if I add an update function in my prefab, does anyone know the order of execution? e.g. if I update a global variable in my play state's update function and in my prefab's update function, which will be executed last? Link to comment Share on other sites More sharing options...
drhayes Posted March 9, 2016 Share Posted March 9, 2016 You're extending Phaser.Sprite correctly. Unfortunately, going up the prototype chain in ES5 isn't exactly straightforward. Wherever it is you want to call your parent's kill method in your kill method, do this: "Phaser.Sprite.kill.call(this);" The part where you say "Object.getPrototypeOf(Block.prototype)" isn't what you want to happen. You probably want "Phaser.Sprite.kill.call(this);" if I understand your intent. You should *probably* set some kind of flag so that your kill method doesn't get called repeatedly, spawning lots and lots of tweens. Pryme8 1 Link to comment Share on other sites More sharing options...
Pryme8 Posted March 9, 2016 Share Posted March 9, 2016 Yea from what I see when your looking to kill the Sprite your effectively doing it to all block objects. When you make the Sprite assign it to this._sprite on the blocks object and when you need to kill it do what ever you would do to kill a phaser Sprite but access the Sprite through this._sprite.kill() or something like that if being accessed locally or if you have an array holding all the blocks and know which block it is in the array it would look like blocks[id]._sprite.kill() but seince you you are looking to access your kill method in the object not the phaser kill method (if there is one) you would actually just do blocks[id].kill() and in that function on the object have the Sprite be killed so that's where the this._sprite.kill() but if you are able to reference the Sprite directly I don't see why you would need a secondary function. like I said I don't know phaser but I'm kinda savey in js so that's what I see. I'll get my nose out of my area of non knowledge though! Good luck. Link to comment Share on other sites More sharing options...
GaryS Posted March 9, 2016 Author Share Posted March 9, 2016 Thanks for the suggestion, but Phaser.Sprite.kill.call(this); didn't seem to work. I get the following error: Uncaught TypeError: Cannot read property 'call' of undefined My intent is to simply extend the kill function so that when called on objects instantiated via the prefab, the extra code (the tween) is run, and then the parent's kill function is called to actually perform the usual tasks of the kill function. In other languages I would simply use 'super.kill()', but I understand JS (at least in ES5) doesn't work that way. I've seen that TypeScript can do things like this, and apparantly ES6 also, but I'm new to all this and haven't really got to that level yet! Link to comment Share on other sites More sharing options...
Pryme8 Posted March 9, 2016 Share Posted March 9, 2016 Re read what I posted. The anwser is in there. You don't need to extend the kill function. Just call the built in kill function on the Sprite stored in the block object. When you make the block object and it calls the phaser Sprite creation assign that Sprite creation to this._sprite and from then on that block objects Sprite can be accessed and built in phaser Sprite methods should be accessible. Link to comment Share on other sites More sharing options...
drhayes Posted March 9, 2016 Share Posted March 9, 2016 Gah, sorry, that's what I get for typing while distracted: Phaser.Sprite.prototype.kill.call(this); Link to comment Share on other sites More sharing options...
GaryS Posted March 9, 2016 Author Share Posted March 9, 2016 Thanks, Phaser.Sprite.prototype.kill.call(this); seems to work... However, as Pryme8 suggested, this seems to affect multiple blocks in the group. I have blocks in a group being generated at set intervals, but after I've killed a few blocks in this way, no more are generated. Everything works if I don't extend the kill() function. Sorry Pryme8 but I'm afraid I can't make sense of your answer.... Not to say that it's not right, just that I don't have the understanding to get what you're telling me to do! Can you show me some code? As it stands, I'm creating an instance of a block and assigning it to a group, which is a Phaser object. When calling the extended kill function, it's in the context of a specifc block so I don't know how it can affect others. Link to comment Share on other sites More sharing options...
GaryS Posted March 21, 2016 Author Share Posted March 21, 2016 So, did anyone else have any ideas on this? Link to comment Share on other sites More sharing options...
drhayes Posted March 21, 2016 Share Posted March 21, 2016 How are you generating the blocks? Link to comment Share on other sites More sharing options...
GaryS Posted March 21, 2016 Author Share Posted March 21, 2016 The blocks are generated at timed intervals with a call to the following function: // Create a row of blocks createBlockRow: function(row, speed) { var row = row || [1,1,1,1,1,1]; // Loop through block count for (var i = 0; i < row.length; i++) { // 50/50 chance of block being created in this position if (this.game.rnd.integerInRange(0,1) == 1) { // Get first available block var block = this.blockPool.getFirstExists(false); // Check block exists if (!block) { // Create new block block = new Block(this.game, this.game.width, 160 + (i*70)); // Add to block pool this.blockPool.add(block); } else { // Reset position of existing block block.reset(this.game.width, 160 + (i*70)); } block.body.velocity.x = -speed; block.body.immovable = true; block.tint = Math.random() * 0xffffff; } this.blockTime = 0; this.lastBlock = block; } }, Link to comment Share on other sites More sharing options...
drhayes Posted March 22, 2016 Share Posted March 22, 2016 And what does your new, fixed "kill" method look like? Link to comment Share on other sites More sharing options...
GaryS Posted March 22, 2016 Author Share Posted March 22, 2016 I've got this inside my block prefab Block.prototype.kill = function() { var killTween = this.game.add.tween(this.scale); killTween.to({x:0,y:0}, 200, Phaser.Easing.Linear.None); killTween.onComplete.addOnce(function(){ Phaser.Sprite.prototype.kill.call(this); }, this); killTween.start(); }; It appears to work correctly, but seems to adversely affect the rest of the block group and after a few more iterations no more rows of blocks are generated! Link to comment Share on other sites More sharing options...
drhayes Posted March 23, 2016 Share Posted March 23, 2016 I'm stumped. It looks like all of this should work. About the only thing I could think of is that your "kill" method is getting called multiple times, so it's killing your blocks after you've reset them from the pool. Maybe "console.log('kill');" at the start of your "kill" method to verify that's not the case? Link to comment Share on other sites More sharing options...
GaryS Posted March 24, 2016 Author Share Posted March 24, 2016 It seems you're correct - the kill method is being called multiple times... Indefinitely, in fact, whenever the block is 'reset' from the createBlockRow function. It doesn't seem related to be related to the way I'm calling the parent 'kill()' function either. I've narrowed it down to the 'exists' property. So, specifically, this issue will occur with the following code: Block.prototype.kill = function() { var killTween = this.game.add.tween(this.scale); killTween.to({x:0,y:0}, 200, Phaser.Easing.Linear.None); killTween.onComplete.addOnce(function(){ this.exists = false; }, this); killTween.start(); }; Every time the block is 'reset', the kill function is immediately called. Not sure if I've done something wrong here... Link to comment Share on other sites More sharing options...
GaryS Posted March 24, 2016 Author Share Posted March 24, 2016 Aha! It seems it's actually related to the tween! If I remove the tween, all is well: Block.prototype.kill = function() { console.log('Kill called'); Phaser.Sprite.prototype.kill.call(this); }; It doesn't seem to just be the onComplete function that's running though, the whole kill function runs... I may not know much javascript, but I understand even less about how the tweening system works!! I'm guessing it has something to do with the scope in which the onComplete function is running... but I get a little confused with scopes at this level. Link to comment Share on other sites More sharing options...
drhayes Posted March 24, 2016 Share Posted March 24, 2016 Nope, it's not a scope thing, it's an asynchronous thing. Something in Phaser is saying "kill this sprite" and your sprite waits 200ms to kill itself. In that 200ms kill gets called a bunch more times, which adds a bunch more tweens. During all this, your block generation code says "make me a block" and the group says "here's a dead one". Your block code says "cool, reset that block". *Then one of those repeated tweens finishes* and kills the block. Repeat. ( = What you probably want is for your override of kill to only be called once. You could say something like "this.killReceived = true;" at the start of your kill override. Then, in your onComplete handler you call the parent kill method, then set "this.killReceived = false;" so the killing will work correctly the next time. Link to comment Share on other sites More sharing options...
GaryS Posted March 24, 2016 Author Share Posted March 24, 2016 I'm not sure that this is what's happening... The kills aren't being called 200ms after the original, they're being called immediately whenever a reset is run - i.e. when new blocks appear on the screen. The kills take different amounts of time to occur, depending on how long it takes for a block to be reset. Link to comment Share on other sites More sharing options...
drhayes Posted March 24, 2016 Share Posted March 24, 2016 Not your block's kill function, the block's parent kill function. The "Phaser.Sprite.prototype.kill.call(this);" part of the onComplete handler. At least, I *bet* that's what it is. You could log in both places to verify, maybe also in the createBlockRow function, maybe assign IDs to each block as its generated, check to see if a block with a particular ID is about to die. Link to comment Share on other sites More sharing options...
GaryS Posted March 25, 2016 Author Share Posted March 25, 2016 So, I'm logging the block reset function, the call to my child kill function and the tween onComplete. What I've found is that my child reset function is being called immediately on a block.reset() call. This only happens when a tween is in the child kill function - even if I completely remove the tween's onComplete callback and make no call to the parent's kill function at all! At this point I think this is either a bug, or more likely some caveat of how tweens function that I'm not understanding. Link to comment Share on other sites More sharing options...
GaryS Posted March 25, 2016 Author Share Posted March 25, 2016 This happens even if I move the tween outside of the prefab and make no use of a child 'kill' function! I'm getting exactly the same problem if I remove my kill function from the prefab, and use the tween directly in the collision function: blockCollide: function(projectile, block) { // Destroy block var killTween = this.game.add.tween(block.scale); killTween.to({x:0,y:0}, 200, Phaser.Easing.Linear.None); killTween.onComplete.addOnce(function(){ block.kill(); }, this); killTween.start(); }, Link to comment Share on other sites More sharing options...
GaryS Posted March 26, 2016 Author Share Posted March 26, 2016 Aha!! So I've done a bit more testing and I've found out that setting the scale to 0... calls the kill function!!! Secondly, calling the reset() function does not reset the scale... so existing blocks that are reset in their starting position still have a scale of 0 and thus the kill function is immediately called. So, if I simply set the scale back to 1, everything works as I'd expect. What's odd, is that if I set the scale before hitting the reset function, the physics body of the block is roughly 0.1 while the sprite is reset to 1. Even stranger is that this is only the case most of the time... sometimes the physics body is about 0.2... It's almost as if the scale is set to 1 over the course of a few updates and hasn't completed by the time the reset function runs? Here's a screenshot.. the blocks are grey, with the debug of the physics body in green over the top. The blocks that are full green are the new blocks, the grey are ones that have been killed and reset. The top three have the smallest physics body and the fourth is slightly bigger... If I set the scale after resetting the block, everything works fine. drhayes 1 Link to comment Share on other sites More sharing options...
drhayes Posted March 26, 2016 Share Posted March 26, 2016 I'm glad you figured it out! And setting scale = 0 === kill, that's weird. Link to comment Share on other sites More sharing options...
Recommended Posts