[up]Event handling, collision detection precision?

edited November 2013 in Help request
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

  • edited October 2013
    Hi Knolan,

    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?
  • edited October 2013
    Thanks for the reply.

    I looked at the Main settings at the wiki after what you said and added this to my ini:
    AllowSleep = false
    IterationsPerStep = 30
    

    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.
  • edited October 2013
    Nice one, excellent find.
  • edited October 2013
    Knolan wrote:
    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?

    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.
  • jimjim
    edited October 2013
    As I can see from your video, your arrows have have much speed, in this case its better that you use HighSpeed = true in your config for your Arrows, its there for high speed objects like arrows or bullets.

    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.
  • edited October 2013
    I can try those but...

    Which config does the HighSpeed option belong to? Body or parts?
    How do I check if the event is a collision Add or End?
  • jimjim
    edited October 2013
    Which config does the HighSpeed option belong to? Body or parts?
    It's property of a Body.
    How do I check if the event is a collision Add or End?
    In your case, it should be
    if(event->eID == orxPHYSICS_EVENT_CONTACT_ADD)
    {
    // your code here
    }
    
    You should check physics tutorial of orx for further details
  • edited October 2013
    If you're not sure what config an items belongs to or if you're otterly lost, you can refer to the config system guide on the wiki which starts here:

    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
  • edited October 2013
    Thanks for the links, I was really looking for that second one.

    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.
  • edited November 2013
    I added a Spawner to the arrow and the problem started again :(

    The collision happens and creates no event... I tried adding the contact end to the code:
        if(event->eType == orxEVENT_TYPE_PHYSICS && (event->eID == orxPHYSICS_EVENT_CONTACT_ADD || event->eID == orxPHYSICS_EVENT_CONTACT_REMOVE)){
            physicsContactHandler(event);
        }
    

    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?
  • edited November 2013
    Here's some current code of mine which looks for action against a spawned particle and a regular. Main difference is I never call against orxPHYSICS_EVENT_CONTACT_REMOVE.
    orxSTATUS orxFASTCALL ProcessPhysicsEvents(const orxEVENT *_pstEvent){
    	
     if (_pstEvent->eType == orxEVENT_TYPE_PHYSICS){
       if(_pstEvent->eID == orxPHYSICS_EVENT_CONTACT_ADD){
        orxOBJECT *pstObject1, *pstObject2;
        
        /* Gets colliding objects */
        pstObject1 = orxOBJECT(_pstEvent->hRecipient);
        pstObject2 = orxOBJECT(_pstEvent->hSender);
    		
        if (orxString_Compare(orxObject_GetName(pstObject1), "Alien") == 0
        &&
        orxString_Compare(orxObject_GetName(pstObject2), "ParticleObject") == 0 
          ){
             ExplodeAlien(pstObject1);
    		
          } 
    
  • edited November 2013
    Thanks for the reply.

    I added the CONTACT_REMOVE to check if that wasn't the problem.
    Nevermind, I found the error, it was really stupid :blush:

    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.
        else if (strcmp(orxObject_GetName(object2), "Arrow") == 0){
    

    Fixed to this:
        else if (orxString_NCompare(orxObject_GetName(object2), "Arrow", 5) == 0)
    

    All works now, seriously, I need to either stop programming till 3 a. m. or stop my 2 coffees/day policy :laugh:
  • edited November 2013
    As Jim already advised, I'd go with Scroll, this way you won't encounter this problem anymore! :)
    class Arrow : public ScrollObject
    {
      virtual orxBOOL OnCollide(ScrollObject *_poCollider, const orxSTRING _zPartName, const orxVECTOR &_rvPosition, const orxVECTOR &_rvNormal);
    };
    
  • edited November 2013
    Maybe next time, the project deadline is december 13, so I am kinda in a hurry to get it all working.
    Also I have far more experience with C, the chances that I will screw up something in C is way smaller than C++.
Sign In or Register to comment.