Sunday, September 9, 2018

unity - Nestling into contact with a group of physics objects without exerting forces on them


I'm making a pool game, and would like the circle (imaginary) to be in contact with the table and balls, and to fit perfectly as a ball would.


Now I thought I would use Rigidbody and lerp the position of the circle to the raycast hit, and its collider it would force it into a fitting position.


The problem I encountered is that since it has a collider, it also pushes the balls, which I do not want.


So my question is, how do I make an object act like it's touching & blocked by other GameObjects with Colliders and Rigidbodies, without pushing them?


enter image description here



Answer




Here's a way to do this using Physics.SphereCast to test against the bodies' colliders without actually exerting any forces on them.


enter image description here


It assumes that your cursor object is placed on exactly the same y plane as the centers of the balls, so that all the collisions are in-plane against their widest circumference, and the camera is looking straight down at the playfield. (This can be adjusted for a camera at an oblique angle, I just haven't done that bit)


It also currently only works for colliders at rest - it tests that the cursor doesn't move into a ball, but not if a ball moves into the cursor. That could be added if needed.


public class ContactCursor : MonoBehaviour {

// Limits our maximum travel to keep the complexity down.
public float maxSpeed = 15f;

public float radius = 0.5f;


// Remember to set this to include the layer your balls are on.
public LayerMask collisionLayers;

Camera _camera;
float _cameraPlane;

// Cache camera and our distance from it, for positioning relative to cursor.
void Start () {
_camera = Camera.main;

_cameraPlane = _camera.WorldToScreenPoint(transform.position).z;
}

void LateUpdate () {
// Transform the mouse into a position on our travel plane.
Vector3 mousePosition = Input.mousePosition;
mousePosition.z = _cameraPlane;
mousePosition = _camera.ScreenToWorldPoint(mousePosition);

Vector3 offset = mousePosition - transform.position;

Vector3 direction = offset.normalized;
float maxDistance = Mathf.Min(offset.magnitude, maxSpeed * Time.deltaTime);

Vector3 targetPosition = transform.position + direction * maxDistance;

// Currently set to do 2 passes:
// 1: Beeline toward the mouse until you hit something.
// 2: Use any remaining movement to move perpendicular to the obstacle.
// In my tests this was enough, but you can increase this
// if you have more complex arrangements or a faster cursor.

int limit = 2;
for (int i = 0; i < limit; i++) {

// Check for a collision in the direction we're trying to move.
RaycastHit hit;
if (Physics.SphereCast(transform.position, radius, direction, out hit, maxDistance, collisionLayers)) {

// Back up to closest non-intersecting point.
// (Plus a small fudge factor for stability).
offset = hit.point + radius * hit.normal - transform.position;

float distance = Vector3.Dot(offset, direction) - 0.001f;
targetPosition = transform.position + direction * distance;

transform.position = targetPosition;

if (i + 1 == limit)
return;

// Determine a new move to approach the cursor without
// penetrating deeper into whatever we hit.

maxDistance = Mathf.Max(maxDistance - distance, 0f);

offset = mousePosition - transform.position;

// Make sure we don't overshoot the closest point on this line
// to the mouse (prevents vibrating when close-but-mot-quite).
distance = offset.sqrMagnitude;
float forbidden = Vector3.Dot(offset, hit.normal);
distance -= forbidden * forbidden;
// Shouldn't happen with infinite-precision real numbers,

// but rounding errors happen in practice.
if (distance < 0f)
return;
maxDistance = Mathf.Min(maxDistance, Mathf.Sqrt(distance));

// Calculate new movement direction.
offset -= forbidden * hit.normal;
offset.y = 0f;
direction = offset.normalized;


targetPosition = transform.position + direction * maxDistance;
}
else {
// No collision! Complete the move.
transform.position = targetPosition;
return;
}
}
}
}

No comments:

Post a Comment

Simple past, Present perfect Past perfect

Can you tell me which form of the following sentences is the correct one please? Imagine two friends discussing the gym... I was in a good s...