private void SnapshotCollision() { Profiler.BeginSample("Snapshot"); numCollisions = 0; // Loop through each node and get collisions within a radius. for (int i = 0; i < nodes.Length; i++) { int collisions = Physics2D.OverlapCircleNonAlloc(nodes[i].position, collisionRadius, colliderBuffer); for (int j = 0; j < collisions; j++) { Collider2D col = colliderBuffer[j]; int id = col.GetInstanceID(); int idx = -1; for (int k = 0; k < numCollisions; k++) { if (collisionInfos[k].id == id) { idx = k; break; } } // If we didn't have the collider, we need to add it. if (idx < 0) { // Record all the data we need to use into our classes. CollisionInfo ci = collisionInfos[numCollisions]; ci.id = id; ci.wtl = col.transform.worldToLocalMatrix; ci.ltw = col.transform.localToWorldMatrix; ci.scale.x = ci.ltw.GetColumn(0).magnitude; ci.scale.y = ci.ltw.GetColumn(1).magnitude; ci.position = col.transform.position; ci.numCollisions = 1; // 1 collision, this one. ci.collidingNodes[0] = i; switch (col) { case CircleCollider2D c: ci.colliderType = ColliderType.Circle; ci.colliderSize.x = ci.colliderSize.y = c.radius; break; case BoxCollider2D b: ci.colliderType = ColliderType.Box; ci.colliderSize = b.size; break; default: ci.colliderType = ColliderType.None; break; } numCollisions++; if (numCollisions >= MAX_ROPE_COLLISIONS) { Profiler.EndSample(); return; } // If we found the collider, then we just have to increment the collisions and add our node. } else { CollisionInfo ci = collisionInfos[idx]; if (ci.numCollisions >= totalNodes) { continue; } ci.collidingNodes[ci.numCollisions++] = i; } } } shouldSnapshotCollision = false; Profiler.EndSample(); }
private void Awake() { if (totalNodes > MAX_RENDER_POINTS) { Debug.LogError("Total nodes is more than MAX_RENDER_POINTS, so won't be able to render the entire rope."); } nodes = new VerletNode[totalNodes]; collisionInfos = new CollisionInfo[MAX_ROPE_COLLISIONS]; for (int i = 0; i < collisionInfos.Length; i++) { collisionInfos[i] = new CollisionInfo(totalNodes); } // Buffer for OverlapCircleNonAlloc. colliderBuffer = new Collider2D[COLLIDER_BUFFER_SIZE]; renderPositions = new Vector4[totalNodes]; // Spawn nodes starting from the transform position and working down. Vector2 pos = transform.position; for (int i = 0; i < totalNodes; i++) { nodes[i] = new VerletNode(pos); renderPositions[i] = new Vector4(pos.x, pos.y, 1, 1); pos.y -= nodeDistance; } // Mesh setup. Mesh mesh = new Mesh(); { Vector3[] vertices = new Vector3[totalNodes * VERTICES_PER_NODE]; int[] triangles = new int[totalNodes * TRIANGLES_PER_NODE * 3]; for (int i = 0; i < totalNodes; i++) { // 4 triangles per node, 3 indices per triangle. int idx = i * TRIANGLES_PER_NODE * 3; // 8 vertices per node. int vIdx = i * VERTICES_PER_NODE; // Unity uses a CLOCKWISE WINDING ORDER -- clockwise tri indices are facing the camera. triangles[idx + 0] = vIdx; // v1 top triangles[idx + 1] = vIdx + 1; // v2 bottom triangles[idx + 2] = vIdx + 2; // v1 bottom triangles[idx + 3] = vIdx; // v1 top triangles[idx + 4] = vIdx + 3; // v2 top triangles[idx + 5] = vIdx + 1; // v2 bottom triangles[idx + 6] = vIdx + 4; // tl triangles[idx + 7] = vIdx + 7; // br triangles[idx + 8] = vIdx + 6; // bl triangles[idx + 9] = vIdx + 4; // tl triangles[idx + 10] = vIdx + 5; // tr triangles[idx + 11] = vIdx + 7; // br } // We only really care about the number of vertices, not what they actually are -- the positions aren't used. mesh.vertices = vertices; mesh.triangles = triangles; // Since we pretty much want the rope to always render (it's always going to be on screen if it's active), we // just set the bounds super large to avoid recalculating the bounds when the rope changes. mesh.bounds = new Bounds(Vector3.zero, Vector3.one * 100f); } GetComponent <MeshFilter>().mesh = mesh; material = GetComponent <MeshRenderer>().material; material.SetFloat("_Width", drawWidth); }
private void AdjustCollisions() { Profiler.BeginSample("Collision"); for (int i = 0; i < numCollisions; i++) { CollisionInfo ci = collisionInfos[i]; switch (ci.colliderType) { case ColliderType.Circle: { float radius = ci.colliderSize.x * Mathf.Max(ci.scale.x, ci.scale.y); for (int j = 0; j < ci.numCollisions; j++) { VerletNode node = nodes[ci.collidingNodes[j]]; float distance = Vector2.Distance(ci.position, node.position); // Early out if we're not colliding. if (distance - radius > 0) { continue; } Vector2 dir = (node.position - ci.position).normalized; Vector2 hitPos = ci.position + dir * radius; node.position = hitPos; } } break; case ColliderType.Box: { for (int j = 0; j < ci.numCollisions; j++) { VerletNode node = nodes[ci.collidingNodes[j]]; Vector2 localPoint = ci.wtl.MultiplyPoint(node.position); // If distance from center is more than box "radius", then we can't be colliding. Vector2 half = ci.colliderSize * .5f; Vector2 scalar = ci.scale; float dx = localPoint.x; float px = half.x - Mathf.Abs(dx); if (px <= 0) { continue; } float dy = localPoint.y; float py = half.x - Mathf.Abs(dy); if (py <= 0) { continue; } // Need to multiply distance by scale or we'll mess up on scaled box corners. if (px * scalar.x < py * scalar.y) { float sx = Mathf.Sign(dx); localPoint.x = half.x * sx; } else { float sy = Mathf.Sign(dy); localPoint.y = half.y * sy; } Vector2 hitPos = ci.ltw.MultiplyPoint(localPoint); node.position = hitPos; } } break; } } Profiler.EndSample(); }