Hey.
So, while working on Brickout, I was adding a feature that allows the bricks to descend one level after clearing so many of them, much like Atari’s Super Breakout.
Everything was working fine, until I encountered a problem. Eventually, the collision between the ball and bricks wasn’t working. The ball was going right through some of the bricks.
Using Phaser CE 2.10.5, so I double-checked what I was doing when I clear bricks. All the bricks are in a group, and that group is being tested for collision against the a group of balls like this:
arcade.collide(m_ballsGroup, gameMap.bricksGroup, onBallBrickCollide, null, this);
This phenomenon was strange because, all the ball sprite has a physics body on it (using Arcade), as well as every brick sprite, so I wasn’t sure why the ball would collide with some and not others.
I turned on debugging, so I could see the physics bodies, because it looked like somehow, some brick sprites were missing their bodies. And, as you can see here, all the brick sprites have bodies, and the ball was going right through them! Argh!
Ok, I was pretty sure I was doing something simple (and dumb) here, but I needed to jump a bit out of my comfort zone, and take some peeks into the Phaser source code. Since it seems to be a collision issue, I started with that collide call earlier, and eventually found my way into the Phaser.Physics.Arcade.collideSpriteVsGroup function.
Most of the function was just checking bounds checking, which I knew was not what I was looking for. However, there was a preliminary check against for validation for the sprite objects.
... // Skip duff entries - we can't check a non-existent sprite or one with no body if (!object1 || !object1.exists || !object1.body) { continue; } ...
This looked interesting. Since I have the full source of Phaser.js, not just the minified, I set a breakpoint on that continue
statement to see if the code would ever execute that line. And sure enough, it did! Hmm! Ok, so I’m not writing any code that makes the bricks or ball sprites not exist, and they all “should” have a physics body on them (note that “should”). So, I added additional checks to see which of those three conditions was true. The object wasn’t evaluating to false, and it did exist, but it didn’t have a body on it (the body property was null). I did wrote some additional code to make sure that the object was either the ball sprite, or one of the brick sprites, and it was always one of my brick sprites. So, somehow, some brick sprites were losing their physics body. Dude. WTF!?
Going back to my game code, the only place I’m destroying a physics body on a sprite is when I’m removing it after being hit by a ball. Destroying a physics body does remove the body from the sprite – _however_, I’m also removing the object from the group altogether. So it should no longer be tested for collisions, right? Riiight??
WRONG!
It took me some time – in fact, it was late night, and decided to call it a night and return to this the following morning – but I did finally figure out WHAT THE HELL’S GOIN ON HEA!
When I removed the brick sprite from the bricks group, I wasn’t actually removing it. I was calling Phaser.Group.removeChild
, when I should’ve used Phaser.Group.remove
.
That validation check above is enclosed in a for loop that iterates an array of all the objects. But the array isn’t the group’s “normal” array – it’s a separate array inside the group called a hash. The hash is used for z-sorting purposes, by the Phaser Arcade physics, but it’s also used in this collision checking.
... for (var i = 0; i < group.hash.length; i++) { var object1 = group.hash[i]; // Skip duff entries - we can't check a non-existent sprite or one with no body if (!object1 || !object1.exists || !object1.body) { continue; } ...
And when I called Phaser.Group.removeChild
, that didn’t remove the object from the hash. It only removes the object from the group. (Phaser.Group
inherits from PIXI.DisplayObjectContainer
, and it’s PIXI.DisplayObjectContainer.removeChild
that was being called, which has nothing to do with hashes.) This is how sprites with no bodies were being checked for collisions when they shouldn’t have. They were never being removed.
However, Phaser.Group.remove
does remove the object not only from the group but the has as well. So, once I changed my code to use remove instead of removeChild, everything worked as expected.
Geez. See I told you. It was something simple (and dumb) I was doing. And I learned a little more about how Phaser works too. So win, win!
– C. out.