Jump to content

Remove body/sprite from CollisionGroup when destroyed


Miniversal
 Share

Recommended Posts

I'm trying to handle "killing" enemies, in my case aliens, when they collide with the player's bullets. It feels like I'm doing the right thing when I use the destroy method on the body when they collide except my update functions are trying to move both the enemy and the bullet after they've been destroyed. Obviously, this causes a runtime error because the body is no longer in the collection. I'm using removeFromWorld() at the moment to avoid the runtime issue but my sprites just stop moving on screen. I'd like to remove them from the canvas (and play an explosion at some point).

 

How do I remove the body from the group/world/etc. properly?

 

Here's the essential bits of code:

game.physics.p2.setImpactEvents(true);game.physics.p2.restitution = 0.8;alienCollisionGroup = game.physics.p2.createCollisionGroup();bulletCollisionGroup = game.physics.p2.createCollisionGroup();function destroyEnemy(body1, body2){    body1.removeFromWorld();    //body1.destroy();    body2.removeFromWorld();    //body2.destroy();    console.log(body1.toString());}// ENEMIESaliens = game.add.group();aliens.enableBody = true;aliens.physicsBodyType = Phaser.Physics.P2JS;for (var i = 0; i < 10; i++) {    var alien = aliens.create(game.rnd.integerInRange(200, 1700), game.rnd.integerInRange(-200, 400), 'alien');    alien.body.setCollisionGroup(alienCollisionGroup);    alien.body.collides([alienCollisionGroup, playerCollisionGroup]);    alien.body.collides([alienCollisionGroup, bulletCollisionGroup]);    alien.animations.add('left',[1], 3, true);    alien.animations.add('right',[0],3,true);    game.physics.p2.enable(alien,false);}var Bullet = function (game, key) {    Phaser.Sprite.call(this, game, 0, 0, key);    this.texture.baseTexture.scaleMode = PIXI.scaleModes.NEAREST;    this.anchor.set(0.5);    this.checkWorldBounds = true;    this.outOfBoundsKill = true;    this.exists = false;    this.tracking = true;    this.scaleSpeed = 0;};Bullet.prototype = Object.create(Phaser.Sprite.prototype);Bullet.prototype.constructor = Bullet;Bullet.prototype.fire = function (x, y, angle, speed, gx, gy) {    gx = gx || 0;    gy = gy || 0;    this.body.setCollisionGroup(bulletCollisionGroup);    this.body.collides(alienCollisionGroup, destroyEnemy, this);    this.reset(x, y);    this.scale.set(1);    this.angle = angle;    this.body.rotation = 0;    this.body.force.x = Math.cos(angle) * speed;        //this.body.force.y = Math.sin(angle) * speed;    this.body.gravity.set(gx, gy);};Bullet.prototype.update = function () {    if (this.tracking){       this.rotation = Math.atan2(this.body.velocity.y, this.body.velocity.x); //<-Runtime issue     }    if (this.scaleSpeed > 0){        this.scale.x += this.scaleSpeed;        this.scale.y += this.scaleSpeed;    }};

Many thanks in advance :)

Link to comment
Share on other sites

Okay, I feel dumb but at least I figured out what I was doing wrong. I had tried to use body1.kill() but that threw an error. I didn't realize you have to use kill() on the sprite.

 

Just in case anyone else has similar trouble figuring this out, I simply changed the call in my destroyEnemy() method to refer to the sprite of the body ( e.g body1.sprite.kill() ).

Link to comment
Share on other sites

I don't see a way to remove an object from a collisiongroup. However, you could create another collisiongroup that does not collide with anything and use the setCollisionGroup(object, group) method to achieve a similar effect. At least, it seems that would accomplish the same thing as removing the sprites from their original collision group(s).

 

//Create an extra CollisionGroup

collidedCollisionGroup = game.physics.p2.createCollisionGroup();

 

//Then update the destroyEnemy method to change the effected object's group

