how to create a collision event between two rigidbodies that *aren't* touching

expansion/continuation from this question : is there a way to connect two rigidbodies, as if by collision, without them actually coming into contact? a way to manually create a contact/collision event and inject it into the physics simulation?

in my specific case, i have a number of moving objects that i need my character to be able to stand on and be moved by. my character is a dynamic rigidbody being moved by forces, but it does not ever touch the ground; it uses a number of different casts (ray, sphere, and rigidbody) to find out where the ground is, and be pushed away from it. so no contact is ever made.

is there a way to use this RaycastHit info to generate a collision event that can be manually inserted into the physics simulation so that my character can be carried by the objects it is standing on (assuming they are all kinematic rigidbodies being moved by MovePosition)?

Being all kinematic rigidbodies it’s up to you to implement an entire collision detection and response system. Physics may notify you on collisions, but won’t do anything to them. If your character doesn’t even touches these objects, then you won’t get these notifications either.

A way to “inject” simulated collision events into non-kinematic physics objects is using Rigidbody.AddForceAtPosition with the parameter ForceMode.Impulse (or ForceMode.VelocityChange, which would ignore the rigidbody’s mass). But as said that’s for non-kinematic objects.

sorry, i wasn’t clear: the assumption was that all the objects that the player is standing on are kinematic (because unity already support kinematic rigidbodies carrying dynamic ones, so long as the carriers are moved with MovePosition). the player itself is a dynamic rigidbody being moved with AddForce.

Yes, that’s also my assumption. Let me rephrase it: “Being the rigidbodies the player is standing on kinematic bodies it’s up to you to implement a collision detection and response system on these.”

really? there’s no way to access the underlying physics sim and call functions in it? that seems like something people might wanna do. ok well i’ll try another solution then.

You do that all the time, there’s lots of “functions” exposed for stuff like physics queries, components etc.

Don’t let misundersanding cause you to change your approach.

When you want the physics sim to simulate a collision between non-kinematic bodies that aren’t touching then you can simply compute and apply impulses to them. If you also want to control those bodies in some specific way (i.e. a moving platform) then you can do so via AddForce / AddForceAtPosition, so the body also responds to the fictitious collision events.

If using kinematic bodies is a must, then you have many resources available:

But in the end, after using all of that, the actual reaction of the contacted rigidbody must be entirely implemented by you. Kinematic bodies can only be moved to the exact pose you give them via MovePosition and MoveRotation (or via the their Transform if you need to teleport them without causing physics consequences on their way). There’s no other way as per the definition of kinematic rigidbody.

I assume your player controller works by keeping constant distance from any object below by adding a force pointing up. Now you have the problem that the player doesn’t move with a platform. My player controller works the same way and I think it’s a very good route to go.

What you’re missing is the following:
Instead of just adding horizontal forces when the movement buttons are pressed, you should also get the velocity of the object you’re standing on (the rigidbody is a property of RaycastHit afaik). Calculate the difference between your characters velocity and the object below and add some fraction of that as a force to your player.

Some notes:
If the raycastHit.rigidbody property is null, said velocity should be Vector3.zero

For this to work you should set rigidbody.damping to zero because that’s what your now effectively doing yourself

yeah this makes sense to me, where i get lost is working out what the correct uh, application of that force is. i can calculate the exact velocity (by dividing by fixed delta time) and the velocity difference between the player and the ground very easily, but then the next step is giving some (or all) of that force to the player. i assume the next step is not to hard-set the player’s velocity to be some interpolation between its current velocity and the inherited velocity, but then, what is the correct force mode for this kind of change?
here is what my code looks like at the moment, with space for the application of inherited velocity:

private void ApplyInheritedVelocity () { //tracks contact transforms and the character's location relative to them in both world and local space, then applies the difference between the two so that the character remains attached to moving objects;
  if (grounded) { //if we're grounded (we only want to apply inherited velocity if there is something to generate it);
    currentContactTransform = groundContact.collider.transform; //set our current contact transform to be whatever we are standing on;
    if (currentContactTransform == lastContactTransform) { //if the current contact transform is the same as last frame (which means we have remained in contact with it);
      inheritedVelocity = (currentContactTransform.TransformPoint (localContactPoint) - globalContactPoint); //find the difference between our current and previous contact points in world space, this is how far the object should have carried us in that time;
     
      /*SOMETHING GOES HERE*/

      //at the moment i'm literally just adding this vector to the position of the player. it works but it's very inelegant and looks bad when paired with a fixed timestep;
    }
    localContactPoint = currentContactTransform.InverseTransformPoint (groundContact.point); //create new local contact point;
    globalContactPoint = groundContact.point; //and new global contact point;
  } else { //if we're not grounded;
    currentContactTransform = null; //then our ground contact is null;
  }
  lastContactTransform = currentContactTransform; //and store the current contact transform in our last contact variable, even if that contact is null;
}

EDIT: re: ground objects being kinematic: this is not a requirement. i just thought it would be helpful, given that there is clearly some quality inherent to kinematic rigidbodies that the simulation needs in order to perform automatic velocity inheritance. as you can see above, my current system is entirely rigidbody agnostic, and if i can keep it that way i would like to, simply because it’s less work for me! :smile:

also what is damping? do you mean drag?

sorry, yes, drag :slight_smile:

