CollisionHandlerEvent leads to jerky collisions?

This is a tough one to describe, but I will try :slight_smile:

CollisionHandlerEvent gives me really jerky collisions. If I run straight into a wall (not at an angle), I expect my model to stop and stay still. Instead it jerks in and out of the wall. If I walk into the wall at an angle, I expect to slide smoothly along it. Instead, the character moves as if the wall had a saw-tooth shape to it.

I have a task which handles keyboard movement, and a collision event handler which moves the model out of the wall. Is it possible that the collision event handler is called first, before the keyboard task moves the character into the wall?

For what it is worth, if I use a CollisionHandlerQueue and do the collision handling in the movement task after the keyboard movement, it is smooth. I would prefer to use CollisionHandlerEvent though…

Yes, its perfectly possible. You can check by typing in ‘taskMgr’ during runtime. You should see your keyboard reading task on one line, and the collisionLoop on another line.

Notice the priority number on the far right of each line? Those define the task ordering. To put your task in a different position:


myTask=taskMgr.add(self.theTask,"the task", priority=25)

This way you can ensure the calling order of functions.

That didn’t seem to fix it, sadly. It may have even made it worse. I’m not sure whether high or low numbers are high priority. It is this by default:

taskList                            dt(ms)       avg       max  priority
-------------------------------------------------------------------------
mayfly control_task                   ----      ----      ----         0
collisionLoop                         ----      ----      ----        30

I changed the control task to be priority 40

Also, I notice that CollisionHandlerEvent is double-calling my event. If I start with my character inside a wall, my handler function gets called twice.

If I use CollisionHandlerQueue, then the collision is only handled once.

zovril: I think you have figured out the problem in your first post. The collision needs to be handled after the key movement. Even the tutorial had a problem with this sort fo anomaly at one time:

https://discourse.panda3d.org/viewtopic.php?t=536

The bottom of the post gives the answer to their problem. Your particular situation may be a little different, but if you can straighten up the collisions after you have tried to move, things may look a little smoother.

Yes. The trick is, how do I control when collision handling is done while using CollisionHandlerEvent?

So there are three patterns for CollisionHandlerEvent:

InPattern
OutPattern
AgainPattern

You will have to figure out when you should accept some patterns while ignoring others. You will be constantly switching which patterns are accepted while you are trying to run into the wall. It will take careful planning but go somewhere along the lines of:

“When the Out pattern is thrown, ignore the Again pattern and do method C. When the Again pattern is thrown accept the Out pattern, ignore the In pattern and do method B…” (You will have to figure out when to ignore/accept which). Some methods may do similar operations.

It will be more code than just “accept In pattern and move out of wall” but it may allow the event handler to do what you want.

First of all, I assume that you aren’t using the built in CollisionHandlerPusher (which incidentally throws events as well, since its a child of CollisionHandlerEvent)

So lets run through the steps of what’s going on here:

You have some keyboard events that move your character

When you press the keys, it renders you inside the bounding volume, then pushes you out the next frame.

Anywho, lets chart this out based on the taskManager priorities

dataLoop -> -50
eventManager -> 0
yourLoop -> 0
collisionLoop -> 30
igLoop -> 50

These are the major players here.

Now, lets run through case, where the “w” key moves you into the wall

{Pressing w}

event “w” is thrown, to be picked up by eventManager

dataLoop is the lowest priority task on the list above, so it gets called first
(note: task with the same priority are called in the order they were added). The dataLoop sees that the “w” key was pressed, and so it throws the event “w”

eventManager is called sometime after that. It receives the event, then routes it to one of your functions that sets the state of your avatar (in this case, “walk forward”)

now yourLoop is called, which will move the dude forward into the wall.

Moving on, we come to the collisionLoop. It sees the dude colliding with the wall, and sends and event “dude-hit-wall” or something.

Finally we come to igLoop, which will render the scene. Since we’ve allready gone through the eventManager task this frame, the “dude-hit-wall” event won’t be processed until the next frame

So what can you do about this?

  1. One thing to try, though it could have wierd results:

 def finalCheck(self,task):
       #tell the event manager to do another event pass.
       base.eventMgr.doEvents()
       #add the new frame event to the end, in case anything comes after
       base.eventMgr.eventQueue.queueEvent(Event("NewFrame"))
       return Task.cont

#in init
taskMgr.add(self.finalCheck,"final Check",priority=35)
 Again, this may have unwanted side effects, but it will ensure that the events get processed.
  1. Another way that might work:

#init
self.lastPos=self.object.getPos(render)
self.newPos=self.object.getPos(render)
self.hitSomething=False

taskMgr.add(self.moveTask,"movement Task",priority=10)
taskMgr.add(self.afterCollision,"after collision Task",priority=35)


def moveTask(self,task):
     #first check to see if we didn't hit anything
     if(self.hitSomething):
         #keep in last position
         pass
     else: 
         #the new position is good, move it there. 
         self.object.setPos(self.newPos)
         #store away the last position
         self.lastPos=self.object.getPos(render)
         
     #move to new position
     #
     #

     #store away the new position as well
     self.newPos=self.object.getPos(render)

     self.hitSomething=False
     return Task.cont

def afterCollision(self,task):
    #reset the position to the last known "good" position
    self.object.setPos(self.lastPos)
    
#your accepts point to functions like this
def processCollision(self,cevent):
    self.hitSomething=True

Ah yes, that is the problem. The collision events get queued up instead of being processed right away. For now I guess I’ll stick with doing the collision loop myself, since I want collisions to be resolved immediately.