Jump to content

Sprite collides with nested groups


Recommended Posts

Hey guys,

I'm trying to learn Phaser by recreating Space Invaders.

I'm now developing the player shot colliding with the aliens.


My aliens are in a nested group (rows of aliens inside a generic rows group, since in the game every row moves at different times) and my player and the sprites of the shots are external of this groups.

In order to make the collide happen I've looked at THIS example and set things like this

//I won't write all the inheriting stuff Alien = function(game){ Phaser.Sprite.call(this,game,0,0,'alienimg'); this.physicsBodyType = Phaser.Physics.ARCADE; this.bodyEnabled = true;}AlienRow = function(game){ Phaser.Group.call(this,game); for(var i = 0; i < numAlienPerOneRow; i++) {  this.add(new Alien(this.game))  //more } this.bodyEnabled = true;}AlienGroup = function(game){ Phaser.Group.call(this,game); for(var i = 0; i < numRows; i++) {  this.add(new AlienRow(this.game))  //more } this.physicsBodyType = Phaser.Physics.ARCADE; this.bodyEnabled = true;}/******** Game.js ***************/    create: function ()    {        console.log('Game create');        this.physics.startSystem(Phaser.Physics.ARCADE);        this.alienG = new AlienGroup(this.game,'easy');        this.alienG.x = (this.world.width - this.alienG.width) / 2;        this.alienG.y = 10;        this.add.existing(this.alienG);        this.player = new Player(this.game);        this.player.x = this.world.centerX;        this.player.y = this.world.height;        this.add.existing(this.player);        this.controls = this.input.keyboard.createCursorKeys();    },    update: function ()    {        if(this.controls.left.isDown)            this.player.x -= 5;        else if(this.controls.right.isDown)            this.player.x += 5;                if(!this.spacebarWasDown && this.input.keyboard.isDown(Phaser.Keyboard.SPACEBAR))        {            this.projectile = new Projectile(this.game);            this.projectile.x = this.player.x;            this.projectile.y = this.player.y - this.player.height;            this.add.existing(this.projectile);            this.physics.arcade.enable(this.projectile,Phaser.Physics.ARCADE);        }        if(this.projectile){            this.physics.arcade.collide(this.projectile, this.alienG, this.collisionHandler, null, this);            //this.physics.arcade.collide(this.alienG, this.alienG);        }        this.spacebarWasDown = this.input.keyboard.isDown(Phaser.Keyboard.SPACEBAR);    },    collisionHandler: function (what)    {        console.log(what);    }

Unfortunately collisionHandler console.log is not showing.

Am I missing something?

Is the problem the fact that I'm working with nested groups?


I hope I've been clear =)


Thank you in advance!

Link to comment
Share on other sites

Collide/overlap will not step through groups recursively, so yes, if the collide handler is looking at a group, and the only thing that group contains is other groups, you won't get any collisions. A way around this would be to add all of your aliens to a flat array as you create them, and check your shot against this array instead. This will then work regardless of how they're grouped.

Link to comment
Share on other sites

Hey guys, sorry for spamming but I've been testing the code I wrote with Lewster suggestion and I've found an issue.

It happens sometimes that when I press the shoot button I see no projectile go out from my player's cannon and an alien magically dies somewhere in the formation.

It's like the 2 elements are in the same spot when the projectile is added to the world but that's not true.


It even happens that I see the projectile and that the collision happens with a very distant element.


Isn't it that collision doesn't look for the absolute position while working with sprites inserted in nested groups but looks for the relative one?


Thank you again for your patience!

Link to comment
Share on other sites

Yes, I believe this is the case Miroku - if you debug the bodies of all of your sprites you should theoretically be able to see where the game thinks they are and make a judgement call based on that. I don't think currently bodies calculate their positions based on the global coordinates of their sprites, though I guess there are arguments for and against that.


I guess my solution would be more arrays! Have an array for all of your enemies, then several arrays holding each row, and then loop through the row arrays moving each sprite by the amount you'd normally have moved the groups each update.

Link to comment
Share on other sites

Hmm actually I'm wrong - it does seem to try and get the world coordinates:

this.position.x = (this.sprite.world.x - (this.sprite.anchor.x * this.width)) + this.offset.x;this.position.y = (this.sprite.world.y - (this.sprite.anchor.y * this.height)) + this.offset.y;