You don’t need the calculate the velocity. Rigidbodies already have that as a property (rb.velocity). In case the ground objects might also be rotating, you actually need to factor this in. So the steps to take are the following:

  1. moveVelocity = …your input converted to Vector3 set velocity
  2. Sphere cast to the ground. Store hit world position and hitInfo.rigidbody
  3. use hit rigidbody.GetPointVelocity(hit world position) (https://docs.unity3d.com/ScriptReference/Rigidbody.GetPointVelocity.html) to get the velocity at the position where your character is standing. This needs a special case where the ground doesnt have a rigidbody so the ground velocity defaults to Vector3.zero. I assume you store this in a Vector3 groundVelocity
  4. AddForce to character rigidbody: characterRb.AddForce((moveVelocity + groundVelocity - characterRigidbody.velocity) * someConstant)

ok yeah this more or less makes sense. a couple points of clarification (and if u don’t know the answer that’s ok I’ll do some tests later):
1: the input forces (gravity and directional inputs) are stored/represented as forces, whereas the ground velocity and character velocities are, well, velocities. they represent distances over time. so, do i need to perform a conversion on the velocities in order to make them forces (or vice versa) in order to make them compatible with the addforce func?
2: i assume the constant (which is like, a friction coefficient?) needs to be between 0 and 1 in order to prevent overshoot?

good to hear :slight_smile:

Speaking in terms of the compiler, they’re “compatible” since both are Vector3. But I guess you’d like to know how they’re compatible in their physical interpretation.

Remember F = m * a from physics? “a” is vectorial acceleration, F is a vectorial force and “m” the mass. So the translation between the “force world” and “acceleration world” is done by multiplying by “m”, the mass.

We find this again in the differential equation of the spring damper equation: a * m + v * d + x * c = 0
Here, acceleration (second derivative of the position x) is multiplied by “m (the mass)”, velocity (first derivative of the position x) is multiplied by “d (damping)” and the actual position x is multiplied by “c (spring constant)”. So we’re actually doing this all the time in physics and in the same way, we can do this in PhysX. It just needs to make physical sense :slight_smile:

In the code above, we’re almost the same thing as in the differential equation, but with c := 0 (there is no spring). So we’re left with a * m = -v * d.

I chose “v” to be the set velocity (moveVelocity) plus the moving reference frame (groundVelocity) minus the characters velocity. I hope with some experimentation you’ll understand why exactly I’m doing this, but intuitively it should make sense.

Yes, somewhat like a friction coefficient but linear instead of constant. With friction you have a constant force acting on whatever is moving across a surface, no matter how fast. The parameter is used linearly, so double the velocity in a direction, doubles the counteracting force. This results in an exponential decay. You can use values between 0 (won’t do anything) and maybe 20. The bigger the number, the snappier the movement gets. It won’t lead to overshoot, but you’ll notice numerical instability if you choose insanely high numbers here.

ok everything seems to have sorted itself out and i think this solution works! [fingers crossed]
here is my final code for those interested:

void FixedUpdate () {
  CalculateInheritedVelocity ();
  EngineUpdate ();
}

private void CalculateInheritedVelocity () { //calculates the inherited velocity vector;
  inheritedVelocity = Vector3.zero; //assume we have no inherited velocity until proven otherwise;
  approximateGroundVel = Vector3.zero;
  exactGroundVel = Vector3.zero;

  if (grounded) { //if we're grounded (we only want to apply inherited velocity if there is something to generate it);
    currentContactTransform = groundContact.collider.transform; //set our current contact transform to be whatever we are standing on;
    if (currentContactTransform == lastContactTransform) { //if the current contact transform is the same as last frame (which means we have remained in contact with it);
      approximateGroundVel = (currentContactTransform.TransformPoint (localContactPoint) - globalContactPoint) / Time.fixedDeltaTime; //find the difference between our current and previous contact points in world space, this is how far the object should have carried us in that time, divided by delta to convert to velocity;
      Rigidbody connectedRigidbody = currentContactTransform.GetComponent <Rigidbody> ();
      if (connectedRigidbody != null) { //if what we are standing on has a rigidbody;
        exactGroundVel = connectedRigidbody.GetPointVelocity (globalContactPoint); //then get the exact velocity of that rigidbody at the point on which we are standing;
      }
    }
    localContactPoint = currentContactTransform.InverseTransformPoint (groundContact.point); //create new local contact point;
    globalContactPoint = groundContact.point; //and new global contact point;
  } else { //if we're not grounded;
    currentContactTransform = null; //then our ground contact is null;
  }
  lastContactTransform = currentContactTransform; //and store the current contact transform in our last contact variable, even if that contact is null;

  inheritedVelocity = exactGroundVel == Vector3.zero ? approximateGroundVel : exactGroundVel; //then we set our inheritedVelocity to be either the exact ground vel or the approx ground vel, depending on whether the exact ground velocity has been assigned;
}

public void EngineUpdate () {
  //blah blah blah various movement stuff, not relevant...

  Vector3 totalMovementForce = movementVector + lift; //combine total input forces;
  rb.AddForce (totalMovementForce + ((inheritedVelocity - rb.velocity) * (grounded ? friction : drag))); //add the difference between the ground's velocity and the player's velocity to simulate friction between the two, if no ground then add drag to slow the player down anyway (player rigidbody has zero drag assigned in the inspector);

  //blah blah other stuff...
}

the way the expression is bracketed has a greater effect than i was expecting (no brackets between totalMovementForce and the velocity difference seems to cause problems) so i had to experiment with a few different arrangements to end up at this one.

i compared the difference between my approximate ground velocity and the GetPointVelocity function, and they’re very close until any angular velocity is introduced, but still pretty close regardless. if i could be bothered i could always make the approximate algorithm a little more rigorous, but it’s close enough that i can’t really be bothered, and most of my moving objects have (or can be made to have) rigidbodies anyway.

thanks so much @tjmaul , you’ve been a great help. :smile:

1 Like