Jump to content

Extended sprites don't appear if added as children?


Tilde
 Share

Recommended Posts

This little unfinished Sprite is a Blaad.

 

post-15035-0-28202400-1437132982.png

var Blaad = function(gameS, xS, yS) {  Enemy.apply(this, [gameS, xS, yS, 'blaad']);  this.game = gameS;  this.x = xS;  this.y = yS;  this.health = 40;  this.maxHealth = this.health;  this.worth = 400;  this.comboWorth = 12;  this.rotationSpeed = 15;  this.reverseRotation = false;  this.nextReverse = 0;  this.laserBeam = new Weapon.LaserBeam(this.game);   this.shields = this.game.add.group();  this.shields.create(new BlaadShield(this.game, this.x + 5, this.y - 5));  this.shields.create(new BlaadShield(this.game, this.x + 11, this.y - 2));  this.shields.create(new BlaadShield(this.game, this.x + 15, this.y + 5));  this.shields.create(new BlaadShield(this.game, this.x + 11, this.y + 11));  this.shields.create(new BlaadShield(this.game, this.x + 5, this.y + 14));  this.shields.create(new BlaadShield(this.game, this.x - 1, this.y + 11));  this.shields.create(new BlaadShield(this.game, this.x - 5, this.y + 5));  this.shields.create(new BlaadShield(this.game, this.x - 1, this.y - 2));   this.addChild(this.shields);   //this.idleAnimation = true; // Animation stuff doesn't work yet because you can't add animations to extended sprites. Gotta figure this out later.  //this.animations.add('standing', [0]);  //this.animations.add('idle', [1, 2, 3, 4, 5, 6, 7]);  //this.animations.add('damage', [8, 9, 10, 11]);  //this.animations.play('standing', 0, false);}Blaad.prototype = Object.create(Enemy.prototype);Blaad.prototype.constructor = Blaad;

This is a BlaadShield, its main line of defense, which you'll notice is generated 8 times in the previous constructor.

 

post-15035-0-62298900-1437133234.png

var BlaadShield = function(gameS, xS, yS) {  Enemy.apply(this, [gameS, xS, yS, 'blaadShield']);  this.game = gameS;  this.x = xS;  this.y = yS;  this.health = 10;  this.maxHealth = this.health;  this.minorEnemy = true;}BlaadShield.prototype = Object.create(Enemy.prototype);BlaadShield.prototype.constructor = BlaadShield;

So does the Blaad appear with its shields?

 

post-15035-0-13922900-1437133364.png

 

Nope. And this goes for any Sprite with a child in my game.

 

If I console.log(this) in the BlaadShield's constructor and look at the x/y values, they seem to be generating exactly as they should, at least up to that point. But if I try console.log(this.shields) after the creation of the shields group in the Blaad's constructor, I get something interesting. The shields DO seem to exist, so I check the X and Y values. The x value of any one of the shields is... "[object Object]NaN0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"

 

And the y value is... 0. Huh.

 

This is really important because so many enemies in my game rely on the parent/child structure for their particular gameplay gimmicks.

Link to comment
Share on other sites

Phaser.Group.create doesn't work the way you're using it there. It's trying to interpret the object you're passing as an x value.

 

What you probably want to do is set

 this.shields.classType = BlaadShield 

And use the appropriate parameters for create to make your shields.

 

Also, you should probably make your shields group with this.game.make rather than this.game.add, since add will automatically add the group to the game world, so when you attach it as a child later Phaser might end up counting it twice for various processes.

Link to comment
Share on other sites

Well I'll be damned, you're totally right. I had it all wrong. So I looked through Group again for its methods and restructured my code like this:
 

this.shields = this.game.make.group();this.shields.add(new BlaadShield(this.game, this.x + 5, this.y - 5));this.shields.add(new BlaadShield(this.game, this.x + 11, this.y - 2));this.shields.add(new BlaadShield(this.game, this.x + 15, this.y + 5));this.shields.add(new BlaadShield(this.game, this.x + 11, this.y + 11));this.shields.add(new BlaadShield(this.game, this.x + 5, this.y + 14));this.shields.add(new BlaadShield(this.game, this.x - 1, this.y + 11));this.shields.add(new BlaadShield(this.game, this.x - 5, this.y + 5));this.shields.add(new BlaadShield(this.game, this.x - 1, this.y - 2));  this.addChild(this.shields);

What that did was put the shields group in random locations around the map, and I wasn't colliding with them, which means they're not part of the enemies group like Blaad itself is. So I tried your idea instead:

this.shields = this.game.make.group();this.shields.classType = BlaadShield;this.shields.create(this.x, this.y - 12); // Tried multiple ways of passing in parameters to this function, with different results.

This one puts the child shield in an apparently random location, and while it does disappear if the Blaad is killed, it still isn't considered to be part of the enemies group. So close!

Link to comment
Share on other sites

Once the group becomes a child of your sprite all its children's positions are relative to the parent; i.e. if your Sprite is at (10, 10) and you set a child at (20, 30) it'll end up being at (30, 40) in world coordinates. Did you mean for the shields to be closer to the parent sprite?

 

Also, a bunch of stuff in Phaser around groups doesn't descend into grandchild groups. So if you collide the enemies group and it has a blaad in it the blaad's child group won't be checked; you have to check it manually, probably in the blaad's update method. But don't forget to call Sprite's update method first if you override it: "Phaser.Sprite.prototype.update.call(this);".

Link to comment
Share on other sites

Once the group becomes a child of your sprite all its children's positions are relative to the parent; i.e. if your Sprite is at (10, 10) and you set a child at (20, 30) it'll end up being at (30, 40) in world coordinates. Did you mean for the shields to be closer to the parent sprite?

 

Also, a bunch of stuff in Phaser around groups doesn't descend into grandchild groups. So if you collide the enemies group and it has a blaad in it the blaad's child group won't be checked; you have to check it manually, probably in the blaad's update method. But don't forget to call Sprite's update method first if you override it: "Phaser.Sprite.prototype.update.call(this);".

 

Ooooooh, you're totally right!! One sec while I see if all this works.

Link to comment
Share on other sites

So that's all brilliant, I just didn't realize that grandchild sprites weren't automatically counted as part of the same group, so that poses an interesting new problem. I wasn't expecting it to work this way at all, and I might've written myself into a corner because of it. Sucks for me. The way enemies are collided is that if the player or the player's weapon overlaps the "enemies" group, the collision is handled through a one-time method taking in both objects. I'm only using game.update to check for that overlap between the two groups. Overriding Blaad's death method to destroy its shields group as well is functional, but I (probably) can't get around the fact that the shields need to be in a self-contained group, because that's what allows them to all rotate around the enemy. Am I screwed? Should I restructure my collision once again?

Link to comment
Share on other sites

So you're not using a physics system?

 

If I'm not mistaken, using sprite.overlap vs. a Group might be problematic because the Group's getBounds function is based on the furthest children, so if you have 2 enemies on opposite sides of the screen in the same group, the entire space between them is considered a part of the group's bounds and will cause overlap to return true.

 

You can write a recursive function that walks the children of the children of a group, but be careful. As the documentation says, sprite.overlap is an expensive operation, if you do many checks often it might cause performance issues. I'd consider using one of the physics systems in Phaser to help you out with collisions.

Link to comment
Share on other sites

So you're not using a physics system?

 

If I'm not mistaken, using sprite.overlap vs. a Group might be problematic because the Group's getBounds function is based on the furthest children, so if you have 2 enemies on opposite sides of the screen in the same group, the entire space between them is considered a part of the group's bounds and will cause overlap to return true.

 

What's going on is, before I switch over to p2 to hopefully use or write an equivalent function, I'm using Arcade's collide and overlap functions in the level gamestate's update loop.

if (player.alive) {         game.physics.arcade.collide(player, shotPowerupGroup, this.collectShotPowerup, null, this);         game.physics.arcade.collide(player, burstPowerupGroup, this.collectBurstPowerup, null, this);         game.physics.arcade.overlap(player, weaponPowerups, this.collectWeaponPowerup, null, this);         game.physics.arcade.overlap(player, enemies, this.collideEnemy);         game.physics.arcade.overlap(player, enemies.weapon, this.collideEnemyWeapon);         game.physics.arcade.collide(player, this.layerCollision, function(){player.damage()});         game.physics.arcade.overlap(player.weapon, this.layerCollision, this.killBullet, null, this);         game.physics.arcade.overlap(player.weapon, enemies, this.collideBullets, null, this);         game.physics.arcade.collide(player.previousWeapon, this.layerCollision, this.killBullet, null, this);         game.physics.arcade.overlap(player.previousWeapon, enemies, this.collideBullets, null, this);     }

Now, with these functions, the space between entities in a group doesn't count, so this works as far as testing goes. But yeah, now that I know what I do about groups, this may turn into a headache.

Link to comment
Share on other sites

OK, I just realized something crucial.

 

My player's "weapon" is a group of bullets.

Weapon.SingleBullet = function(game, ownerRef) {  Phaser.Group.call(this, game, game.world, 'Single Bullet', false, true, Phaser.Physics.ARCADE);  this.ownerRef = ownerRef;  etc. code}

But some Weapons are parents to other weapons which are, themselves, groups of bullets.

Weapon.Barrage = function (game, ownerRef) {  Phaser.Group.call(this, game, game.world, 'BARRAGE', false, true, Phaser.Physics.ARCADE);  this.ownerRef = ownerRef;  this.weapon1 = new Weapon.BarrageBeam(game, this.ownerRef);  this.addChild(this.weapon1);  this.weapon2 = new Weapon.BarrageQuint(game, this.ownerRef);  this.addChild(this.weapon2);  this.weapon3 = new Weapon.BarrageHomer(game, this.ownerRef);  this.addChild(this.weapon3);};

And they work. The bullets of these weapons still collide with enemies.

 

So why can't the same apply to enemies? Is it because I'm adding a group as a child instead of individual enemies? Would enemies outside of a group work?

Link to comment
Share on other sites

Whoops, yeah, sorry.

 

So, above, you can see a listing of all the collision groups I'm using and how I'm checking them in the update call. Enemies is a group. Player is not a group, it's a stand-alone sprite, but it does contain a group, which is player.weapon. This is a system created by Rich in this shooter example: http://jsbin.com/pinone/1/edit?js,output

 

Likewise, each sprite in the enemy group also has a .weapon that's checked against the player.

 

But some player weapons are special, and work like the "combo" weapons in that example. These weapons are not groups of Bullets, but instead are groups of two or more groups that contain Bullets.

 

So here's what's happening that's peculiar: The Back Attack is the player.weapon being checked in this line.

game.physics.arcade.overlap(player.weapon, enemies, this.collideBullets, null, this);

Here's what the Back Attack looks like.

Weapon.BackAttack = function (game, ownerRef) {  Phaser.Group.call(this, game, game.world, 'BACK ATTACK', false, true, Phaser.Physics.ARCADE);  this.ownerRef = ownerRef;  this.weapon1 = new Weapon.BackAttackFront(game, this.ownerRef);  this.addChild(this.weapon1);  this.weapon2 = new Weapon.BackAttackBack(game, this.ownerRef);  this.addChild(this.weapon2);}Weapon.BackAttack.prototype = Object.create(Phaser.Group.prototype);Weapon.BackAttack.prototype.constructor = Weapon.BackAttack;Weapon.BackAttack.prototype.updateFireRate = function(powerups) {  this.weapon1.fireRate = this.weapon1.baseFireRate - (8 * powerups);  this.weapon2.fireRate = this.weapon2.baseFireRate - (8 * powerups);}Weapon.BackAttack.prototype.fire = function (source) {  this.weapon1.fire(source);  this.weapon2.fire(source);};

So, to recap: player.weapon, a group, is being checked against all entities in the enemies group. When player.weapon is actually a group containing two more groups and a bullet object from one of those groups hits an enemy, it actually works. (although not against layerCollision, for some reason...)

 

Since this is true, shouldn't a bullet be able to hit an enemy that's a child of another enemy?

Link to comment
Share on other sites

¯\_(ツ)_/¯ I got nothin'. Are you absolutely sure the bullets belong to the "BackAttackFront" group and not the "BackAttack" group? Only reason I ask is the weapons look like they're getting the parent of BackAttack as a ctor param?

Link to comment
Share on other sites

See for yourself. ownerRef is just used for various checks.

Weapon.BackAttackFront = function (game, ownerRef) {  Phaser.Group.call(this, game, game.world, 'BACK ATTACK FRONT', false, true, Phaser.Physics.P2JS);  this.ownerRef = ownerRef;  clearWindows();  //document.getElementById("back-attack").style.display = "inline";  this.bulletSpeed = 700;  this.baseFireRate = 140;  this.fireRate = this.baseFireRate;  this.power = 1;  for (var i = 0; i < 64; i++)  {      this.add(new Bullet(game, 'playerBullet', 1), true);  }  return this;};

Compare to, say, SingleBullet, which does not use nested groups.

Weapon.SingleBullet = function(game, ownerRef) {  Phaser.Group.call(this, game, game.world, 'Single Bullet', false, true, Phaser.Physics.P2JS);  this.ownerRef = ownerRef;  clearWindows();  document.getElementById("powerup").innerHTML = " ";  this.bulletSpeed = 700;  this.baseFireRate = 140;  this.fireRate = this.baseFireRate;  this.setAll('tracking', true);  for (var i = 0; i < 64; i++) {    var bullet = this.add(new Bullet(game, 'playerBullet', 1), true);  }  return this;}

The fire functions for the bullets are almost exactly as they are in the example linked in the previous post.

Link to comment
Share on other sites

Mostly I got nothin', still. There's some weird stuff that I don't know if it matters, like calling "addChild" on weapon1 and weapon2 instead of "add", and how your BackAttackFront group is using P2JS, while its parent is using Arcade?

 

Again, I don't know why the end result of things that might not matter would be grandchildren getting physics checks, so I'm stumped.

Link to comment
Share on other sites

Well hopefully it's legit to use them this way. I will say, the Arcade/P2 thing doesn't actually matter. They were all Arcade before and they're all P2JS now. I'm just in the middle of switching everything over. Sorry for the confusion there.

 

My theory is, it has something to do with the fact that the weapon's weapon is a group of a group, while the enemy sprite's minions are a group of a sprite, and it matters in that function whether you compare a sprite's group's group to a group of sprites, or a sprite's group's group to a group's sprite's group.

 

Is that the most insane thing I've ever typed? Probably.

Link to comment
Share on other sites

 Share

  • Recently Browsing   0 members

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