예제 #1
0
        private void Simulate()
        {
            for (int i = 0; i < nodes.Length; i++)
            {
                ref VerletNode node = ref nodes[i];

                Vector2 temp = node.position;
                node.position   += (node.position - node.oldPosition) + gravity * stepTime * stepTime;
                node.oldPosition = temp;
            }
예제 #2
0
        private void ApplyConstraints()
        {
            Profiler.BeginSample("Constraints");

            for (int i = 0; i < nodes.Length - 1; i++)
            {
                VerletNode node1 = nodes[i];
                VerletNode node2 = nodes[i + 1];

                // First node follows the mouse, for debugging.
                if (i == 0 && Input.GetMouseButton(0))
                {
                    node1.position = Camera.main.ScreenToWorldPoint(Input.mousePosition);
                }

                // Current distance between rope nodes.
                float diffX      = node1.position.x - node2.position.x;
                float diffY      = node1.position.y - node2.position.y;
                float dist       = Vector2.Distance(node1.position, node2.position);
                float difference = 0;
                // Guard against divide by 0.
                if (dist > 0)
                {
                    difference = (nodeDistance - dist) / dist;
                }

                Vector2 translate = new Vector2(diffX, diffY) * (.5f * difference);

                node1.position += translate;
                node2.position -= translate;
            }

            /*
             * // Distance constraint which reduces iterations, but doesn't handle stretchyness in a natural way.
             * VerletNode first = nodes[0];
             * VerletNode last = nodes[nodes.Length-1];
             * // Same distance calculation as above, but less optimal.
             * float distance = Vector2.Distance(first.position, last.position);
             * if (distance > 0 && distance > nodes.Length * nodeDistance) {
             *  Vector2 dir = (last.position - first.position).normalized;
             *  last.position = first.position + nodes.Length * nodeDistance * dir;
             * }
             */

            Profiler.EndSample();
        }
예제 #3
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);
        }
예제 #4
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();
        }