Ridgidbody2D.Cast returns a slightly wrong RaycastHit2D.distance?
I'm trying to move a kinematic 2D physics object using Ridgidbody2D.Cast. When a collision is detected, the distance given by the RaycastHit2D seems incorrect because the collider never ends up flush with the collider that was hit.
I've made a sample project. If you hit play and watch both colliders in the scene view of the editor, you can see that the CircleCollider2D of the falling "ball" object goes a little ways into the BoxCollider2D of the "Quad" object.
Here's the script that handles movement:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
[RequireComponent(typeof(Rigidbody2D))]
[RequireComponent(typeof(Collider2D))]
public class Actor : MonoBehaviour {
[SerializeField]
private Collider2D otherCollider;
private Rigidbody2D rb2d;
private Collider2D collider;
private RaycastHit2D[] hitBuffer = new RaycastHit2D[8];
private ContactFilter2D contactFilter;
void Awake() {
rb2d = GetComponent<Rigidbody2D>();
collider = GetComponent<Collider2D>();
contactFilter = new ContactFilter2D();
contactFilter.useTriggers = false;
contactFilter.SetLayerMask(Physics2D.GetLayerCollisionMask(gameObject.layer));
}
public void Move(Vector2 move) {
float distance = move.magnitude;
Vector2 direction = move.normalized;
float searchDistance = distance;
int count = rb2d.Cast(direction, hitBuffer, searchDistance);
if (count > 0) {
distance = hitBuffer[0].distance;
}
// transform.Translate((Vector3)direction * distance);
rb2d.MovePosition(rb2d.position + direction * distance);
ColliderDistance2D colDistance = collider.Distance(otherCollider);
Debug.Log(string.Format("Cast distance: {0}, Overlap distance: {1}", distance, colDistance.distance));
}
}
And here's the script that causes downward movement using the above script:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
[RequireComponent(typeof(Actor))]
public class SimpleBallController : MonoBehaviour {
private Actor actor;
void Awake() {
actor = GetComponent<Actor>();
}
void FixedUpdate() {
actor.Move(Vector2.down * Time.fixedDeltaTime);
}
}
Answer by kreso · Nov 04, 2018 at 06:13 AM
The physics uses ticks (moments in time) for simulation. It is unlikely that a collision will happen exactly at the same time as the tick. If object A is moving 10 units per tick and object B is 5 units away (and static). Physics engine won't know that collision happened until next tick happens (and now the object A will be 5 units 'inside' the collided object B).
With a little bit of math you can calculate actual position of object A after the collision. But again, do not expect it to be on the boundary with object B; but actually outside of the collided object B because some time passed (maybe half a tick) since the collision (and object A probably bounced off a bit, if it has any bounce).
For collisions resulting from the physics simulation that makes sense. It gets moved, I find the overlap if it's collided, and then I adjust the position.
I neglected to say that the moving ball's ridgidbody2D is set to kinematic, though, so it doesn't actually participate in the physics simulation and only moves when I tell it to via script. I'll edit the question.
Since I'm using raycasting I believe I'm kind of doing the opposite: finding the distance and then moving the ridigidbody2D. Since this should be completely under my control it's all the more confusing to me.
You are absolutely right. Still, Unity uses some kind of iteration method for raycasting (when using rigidbody2d.Cast method). We don't really know what it is (I tried going through the source code, couldn't find much for Cast method).
If you are trying to do something in particular, perhaps there is a better approach than using Unity's helper physics functions (and write your own).
I had fun experimenting with your project, l learned about 'Default Contact offset'. Here is a little GIF I made with your project (slowed down the movement). The thin yellowish lines are AABB's of colliders. Notice the Box has almost like an edge to it. This is due to contact offset in Physics2D Unity settings. If bumped down to 0.0001 it is almost perfect (but never perfect of course).
Also, notice that collision does indeed register (the normal's arrow appears). However, the Cast method does not register until somewhere between when distance was '-0.01361971' and '-0.01367971'.
I didn't know about the 'Default Contact Offset', thanks! I noticed that objects under the physics simulation never quite lie flush with things they collide into, and this explains it.