Why are rigidbody interactions only consistent when all mesh colliders are set to be Kinematic rigidbodies, and is there a way around that?

[Edit: Note my script here in my original question post incorrectly uses Update(), it should be FixedUpdate(), but even with that the issue still occurs]

[Note that a previous question touched on this area to some extent:
Rigidbody Character Jumps Higher With At Least 2 Collisions - Unity Answers]

One of the challenges from this issue is that if there are inconsistencies as shown below, it makes it harder to set gameplay limits, e.g. place areas that are out of reach, if you want to guarantee a character couldn’t jump over a certain height, etc.

I setup a test project and scene to demonstrate:

  • The scene contains 7 objects plus a camera and directional light
  • There are 3 cubes (to test 3 ways of collision interactions)
  • There are 3 ‘towers’ (stretched cubes)
  • There is 1 floor plane

The 3 cubes are in slightly different positions:

  • The first cube simply rests on the flat plane
  • The second cube is adjacent to a single tower
  • The third cube is adjacent to two towers
  • Each cube has the same simple script that just waits a couple seconds (for the cubes to settle on the plane) and then adds an upwards force. Whilst it travels the maximum Y coordinate reach is tracked and can be seen in the Inspector

This is the script component attached to each cube:

using UnityEngine;
using System.Collections;

[RequireComponent(typeof(Rigidbody))]
public class WaitAndJump : MonoBehaviour {
	public float jumpForce = 5f;
	public float highestPointReached;
	private bool isJumpTriggered;
	
	void Update () {
		if (isJumpTriggered)
		{
			if (transform.position.y > highestPointReached)
			{
				highestPointReached = transform.position.y;
			}    
		}
		else if (Time.time > 2.0f)
		{
			rigidbody.AddForce(Vector3.up * jumpForce, ForceMode.Impulse);

			isJumpTriggered = true;
		}
	}
}

What I’d expect: Is that each cube will reach the same maximum Y position, regardless of its position.
[Although not shown in this test, I believe this does work if the cube interacts with a single mesh]

What the example shows: With different settings on the ‘tower’ objects, the cubes reach different maximum Y positions.

Here are the four separate outcomes:

(A) With Tower objects set to not be rigidbodies

Max Y reached =
1.70 - Cube on its own
1.75 - Cube next to single tower
3.01 - Cube next to two towers

(B) With Tower objects setup as rigidbodies (but WITHOUT ‘isKinematic’ set)

Max Y reached =
1.70 - Cube on its own
1.75 - Cube next to single tower
1.86 - Cube next to two towers

(C) With Tower objects setup as rigidbodies (and WITH ‘isKinematic’ set)

Max Y reached =
1.70 - Cube on its own
1.75 - Cube next to single tower
1.77 - Cube next to two towers

(D) As above, but with the floor plane also set as a rigidbody (with ‘isKinematic’ set)

Max Y reached =
1.70 - For all 3 cubes

[The attached sample project is setup as (C) above]

So the system can collide as expected, but only when all objects are Kinematic Rigidbodies - but making all objects Kinematic rigidbody would seem like a potentially expensive solution, hence the original question:

Why are rigidbody interactions only consistent when all mesh colliders are set to be Kinematic rigidbodies, and is there a way around that?

[34399-rigidbodymulticollision.zip|34399]

I may have a workaround, so I’ll answer my own question!

To demonstrate the problem and the solution I created a new (and clearer) demo project.

The problem

The root of the issue appears to be
that when rigidbody game objects are
driven with a ‘continuous force’, e.g.
with the default ForceMode.Force or
(as is usually the case) with Unity’s
gravity, then the hit reactions are
inconsistent. The inconsistency seems
to occur when a rigidbody is right
next to several colliders

The workaround

The solution was to disable Unity’s
gravity and implement my own, by
applying an ‘Instant’ force each
FixedUpdate()

The demo

The demo project shows three methods of applying gravity, each part separately coloured:

  1. (Red) Unity standard gravity
  2. (Blue) Custom written gravity (Unity gravity is turned off) with a Continuous AddForce AddForce applied each FixedUpdate
  3. (Green) Custom written gravity (Unity gravity is turned off) with an Instant AddForce applied each FixedUpdate

Each of the three methods shows three cubes falling under that system of gravity:

  • One cube on its own
  • One cube next to a single collider mesh
  • One cube next to two collider meshes

The cubes are set to fall for a short time period, then they are stopped and the Y position of each cube is output to the console along with the mode used.

That output is below (resorted for readability) - the outcome being that only AddForce with the instant force of ForceMode.Impulse gives a consistent result.

[34508-rigidbodymulticollisionv2.zip|34508]

T=0.92: Stop at Y = 0.7581575 - Red_UnityGravity, SoloCube
T=0.92: Stop at Y = 0.7581575 - Red_UnityGravity, CubeWithOneTower
T=0.92: Stop at Y = 2.788827 - Red_UnityGravity, CubeWithTwoTowers

T=0.92: Stop at Y = 0.7581575 - Blue_CustomGravityContinuousForce, SoloCube
T=0.92: Stop at Y = 0.7581575 - Blue_CustomGravityContinuousForce, CubeWithOneTower
T=0.92: Stop at Y = 2.788827 - Blue_CustomGravityContinuousForce, CubeWithTwoTowers

T=0.92: Stop at Y = 0.7581575 - Green_CustomGravityInstantForce, SoloCube
T=0.92: Stop at Y = 0.7581575 - Green_CustomGravityInstantForce, CubeWithOneTower
T=0.92: Stop at Y = 0.7581575 - Green_CustomGravityInstantForce, CubeWithTwoTowers