GaryS

Calling parent (super) functions from prefab

Recommended Posts

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?

Share this post


Link to post
Share on other sites

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.

Share this post


Link to post
Share on other sites

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?

Share this post


Link to post
Share on other sites

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.

Share this post


Link to post
Share on other sites

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.

Share this post


Link to post
Share on other sites

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!
 

Share this post


Link to post
Share on other sites

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.

Share this post


Link to post
Share on other sites

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.

 

Share this post


Link to post
Share on other sites

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;
		}
	},	

 

Share this post


Link to post
Share on other sites

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!

Share this post


Link to post
Share on other sites

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?

Share this post


Link to post
Share on other sites

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...

Share this post


Link to post
Share on other sites

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.
 

Share this post


Link to post
Share on other sites

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.

Share this post


Link to post
Share on other sites

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.

Share this post


Link to post
Share on other sites

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.

Share this post


Link to post
Share on other sites

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.

Share this post


Link to post
Share on other sites

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();
    },

 

Share this post


Link to post
Share on other sites

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.

blocks.png

Share this post


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.