Example #1
0
        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();
        }
Example #2
0
        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);
        }
Example #3
0
        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();
        }