-
-
Save Toqozz/ea6f4414a1298a8b309e5ddb94317d60 to your computer and use it in GitHub Desktop.
rope box collision with thickness/skinwidth parameter
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
private void AdjustCollisions() { | |
Profiler.BeginSample("Adjust Collisions"); | |
// Loop rope nodes and check if currently colliding. | |
for (int i = 0; i < totalNodes - 1; i++) { | |
VerletNode node = nodes[i]; | |
//int result = Physics2D.OverlapCircleNonAlloc(node.position, skinWidth, colliderBuffer, filter.layerMask); | |
for (int j = 0; j < node.numCollisions; j++) { | |
if (node.collisionBuffer[j] is CircleCollider2D) { | |
CircleCollider2D col = node.collisionBuffer[j] as CircleCollider2D; | |
float radius = col.radius * col.transform.lossyScale.x; | |
float distance = Vector2.Distance(col.transform.position, node.position); | |
if (distance - radius > skinWidth) { | |
continue; | |
} | |
Vector2 colliderCenter = col.transform.position; | |
Vector2 collisionDirection = node.position - colliderCenter; | |
Vector2 hitPos = colliderCenter + collisionDirection.normalized * | |
((col.transform.lossyScale.x * col.radius) + skinWidth); | |
node.position = hitPos; | |
} else if (node.collisionBuffer[j] is BoxCollider2D) { | |
Vector2 closestPoint = node.collisionBuffer[j].ClosestPoint(node.position); | |
// NOTE: this probably won't work with a low skin width. | |
if (Vector2.Distance(closestPoint, node.position) > skinWidth) { | |
continue; | |
} | |
BoxCollider2D col = node.collisionBuffer[j] as BoxCollider2D; | |
// Replacing node.position here with closestPoint actually does make a difference. | |
// TODO: try hitNormal approach again. | |
Vector2 localPoint = col.transform.InverseTransformPoint(node.position); | |
Vector2 scale = col.transform.InverseTransformVector(Vector2.one); | |
// Don't remove skinWidth here!!! It's required! | |
float left = -col.size.x * .5f - skinWidth * scale.x; | |
float right = col.size.x * .5f + skinWidth * scale.x; | |
float top = col.size.y * .5f + skinWidth * scale.y; | |
float bottom = -col.size.y * .5f - skinWidth * scale.y; | |
float dl = Mathf.Abs(localPoint.x - left) / scale.x; | |
float dr = Mathf.Abs(localPoint.x - right) / scale.x; | |
float dt = Mathf.Abs(localPoint.y - top) / scale.y; | |
float db = Mathf.Abs(localPoint.y - bottom) / scale.y; | |
// Offsetting by just the skinWidth results in a 'stickyness', i.e. the rope isn't being pushed far enough out. | |
// I'm not sure exactly the reason for this. The effect is more prominent on angled surfaces (where there would | |
// be more float precision loss), so my educated guess is floating point issues, but it seems illogical that it | |
// would have such a large effect. | |
// A possible way to get around this is to find the hit normal and hit position and push out /after/ we transform back. | |
float floatError = .01f; | |
//Vector2 hitNormal; | |
if (dt <= db && dt <= dl && dt <= dr) { | |
//hitNormal = Vector2.up; | |
localPoint.y = top + floatError * scale.y; | |
} else if (db <= dl && db <= dr) { | |
//hitNormal = Vector2.down; | |
localPoint.y = bottom - floatError * scale.y; | |
} else if (dl <= dr) { | |
//hitNormal = Vector2.left; | |
localPoint.x = left - floatError * scale.x; | |
} else { | |
//hitNormal = Vector2.right; | |
localPoint.x = right + floatError * scale.x; | |
} | |
//hitNormal = col.transform.TransformVector(hitNormal); | |
Vector2 hitPos = col.transform.TransformPoint(localPoint); | |
node.position = hitPos; | |
} | |
} | |
} | |
Profiler.EndSample(); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment