Hello,
I’m creating a custom script for a classic pressure plate (i’m not using a joint because its pretty uncontrollable), where the player can stack an arbitrary number of rigidbodies to reach a weight threshold, where the plate activates.
I initially used a simple OnCollisionEnter/Exit approach to keep track on accumulated weight, and i worked for cases like this (note the red cube its the pressure plate, and the white cubes are weights):
But the moment the objects stack, the plate stops triggering because it only detects the weights contacting directly:
I have been searching the docs and different approaches, and all include some sort of raycasting (which I don’t like, because it would detect an object passing over the plate, and tends to be imprecise).
I thought that maybe there is a way of, inside a rigidbody, detecting the added weight other rigidbodies apply, but I can not find anything. I tried GetAccumulatedForce, but seems to ignore other bodies.
None of that information is calculated by the physics system. Each collider is simply having its contacts solved by applying impulses to ensure there’s no overlap.
For the above, you’d have to collate the objects into a contact island (things in contact with the plate and things they too are in contact with) and calculate the total mass in contact yourself.
I’m not a 3D physics dev but there are various contact consumption methods available beyond the physics callbacks such as subscribing to the ContactEvent. You could conceivably subscribe to this and collate contact islands for these pressure plates.
Good to know, but, won’t it be a little too overkill to listen to all contacts happening on the scene just to discriminate a few ones? Our game has a lot of physics going on, and I fear this could be called too much.
And, there is no possible way of, like, having a rigidbody, get the current objects colliding with it? This way, I could go over the contact island recursively without having to listen to all contact events in a rather big scene.
I see Collision (from OnCollisionEnter) has GetContacts, but I fear I can only reach the first object this way
Look at the docs, it provides you with a NativeArray and the code example shows you how to process the data in a job. I don’t see why this couldn’t be made fast. For sure, it might not be great though if you have a lot to listen to but I don’t see any other alternative to producing such a data-structure of connected colliders TBH.
It’d certainly be faster than listening to all the collision callbacks and then trying to collate it all separately.
One other thing that just comes to mind is: Unity - Scripting API: ModifiableContactPair
These you can subscribe to individually per collider I believe.
you can also select which colliders report to the ContactEvents too: Unity - Scripting API: Collider.providesContacts
Ok, thanks for all the info. Im unfamiliar with Unity’s physics API, so Im grasping things slowly.
I did a quick proto, and I think Im getting somewhere.
Im registering now all objects colliding with the plate and already contacted objects, effectively creating the contact islands (I presume I should put it on a job, like the example, but for testing purposes it works).
However, I realized there is no way on distinguishing between Entering contacts and Exiting contacts, so I cannot reduce accumulated weight when exiting contact (documentation on this is scarce).
void Physics_ContactEvent(PhysicsScene scene, NativeArray<ContactPairHeader>.ReadOnly pairHeaders)
{
foreach (ContactPairHeader contact in pairHeaders)
{
// Check if object contacts directly with plate
if (contact.OtherBody && contact.OtherBody.gameObject == this.gameObject)
{
m_ContactedObjects.Add(contact.OtherBody);
}
// If not, check if it contacts with an object already in the isle
else if (m_ContactedObjects.Contains(contact.Body) && !m_ContactedObjects.Contains(contact.OtherBody))
{
m_ContactedObjects.Add(contact.OtherBody);
}
}
// TODO: How to discriminate between Enter and Exit events?
}
private void FixedUpdate()
{
// Compute weight of all contacted events
float totalWeight = 0.0f;
foreach (Component component in m_ContactedObjects)
{
Rigidbody rb = component.GetComponent<Rigidbody>();
totalWeight += rb.mass;
}
Debug.Log(totalWeight);
}
Also, I’m not sure if this is the best way to check if the colliding objects are the correct ones.