Detecting separation and collision of two bodies.
Closed this issue · 2 comments
Hi,
I want to have two events:
// called when two bodies truely separated
void OnBodySeparation(Body body1, Body body2, ...);
// called when two bodies collided after they are truely separated
void OnBodyCollision(Body body1, Body body2, ...);Something similar to what i want to have is done in this sample. In that sample they just keep a count for each body pair and when it reaches zero it is concluded that they are separated. That sample doesnt worry about bodies going to sleep. OnContactRemoved callback is also called when a body goes to sleep. Imagine this scenario:
there is a plane (ground) and there is a cube above it. And cube drops and stays on the ground. If we run this scenario in jolt OnContactRemoved is called when the cube goes to sleep. In this case I dont want to call OnBodySeparation event because they are not truely separated. And when we wake up the cube OnContactAdded is called. In this case I also dont want to call OnBodyCollision event because this is not a new collision, they were already colliding. I solved this via storing the state of the body pairs and I will tell you how it works in a second but I think what I have done is pretty cumbersome and I felt like I was using the library wrong. I feel like it shouldnt be this complicated to detect separation and collision without worrying about contacts being added/removed due to sleep. What I did to solve this:
// i store the bodies that currently have contact
Dictionary<Pair<Body>, ContactData> contacts;
// OnContactAdded and OnContactRemoved callback writes to this dictionaries
// after the PhysicsSystem.Update() i process them and write contacts dictionary above if necessary
Dictionary<Pair<Body>, ContactData> contactsAddedThisPhysicsUpdate;
Dictionary<Pair<Body>, ContactsRemovedThisPhysicsUpdateData> contactsRemovedThisPhysicsUpdate;
...
physicsSystem.OnContactAdded += (...) => {
contactsAddedThisPhysicsUpdate.AddNewOrUpdateExisting(...);
};
physicsSystem.OnContactRemoved += (...) => {
contactsRemovedThisPhysicsUpdate.AddNewOrUpdateExisting(...);
};
...
// inside the main loop
physicsSystem.Update(...);
// when removing contacts check if body is deactivaed if so do not remove the contact
foreach (removedContact in contactsRemovedThisPhysicsUpdate) {
// retrieve the lock and body ids
...
bool isBody1Active = lockRead1.Succeeded && lockRead1.Body.IsActive;
bool isBody2Active = lockRead2.Succeeded && lockRead2.Body.IsActive;
...
if (isBody1Active || isBody2Active) {
// one of the bodies is active, assume OnContactRemoved was not called due to sleep and process the removal
...
if (contactData.manifolds.Count == 0) {
// this was the last contact between these two bodies. so call separation callback
CallOnBodySeparation(...);
...
}
}
}
contactsRemovedThisPhysicsUpdate.Clear();
// check if the so called added contact is already in the contacts. if so ignore it (it will be called as persisted)
// if not add it and call the OnBodyCollision callback
foreach (addedContact in contactsAddedThisPhysicsUpdate) {
...
if (contacts.TryAdd(entities, addedContactData)) {
// certified new contact
CallOnBodyCollision(...);
} else {
// this entities are already contacting. filter the subshapes that are already in the contact data
// and add the new ones to the existing contact data
...
}
}
scene.physicsContactState.contactsAddedThisPhysicsUpdate.Clear();
foreach (contact in contacts) {
// do whatever with contact
// irrelevant for this discussion
...
}My question is, is there a simpler way to detect separation and the first collision between two bodies? In the docs of OnContactAdded it mentions that I can use Body.IsActive() so this works to detect separation but what about the first collision? So I still think that I need to store some state about body pairs so that I can know if it is the first collision and this leads me to think that there should be easier way and probably i am using library wrong.
For example consider the cube ground scenario above. And after cube went to sleep i throw a sphere to the cube and when the cube wakes up there are two OnContactAdded callbacks called: one for cube-sphere, the other for cube-ground. In this case cube-sphere collision I would consider as first collision but not the cube-ground since they were already in contact. So simply ignoring OnContactAdded if the body just woke up is not a solution.
I've updated the contact listener test to illustrate what I think is the simplest way:
It does involve keeping track of a list of sleeping bodies that are in contact.
Note that there's one situation that I'm not covering in this example: When you remove a body from the world, you would need to delete any entries from the mSleepingContacts list involving the body you're removing.
hmm i see. you only keep track of the sleeping contacts instead of all the contacts like i did. yea thats way simpler actually. but still what i understand is we still need to store some state in order to be able to tell if it is the first collision between two bodies, there is no builtin way to get that info. thank you for taking your time and answering. i appreciate it.