However, I don't think the world coordinates are being correctly determined, as the code that does that just looks at the parent position:

this.world.setTo(this.parent.position.x + this.position.x, this.parent.position.y + this.position.y);

When I'm guessing it should really be going through all of the parents to figure this out.

Link to comment
Share on other sites

I worked around this by extending Sprite and creating my enemies from this new class.

Now it works really better!

Haven't tested it enough to say that with 100% of certainty.

I will get back to you when I have done it ;)

/******* FixedSprite.js ********/FixedSprite = function(game, x, y, key, frame){    Phaser.Sprite.call(this,game, x, y, key, frame);};FixedSprite.prototype = Object.create(Phaser.Sprite.prototype);FixedSprite.prototype.constructor = FixedSprite;FixedSprite.prototype.absoluteX = function(){    var level = this;    var absX = level.position.x;    while(level.parent != null)    {        level = level.parent;        absX += level.position.x;    }    return absX;};FixedSprite.prototype.absoluteY = function(){    var level = this;    var absY = level.position.y;    while(level.parent != null)    {        level = level.parent;        absY += level.position.y;    }    return absY;};FixedSprite.prototype.preUpdate = function(){    if (this._cache[4] === 1 && this.exists)    {        this.world.setTo(this.absoluteX(), this.absoluteY());        this.worldTransform.tx = this.world.x;        this.worldTransform.ty = this.world.y;        this._cache[0] = this.world.x;        this._cache[1] = this.world.y;        this._cache[2] = this.rotation;        if (this.body)        {            this.body.preUpdate();        }        this._cache[4] = 0;        return false;    }    this._cache[0] = this.world.x;    this._cache[1] = this.world.y;    this._cache[2] = this.rotation;    if (!this.exists || !this.parent.exists)    {        //  Reset the renderOrderID        this._cache[3] = -1;        return false;    }    if (this.lifespan > 0)    {        this.lifespan -= this.game.time.elapsed;        if (this.lifespan <= 0)        {            this.kill();            return false;        }    }    //  Cache the bounds if we need it    if (this.autoCull || this.checkWorldBounds)    {        this._bounds.copyFrom(this.getBounds());    }    if (this.autoCull)    {        //  Won't get rendered but will still get its transform updated        this.renderable = this.game.world.camera.screenView.intersects(this._bounds);    }    if (this.checkWorldBounds)    {        //  The Sprite is already out of the world bounds, so let's check to see if it has come back again        if (this._cache[5] === 1 && this.game.world.bounds.intersects(this._bounds))        {            this._cache[5] = 0;            this.events.onEnterBounds.dispatch(this);        }        else if (this._cache[5] === 0 && !this.game.world.bounds.intersects(this._bounds))        {            //  The Sprite WAS in the screen, but has now left.            this._cache[5] = 1;            this.events.onOutOfBounds.dispatch(this);            if (this.outOfBoundsKill)            {                this.kill();                return false;            }        }    }    this.world.setTo(this.game.camera.x + this.worldTransform.tx, this.game.camera.y + this.worldTransform.ty);    if (this.visible)    {        this._cache[3] = this.game.stage.currentRenderOrderID++;    }    this.animations.update();    if (this.body)    {        this.body.preUpdate();    }    //  Update any Children    for (var i = 0, len = this.children.length; i < len; i++)    {        this.children[i].preUpdate();    }    return true;};/******* Alien .js ********/Alien = function(game){ FixedSprite.call(this,game,0,0,'alienimg'); this.physicsBodyType = Phaser.Physics.ARCADE; this.bodyEnabled = true;}
Link to comment
Share on other sites

Ok I managed to do some more testing.

Unfortunately I found another strange behaviour.


As you'll see in the video I'm linking below, before one row of aliens moves there's a fraction of second in which all of them are in the left top corner and it's not just a debug render problem because if I shoot at that point I actually kill some alien.

Maybe it's a preUpdate issue? I just don't know .__.


Here the video:


Additional Info: I'm moving the alien rows by using a timer.loop event

Edited by Miroku_87
Link to comment
Share on other sites


  • Recently Browsing   0 members

    • No registered users viewing this page.
  • Create New...