function destroyEnemy(body1, body2){

 Phaser.Physics.P2.setCollisionGroup(body1, collidedCollisionGroup);

 Phaser.Physics.P2.setCollisionGroup(body2, collidedCollisionGroup);
}

Link to comment
Share on other sites

Thanks Miniversal, my codes a little different from the above. I can't quite seem to apply your suggestion to my situation. 

My collisions look like this: 

    //  Run collision        game.physics.arcade.overlap(bullets, aliens, collisionHandler, null, this);        game.physics.arcade.overlap(enemyBullets, player, enemyHitsPlayer, null, this);

Here's my full code: 
 

var game = new Phaser.Game(800, 600, Phaser.AUTO, 'phaser-example', { preload: preload, create: create, update: update, render: render });function preload() {    game.load.image('bullet', 'assets/games/invaders/bullet.png');    game.load.image('enemyBullet', 'assets/games/invaders/enemy-bullet.png');    game.load.spritesheet('invader', 'assets/games/invaders/invader32x32x4.png', 32, 32);    game.load.spritesheet('cured', 'assets/games/invaders/happy32x32x4.png', 32, 32);    game.load.spritesheet('ship', 'assets/games/invaders/player_sprite.png', 68, 43);    game.load.image('ship2', 'assets/games/invaders/playerSolo.png');    game.load.spritesheet('kaboom', 'assets/games/invaders/explode.png', 128, 128);    game.load.spritesheet('love', 'assets/games/invaders/hearts.png', 128, 128);    game.load.image('hills', 'assets/games/invaders/hills.png');    game.load.image('starfield', 'assets/games/invaders/starfield.png');    game.load.image('clouds', 'assets/games/invaders/clouds.png');}var player;var aliens;// var smiles;var bullets;var bulletTime = 0;var cursors;var fireButton;var explosions;var explosions2;var starfield;var score = 0;var scoreString = '';var scoreText;var lives;var enemyBullet;var firingTimer = 0;var stateText;var livingEnemies = [];function create() {    game.physics.startSystem(Phaser.Physics.ARCADE);    //  The scrolling starfield background    starfield = game.add.tileSprite(0, 0, 800, 600, 'starfield');    clouds = game.add.tileSprite(0, 0, 800, 600, 'clouds');    hills = game.add.tileSprite(0, 255, 800, 345, 'hills');    //  Our bullet group    bullets = game.add.group();    bullets.enableBody = true;    bullets.physicsBodyType = Phaser.Physics.ARCADE;    bullets.createMultiple(30, 'bullet');    bullets.setAll('anchor.x', 0.5);    bullets.setAll('anchor.y', 1);    bullets.setAll('outOfBoundsKill', true);    bullets.setAll('checkWorldBounds', true);    // The enemy's bullets    enemyBullets = game.add.group();    enemyBullets.enableBody = true;    enemyBullets.physicsBodyType = Phaser.Physics.ARCADE;    enemyBullets.createMultiple(30, 'enemyBullet');    enemyBullets.setAll('anchor.x', 0.5);    enemyBullets.setAll('anchor.y', 1);    enemyBullets.setAll('outOfBoundsKill', true);    enemyBullets.setAll('checkWorldBounds', true);    //  The hero!    player = game.add.sprite(400, 535, 'ship');    player.anchor.setTo(0.5, 0.5);    game.physics.enable(player, Phaser.Physics.ARCADE);    player.animations.add('fly2', [ 0, 1, 2, 3 , 4, 5, 6, 7, 8, 9], 15, true);    player.play('fly2');    //  The baddies!    aliens = game.add.group();    aliens.enableBody = true;    aliens.physicsBodyType = Phaser.Physics.ARCADE;    createAliens();    // The smilies!  // smiles = game.add.group();  // smiles.enableBody = false;    //  smiles.physicsBodyType = Phaser.Physics.ARCADE;    //  The score    scoreString = 'Score : ';    scoreText = game.add.text(10, 10, scoreString + score, { font: '34px Arial', fill: '#1C2840' });    //  Lives    lives = game.add.group();    game.add.text(game.world.width - 100, 10, 'Lives ', { font: '34px Arial', fill: '#1C2840' });    //  Text    stateText = game.add.text(game.world.centerX,game.world.centerY,' ', { font: '84px Arial', fill: '#5C85D6' });    stateText.anchor.setTo(0.5, 0.5);    stateText.visible = false;    for (var i = 0; i < 3; i++)     {        var ship = lives.create(game.world.width - 100 + (35 * i), 60, 'ship2');        ship.anchor.setTo(0.5, 0.5);        ship.angle = 0;        ship.alpha = 0.65;    }    //  An explosion pool    explosions = game.add.group();    explosions.createMultiple(30, 'love');    explosions.forEach(setupInvader, this);    //  2nd explosion pool    explosions2 = game.add.group();    explosions2.createMultiple(30, 'kaboom');    explosions2.forEach(setupPlayer, this);    //  And some controls to play the game with    cursors = game.input.keyboard.createCursorKeys();    fireButton = game.input.keyboard.addKey(Phaser.Keyboard.SPACEBAR);    }function createAliens () {    for (var y = 0; y < 4; y++)    {        for (var x = 0; x < 10; x++)        {            var alien = aliens.create(x * 48, y * 50, 'invader');            alien.anchor.setTo(0.5, 0.5);            alien.animations.add('fly', [ 0, 1, 2, 3 ], 20, true);            alien.play('fly');            alien.body.moves = false;        }    }    aliens.x = 100;    aliens.y = 200;    //  All this does is basically start the invaders moving. Notice we're moving the Group they belong to, rather than the invaders directly.    var tween = game.add.tween(aliens).to( { x: 200 }, 2000, Phaser.Easing.Linear.None, true, 0, 1000, true);    //  When the tween loops it calls descend    tween.onLoop.add(descend, this);}function setupInvader (invader) {    invader.anchor.x = 0.5;    invader.anchor.y = 0.5;    invader.animations.add('love');}function setupPlayer (ship) {    ship.anchor.x = 0.5;    ship.anchor.y = 0.5;    ship.animations.add('kaboom');}function descend() {    aliens.y -= 10;}function update() {    //  Scroll the background  //  starfield.tilePosition.y += 2;      //  Scroll the clouds    clouds.tilePosition.x -= .4;    if (player.alive)    {        //  Reset the player, then check for movement keys        player.body.velocity.setTo(0, 0);        if (cursors.left.isDown)        {            player.body.velocity.x = -200;        }        else if (cursors.right.isDown)        {            player.body.velocity.x = 200;        }        //  Firing?        if (fireButton.isDown)        {            fireBullet();        }        if (game.time.now > firingTimer)        {            enemyFires();        }        //  Run collision        game.physics.arcade.overlap(bullets, aliens, collisionHandler, null, this);        game.physics.arcade.overlap(enemyBullets, player, enemyHitsPlayer, null, this);    }}function render() {    // for (var i = 0; i < aliens.length; i++)    // {    //     game.debug.body(aliens.children[i]);    // }}function collisionHandler (bullet, alien) {    //  When a bullet hits an alien we kill them both    bullet.kill();    alien.loadTexture("cured");    var happy = alien;//    smiles.add(happy);        // move alien to bottom    var tween2 = game.add.tween(happy).to( { y: 300 }, 1000, Phaser.Easing.Linear.EaseIn, true);        tween2.onComplete.add(doNext, this);    tween2.start();    function doNext (happy){        happy.kill();    };        //  Increase the score    score += 20;    scoreText.text = scoreString + score;    //  And create an explosion     var explosion = explosions.getFirstExists(false);    explosion.reset(alien.body.x, alien.body.y);    explosion.play('love', 30, false, true);    if (aliens.countLiving() == 0)    {        score += 1000;        scoreText.text = scoreString + score;        enemyBullets.callAll('kill',this);        stateText.text = " You Won, \n Click to restart";        stateText.visible = true;        //the "click to restart" handler        game.input.onTap.addOnce(restart,this);    }}function enemyHitsPlayer (player,bullet) {        bullet.kill();    live = lives.getFirstAlive();    if (live)    {        live.kill();    }    //  And create an explosion     var explosion2 = explosions2.getFirstExists(false);    explosion2.reset(player.body.x, player.body.y);    explosion2.play('kaboom', 30, false, true);    // When the player dies    if (lives.countLiving() < 1)    {        player.kill();        enemyBullets.callAll('kill');        stateText.text=" GAME OVER \n Click to restart";        stateText.visible = true;        //the "click to restart" handler        game.input.onTap.addOnce(restart,this);    }}function enemyFires () {    //  Grab the first bullet we can from the pool    enemyBullet = enemyBullets.getFirstExists(false);    livingEnemies.length=0;    aliens.forEachAlive(function(alien){        // put every living enemy in an array        livingEnemies.push(alien);    });    if (enemyBullet && livingEnemies.length > 0)    {                var random=game.rnd.integerInRange(0,livingEnemies.length-1);        // randomly select one of them        var shooter=livingEnemies[random];        // And fire the bullet from this enemy        enemyBullet.reset(shooter.body.x, shooter.body.y);        game.physics.arcade.moveToObject(enemyBullet,player,120);        firingTimer = game.time.now + 2000;    }}function fireBullet () {    //  To avoid them being allowed to fire too fast we set a time limit    if (game.time.now > bulletTime)    {        //  Grab the first bullet we can from the pool        bullet = bullets.getFirstExists(false);        if (bullet)        {            //  And fire it            bullet.reset(player.x, player.y + 8);            bullet.body.velocity.y = -400;            bulletTime = game.time.now + 200;        }    }}function resetBullet (bullet) {    //  Called if the bullet goes out of the screen    bullet.kill();}function restart () {    //  A new level starts        //resets the life count    lives.callAll('revive');    //  And brings the aliens back from the dead     aliens.removeAll();    createAliens();    //revives the player    player.revive();    //hides the text    stateText.visible = false;}

I tried several things. I tried changing the shot alien's name to 'happy', then putting 'happy' in another group, but it was still able to be shot at.  Strange. I thought if you took the object out of one group and placed it in another, it would lose the attributes of the previous group it was in. 

Basically I'm just trying to have this object/sprite not able to be shot at again once it's been shot at once, and to take it out of the group it's it but keep it's current x and y value on the stage. To put it in a new group I added a group called 'smiles' then added 'happy' to smiles with this: 

smiles.add(happy);

But it changed position on me :)

Any suggestions?

 

Link to comment
Share on other sites

  • 3 weeks later...

Dang, that is a pickle. I wasn't sure if my suggestion would work and, unfortunately, it appears that it doesn't. I'm not as familiar with the ARCADE physics library so I'd be shooting in the dark still but I do know that in the P2 physics library, if you remove the body from the sprite, it stops interacting with other sprites. Maybe that would work?

 

Or what's probably the better thing to do would be to make a prototype sprite of your aliens and add a boolean field for whether it's been shot or not. Default it to false when each new alien is created and then, in your collision detection, check the value of the field and set it true the first time an alien is shot.

 

This is totally untested but it would be something like this...

var Alien = function(game,key){    Phaser.Sprite.call(this, game, 0, 0, key);    //...whatever other setup an alien requires as a sprite    //...    this.hasBeenShot = false; //<- This is where you setup the new field that needs to be checked during collision}Alien.prototype = Object.create(Phaser.Sprite.prototype);Alien.prototype.constructor = Alien;

So this would allow you to check for "hasBeenShot" when the Alien collides with anything...so you'd need to check for this in your collision handler and fork your logic accordingly.

 

Does that help?

Link to comment
Share on other sites

 Share

  • Recently Browsing   0 members

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