Hi there I am having quite some trouble with the collision detection lately.
In my game you must move and shoot arrows, the arrows will fly until they hit a monster or the ground.
Upon hitting a monster it will suffer a knockback for some time and then resume walking. A monster should die if hit twice.
After hit, I change the arrow check flags to ground only, which means it should hit only the ground. The flags are:
Arrow = 1
Ground = 2
Character = 4
Monster = 8
I use an event handler to check if the monster was hit, here is how I register it:
orxEvent_AddHandler(orxEVENT_TYPE_PHYSICS, EventHandler);
And the code is:
orxSTATUS orxFASTCALL physicsContactHandler(const orxEVENT* event){
orxOBJECT *object1, *object2;
object1 = orxOBJECT(event->hRecipient);
object2 = orxOBJECT(event->hSender);
if (orxString_NCompare(orxObject_GetName(object1), "Arrow", 5) == 0){
orxLOG("Arrow [%d] is the recipient. Hit %s [%d]", debugGetCheckFlags(object1), orxObject_GetName(object2),
debugGetCheckFlags(object2));
arrowsCollide(object1);
if (orxString_NCompare(orxObject_GetName(object2), "Monster", 7) == 0){
monsterDamage(object2, object1);
}
}
else if (strcmp(orxObject_GetName(object2), "Arrow") == 0){
arrowsCollide(object2);
orxLOG("Arrow is the sender. Hits [%s]", orxObject_GetName(object1));
if (orxString_NCompare(orxObject_GetName(object1), "Monster", 7) == 0){
monsterDamage(object1, object2);
}
}
return orxSTATUS_SUCCESS;
My first problem was that sometimes the monsters were "immortal", I would hit them many times and they would not die. I realised that sometimes the arrow is event recipient (most of the times) and sometimes it is the sender (very few times, but caused the immortality).
I fixed this bug by duplicating the arrow collide/monster hit code.
But there is still a problem that sometimes the monster dies with a single shot. Here is a video showing it happening:
https://docs.google.com/file/d/0B0UdeUM9pB-nSWZiSUJwMWU4UG8/edit?usp=sharing
Edit: You can see it happen the first time at the far right in 0:08 and again pretty easy to spot at 00:16.
Investigating the log I found this:
[20:43:36] [LOG] Arrow [10] is the recipient. Hit Monster_Normal [7]
[20:43:36] [LOG] Monster hit 3.000000!
[20:43:36] [LOG] Arrow [2] is the recipient. Hit Monster_Normal [7]
[20:43:36] [LOG] Monster hit -4.000000!
Which is very strange. The log show the monster being hit twice by the same arrow, but at the second time, the arrow flags - the number [2] - has changed and should not hit the monster.
So finally my questions:
1) How did this event trigger? Is it possible that the event triggered before the change of the flags? In this case, can I increase the precision or should I check the flags myself?
2) How is it possible that the arrow is the sender sometimes and the receiver other times? Should it ever happen?
Thanks in advance and sorry for the long post.
Comments
A couple of things which I hope helps... sometimes one object can be the sender or receiver, depending which is picked up first I suppose. It is something I have to cater for as well, so you're not alone, but I just check for both.
Have you got high speed collisions turned on in the config?
Perhaps when you switch collision flags on the arrow in the arrowCollide function, is the second collision event already queued to fire?
I looked at the Main settings at the wiki after what you said and added this to my ini:
Worked great for me, also arrows has always been the recipient, I left the code in case it thinks it is the sender.
EDIT:
Turning on the allowSleep makes the arrow be the sender sometimes.... still no more immortal monsters or one shot kills.
There isn't much I can add to sausage's answer, I'm afraid.
1) Yes, I guess it's possible. However I'm not familiar enough with the internals of Box2D to pinpoint why/how. I do know that I'm already doing a first pass of filtering within orx to prevent cases where collisions between the same two bodies could be added *and* removed within the same frame (and sometimes more than once).
Bumping the iteration number should help, but you might have to do some extra filtering in the collision event yourself.
Another option would be to actually remove the part and add a new one with the new flags/masks as it is more likely to remove the pending collision if any. The step further would be to remove the body itself and re-add a new one, in this case you'd be sure there wouldn't be any residual collisions left.
2) The order isn't guaranteed. Box2D refers as Body A & Body B and I'm afraid you'll have to test both options every time. I could do some sorting tricks on orx side (like using their name for alphabetical sorting, or something similar), but that would still be pretty rickety as changing the name (or the other parameter used behind the scene for ordering) would then result in a change of behavior with no apparent reason.
My guess based on your observations (and it might be completely wrong), is that, by getting asleep, bodies are probably transferred from one list to another resulting in a different order, resulting in a different sequence of Body A/Body B for new collisions.
Increasing iteration step increases precision of physics simulation, which also suggests using HighSpeed for your arrows could make same impact as increasing iteration step.
Are you checking for Collision Add ? In you event handler, you might get two event trigger one for ADD and another for END.
Afaik, there is no receiver or sender in box2d collision event, so we have to check for both. If I had to do it, I would probably go Scroll way. I don't need to know which is sender or receiver in this case. Inside OnCollide function I would handle arrow flags and monster collision.
Which config does the HighSpeed option belong to? Body or parts?
How do I check if the event is a collision Add or End?
In your case, it should be You should check physics tutorial of orx for further details
http://orx-project.org/wiki/en/orx/config/main
And then into the structure settings:
http://orx-project.org/wiki/en/orx/config/settings_structure/main
Specifically in your case the orxbody structure config settings:
http://orx-project.org/wiki/en/orx/config/settings_structure/orxbody
Tried some tests:
IterationSteps 30 / HighSpeed = false -> works.
IterationSteps 10 / HighSpeed = true -> works.
IterationSteps 10 / HighSpeed = false -> doesn't work.
Then I tested if the eID would change, but it didn't.
Well, I guess I will leave both IterationSteps 30 and HighSpeed. It is not like the performance is a problem.
The collision happens and creates no event... I tried adding the contact end to the code:
I also tried increasing the iterations per step to a really high number (120) but still happened. I also tried setting the monster objects as highspeed bodies, no luck.
With a normal arrow with no spawner the problem doesn't happen.
Anything I could be missing?
I added the CONTACT_REMOVE to check if that wasn't the problem.
Nevermind, I found the error, it was really stupid
All my arrows are called Arrow<X>, in the case of the the ones with particles ArrowIce and ArrowFire.
This code was being used when the arrow was the sender (which is pretty rare, for some reason.
Fixed to this:
All works now, seriously, I need to either stop programming till 3 a. m. or stop my 2 coffees/day policy :laugh:
Also I have far more experience with C, the chances that I will screw up something in C is way smaller than C++.