Miniversal Posted April 27, 2015 Share Posted April 27, 2015 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 More sharing options...
Miniversal Posted April 28, 2015 Author Share Posted April 28, 2015 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() ). Juls 1 Link to comment Share on other sites More sharing options...
GourmetGorilla Posted May 5, 2015 Share Posted May 5, 2015 What if you want to remove the object from the group without killing it? Link to comment Share on other sites More sharing options...
Miniversal Posted May 6, 2015 Author Share Posted May 6, 2015 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 CollisionGroupcollidedCollisionGroup = game.physics.p2.createCollisionGroup(); //Then update the destroyEnemy method to change the effected object's groupfunction destroyEnemy(body1, body2){ Phaser.Physics.P2.setCollisionGroup(body1, collidedCollisionGroup); Phaser.Physics.P2.setCollisionGroup(body2, collidedCollisionGroup);} Link to comment Share on other sites More sharing options...
GourmetGorilla Posted May 6, 2015 Share Posted May 6, 2015 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 More sharing options...
Miniversal Posted May 28, 2015 Author Share Posted May 28, 2015 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 More sharing options...
Recommended Posts