Skip to content

Instantly share code, notes, and snippets.

@Toqozz

Toqozz/Rope.cs Secret

Created July 19, 2021 03:30
Show Gist options
  • Save Toqozz/ea6f4414a1298a8b309e5ddb94317d60 to your computer and use it in GitHub Desktop.
Save Toqozz/ea6f4414a1298a8b309e5ddb94317d60 to your computer and use it in GitHub Desktop.
rope box collision with thickness/skinwidth parameter
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