Corona Collision No-No
While working on our latest project (Biffy directing this one), I ran into what seemed like a bug with collisions in Corona. I hadn’t got very far into the project yet, so the collisions were still pretty basic.
After looking over my code to see if it could possibly be my fault (and not a bug), I didn’t find anything and decided to submit a bug report to the Ansca Bugs team.
Here’s a closer look at what was going on…
The code I was working on involved shooting bullets at moving enemy targets. The crashes seemed to occur pretty randomly, which is why I thought it was a Corona bug. It didn’t occur on every collision though, and never after a specific number of collisions, so identifying it was a little frustrating.
After exchanging some emails with Ansca engineers, and doing more investigating on my own, it turns out it wasn’t a bug within Corona, but a limitation of the Box2D physics engine itself.
Here’s what my code looked like (that produced the crash):
local bulletCollision = function( self, event ) if event.phase == "began" then self.isBodyActive = false self.isVisible = false event.other.isBodyActive = false event.other.isVisible = false event.other.lives = event.other.lives - 1 end end
Looks pretty basic right?
Well, as it turns out, Box2D will crash if you try to change the properties of an object that is involved in a collision at the exact time of the collision. Due to variances in collisions (especially when it comes to shooting bullets), sometimes it would result in a crash, and sometimes it wouldn’t—the timing had to be just right.
Surely you’re going to run into a time when objects need to be modified, moved around, etc. upon collisions, so how do you get around this Box2D limitation?
It’s actually pretty simple. You wait, and then execute your code.
The workaround seemed to fix the issue as it worked flawlessly every time (even after turning up the amount of objects to produce an overwhelming amount of simultaneous collisions):
local bulletCollision = function( self, event ) if event.phase == "began" then local doCollision = function() self.isBodyActive = false self.isVisible = false event.other.isBodyActive = false event.other.lives = event.other.lives - 1 end local collisionTimer = timer.performWithDelay( 1, doCollision, 1 ) end end
The only thing I did was “wrap” the existing code into a local function, and then call a timer with a 1 millisecond delay to execute the function. Like I said, it worked flawlessly every time.
I even tried a timer with a 0 millisecond delay with success, but I didn’t do extensive testing with that so I recommend sticking with 1. There’s not much time difference there anyway.
Admittedly, the whole thing seems a little weird, and Ansca engineers said they’re working on a way to incorporate something like that automatically into Corona’s backend so it’s never an issue.
Until then, this workaround is simple enough to implement and you should be using it with all of your collisions where you need to change any attribute of the objects involved—especially x/y values, I noticed changing those produced the crash nearly every time.
Needless to say, it was a little bit of a headache trying to figure out what was going on, and figuring out how to make things work, so hopefully this will save you some headache in the near future :-)