Unity 程序动画 2D,3D程序蜘蛛脚,人物程序性动画

强大的程序腿效果

请添加图片描述
请添加图片描述
请添加图片描述

请添加图片描述
请添加图片描述

程序脚完全使用算法完成,没有复杂的物理运算

3D蜘蛛脚算法

    public class SpiderProceduralAnimation : MonoBehaviour
    {
        public Transform[] legTargets;
        public float stepSize = 0.15f;
        public int smoothness = 8;
        public float stepHeight = 0.15f;
        public float sphereCastRadius = 0.125f;
        public bool bodyOrientation = true;

        public float raycastRange = 1.5f;
        private Vector3[] defaultLegPositions;
        private Vector3[] lastLegPositions;
        private Vector3 lastBodyUp;
        private bool[] legMoving;
        private int nbLegs;

        private Vector3 velocity;
        private Vector3 lastVelocity;
        private Vector3 lastBodyPos;

        private float velocityMultiplier = 15f;

        Vector3[] MatchToSurfaceFromAbove(Vector3 point, float halfRange, Vector3 up)
        {
            Vector3[] res = new Vector3[2];
            res[1] = Vector3.zero;
            RaycastHit hit;
            Ray ray = new Ray(point + halfRange * up / 2f, -up);

            if (Physics.SphereCast(ray, sphereCastRadius, out hit, 2f * halfRange))
            {
                res[0] = hit.point;
                res[1] = hit.normal;
            }
            else
            {
                res[0] = point;
            }
            return res;
        }

        void Start()
        {
            lastBodyUp = transform.up;

            nbLegs = legTargets.Length;
            defaultLegPositions = new Vector3[nbLegs];
            lastLegPositions = new Vector3[nbLegs];
            legMoving = new bool[nbLegs];
            for (int i = 0; i < nbLegs; ++i)
            {
                defaultLegPositions[i] = legTargets[i].localPosition;
                lastLegPositions[i] = legTargets[i].position;
                legMoving[i] = false;
            }
            lastBodyPos = transform.position;
        }

        IEnumerator PerformStep(int index, Vector3 targetPoint)
        {
            Vector3 startPos = lastLegPositions[index];
            for (int i = 1; i <= smoothness; ++i)
            {
                legTargets[index].position = Vector3.Lerp(startPos, targetPoint, i / (float)(smoothness + 1f));
                legTargets[index].position += transform.up * Mathf.Sin(i / (float)(smoothness + 1f) * Mathf.PI) * stepHeight;
                yield return new WaitForFixedUpdate();
            }
            legTargets[index].position = targetPoint;
            lastLegPositions[index] = legTargets[index].position;
            legMoving[0] = false;
        }


        void FixedUpdate()
        {
            velocity = transform.position - lastBodyPos;
            velocity = (velocity + smoothness * lastVelocity) / (smoothness + 1f);

            if (velocity.magnitude < 0.000025f)
                velocity = lastVelocity;
            else
                lastVelocity = velocity;


            Vector3[] desiredPositions = new Vector3[nbLegs];
            int indexToMove = -1;
            float maxDistance = stepSize;
            for (int i = 0; i < nbLegs; ++i)
            {
                desiredPositions[i] = transform.TransformPoint(defaultLegPositions[i]);

                float distance = Vector3.ProjectOnPlane(desiredPositions[i] + velocity * velocityMultiplier - lastLegPositions[i], transform.up).magnitude;
                if (distance > maxDistance)
                {
                    maxDistance = distance;
                    indexToMove = i;
                }
            }
            for (int i = 0; i < nbLegs; ++i)
                if (i != indexToMove)
                    legTargets[i].position = lastLegPositions[i];

            if (indexToMove != -1 && !legMoving[0])
            {
                Vector3 targetPoint = desiredPositions[indexToMove] + Mathf.Clamp(velocity.magnitude * velocityMultiplier, 0.0f, 1.5f) * (desiredPositions[indexToMove] - legTargets[indexToMove].position) + velocity * velocityMultiplier;

                Vector3[] positionAndNormalFwd = MatchToSurfaceFromAbove(targetPoint + velocity * velocityMultiplier, raycastRange, (transform.parent.up - velocity * 100).normalized);
                Vector3[] positionAndNormalBwd = MatchToSurfaceFromAbove(targetPoint + velocity * velocityMultiplier, raycastRange * (1f + velocity.magnitude), (transform.parent.up + velocity * 75).normalized);

                legMoving[0] = true;

                if (positionAndNormalFwd[1] == Vector3.zero)
                {
                    StartCoroutine(PerformStep(indexToMove, positionAndNormalBwd[0]));
                }
                else
                {
                    StartCoroutine(PerformStep(indexToMove, positionAndNormalFwd[0]));
                }
            }

            lastBodyPos = transform.position;
            if (nbLegs > 3 && bodyOrientation)
            {
                Vector3 v1 = legTargets[0].position - legTargets[1].position;
                Vector3 v2 = legTargets[2].position - legTargets[3].position;
                Vector3 normal = Vector3.Cross(v1, v2).normalized;
                Vector3 up = Vector3.Lerp(lastBodyUp, normal, 1f / (float)(smoothness + 1));
                transform.up = up;
                transform.rotation = Quaternion.LookRotation(transform.parent.forward, up);
                lastBodyUp = transform.up;
            }
        }

        private void OnDrawGizmosSelected()
        {
            for (int i = 0; i < nbLegs; ++i)
            {
                Gizmos.color = Color.red;
                Gizmos.DrawWireSphere(legTargets[i].position, 0.05f);
                Gizmos.color = Color.green;
                Gizmos.DrawWireSphere(transform.TransformPoint(defaultLegPositions[i]), stepSize);
            }
        }
    }

2D蜘蛛脚算法

    public class SpiderProceduralAnimation : MonoBehaviour
    {
        public Transform[] legTargets;
        public float stepSize = 0.15f;
        public int smoothness = 8;
        public float stepHeight = 0.15f;
        public bool bodyOrientation = true;

        public float raycastRange = 1.5f;
        private Vector2[] defaultLegPositions;
        private Vector2[] lastLegPositions;
        private Vector2 lastBodyUp;
        private bool[] legMoving;
        private int nbLegs;

        private Vector2 velocity;
        private Vector2 lastVelocity;
        private Vector2 lastBodyPos;

        private float velocityMultiplier = 7f;

        Vector2[] MatchToSurfaceFromAbove(Vector2 point, float halfRange, Vector2 up)
        {
            Vector2[] res = new Vector2[2];
            res[1] = Vector3.zero;
            RaycastHit2D hit;
            hit = Physics2D.Raycast(point + halfRange * up / 2f, -up, 2f * halfRange);
            Debug.DrawRay(point + halfRange * up / 2f, -up * 2f * halfRange, Color.red, smoothness * Time.deltaTime);
            if (hit.collider)
            {
                res[0] = hit.point;
                res[1] = hit.normal;
            }
            else
            {
                res[0] = point;
            }
            return res;
        }

        void Start()
        {
            lastBodyUp = transform.up;

            nbLegs = legTargets.Length;
            defaultLegPositions = new Vector2[nbLegs];
            lastLegPositions = new Vector2[nbLegs];
            legMoving = new bool[nbLegs];
            for (int i = 0; i < nbLegs; ++i)
            {
                defaultLegPositions[i] = legTargets[i].localPosition;
                lastLegPositions[i] = legTargets[i].position;
                legMoving[i] = false;
            }
            lastBodyPos = transform.position;
        }

        IEnumerator PerformStep(int index, Vector3 targetPoint)
        {
            Vector3 startPos = lastLegPositions[index];
            for (int i = 1; i <= smoothness; ++i)
            {
                legTargets[index].position = Vector3.Lerp(startPos, targetPoint, i / (float)(smoothness + 1f));
                legTargets[index].position += transform.up * Mathf.Sin(i / (float)(smoothness + 1f) * Mathf.PI) * stepHeight;
                yield return new WaitForFixedUpdate();
            }
            legTargets[index].position = targetPoint;
            lastLegPositions[index] = legTargets[index].position;
            legMoving[0] = false;
        }


        void FixedUpdate()
        {
            velocity = (Vector2)transform.position - lastBodyPos;
            velocity = (velocity + smoothness * lastVelocity) / (smoothness + 1f);

            if (velocity.magnitude < 0.000025f)
                velocity = lastVelocity;
            else
                lastVelocity = velocity;


            Vector2[] desiredPositions = new Vector2[nbLegs];
            int indexToMove = -1;
            float maxDistance = stepSize;
            for (int i = 0; i < nbLegs; ++i)
            {
                desiredPositions[i] = transform.TransformPoint(defaultLegPositions[i]);

                float distance = Vector3.ProjectOnPlane(desiredPositions[i] + velocity * velocityMultiplier - lastLegPositions[i], transform.up).magnitude;
                if (distance > maxDistance)
                {
                    maxDistance = distance;
                    indexToMove = i;
                }
            }
            for (int i = 0; i < nbLegs; ++i)
                if (i != indexToMove)
                    legTargets[i].position = lastLegPositions[i];

            if (indexToMove != -1 && !legMoving[0])
            {
                Vector2 targetPoint = desiredPositions[indexToMove] + Mathf.Clamp(velocity.magnitude * velocityMultiplier, 0.0f, 1.5f) * (desiredPositions[indexToMove] -
                    (Vector2)legTargets[indexToMove].position) + velocity * velocityMultiplier;

                Vector2[] positionAndNormalFwd = MatchToSurfaceFromAbove(targetPoint + velocity * velocityMultiplier, raycastRange,
                    ((Vector2)transform.parent.up - velocity * 10).normalized);

                Vector2[] positionAndNormalBwd = MatchToSurfaceFromAbove(targetPoint + velocity * velocityMultiplier, raycastRange * (1f + velocity.magnitude),
                    ((Vector2)transform.parent.up + velocity * 10).normalized);

                legMoving[0] = true;

                if (positionAndNormalFwd[1] == Vector2.zero)
                {
                    StartCoroutine(PerformStep(indexToMove, positionAndNormalBwd[0]));
                }
                else
                {
                    StartCoroutine(PerformStep(indexToMove, positionAndNormalFwd[0]));
                }
            }

            lastBodyPos = transform.position;
            if (nbLegs > 1 && bodyOrientation)
            {
                Vector2 v1 = (legTargets[1].position - legTargets[0].position).normalized;

                Vector3 v2 = Vector3.back;
                Vector3 normal = Vector3.Cross(v1, v2).normalized;
                Vector3 up = Vector3.Lerp(lastBodyUp, normal, 1f / (float)(smoothness + 1));
                transform.up = up;
                transform.rotation = Quaternion.LookRotation(transform.parent.forward, up);
                lastBodyUp = transform.up;
            }
        }

        private void OnDrawGizmosSelected()
        {
            for (int i = 0; i < nbLegs; ++i)
            {
                Gizmos.color = Color.red;
                Gizmos.DrawWireSphere(legTargets[i].position, 0.05f);
                Gizmos.color = Color.green;
                Gizmos.DrawWireSphere(transform.TransformPoint(defaultLegPositions[i]), stepSize);
            }
        }
    }

人物程序动画算法

public class ProceduralAnimation : MonoBehaviour
{
    /* Some useful functions we may need */

    static Vector3[] CastOnSurface(Vector3 point, float halfRange, Vector3 up)
    {
        Vector3[] res = new Vector3[2];
        RaycastHit hit;
        Ray ray = new Ray(new Vector3(point.x, point.y + halfRange, point.z), -up);

        if (Physics.Raycast(ray, out hit, 2f * halfRange))
        {
            res[0] = hit.point;
            res[1] = hit.normal;
        }
        else
        {
            res[0] = point;
        }
        return res;
    }

    /*************************************/


    public Transform leftFootTarget;
    public Transform rightFootTarget;
    public Transform leftFootTargetRig;
    public Transform rightFootTargetRig;
    public Transform pivot;
    public Transform scaler;
    
    public float smoothness = 2f;
    public float stepHeight = 0.2f;
    public float stepLength = 1f;
    public float angularSpeed = 0.1f;
    public float velocityMultiplier = 80f;
    public float bounceAmplitude = 0.05f;
    public bool running = false;

    private Vector3 initLeftFootPos;
    private Vector3 initRightFootPos;

    private Vector3 lastLeftFootPos;
    private Vector3 lastRightFootPos;

    private Vector3 lastBodyPos;
    private Vector3 initBodyPos;

    private Vector3 velocity;
    private Vector3 lastVelocity;

    // Start is called before the first frame update
    void Start()
    {
        initLeftFootPos = leftFootTarget.localPosition;
        initRightFootPos = rightFootTarget.localPosition;

        lastLeftFootPos = leftFootTarget.position;
        lastRightFootPos = rightFootTarget.position;

        lastBodyPos = transform.position;
        initBodyPos = transform.localPosition;
    }

    // Update is called once per frame
    void FixedUpdate()
    {
        velocity = transform.position - lastBodyPos;
        velocity *= velocityMultiplier;
        velocity = (velocity + smoothness * lastVelocity) / (smoothness + 1f);

        if (velocity.magnitude < 0.000025f * velocityMultiplier)
            velocity = lastVelocity;
        lastVelocity = velocity;
        
        int sign = (Vector3.Dot(velocity.normalized, transform.forward) < 0 ? -1 : 1);

        scaler.localScale = new Vector3(scaler.localScale.x, stepHeight * 2f * velocity.magnitude, stepLength * velocity.magnitude);
        scaler.rotation = Quaternion.LookRotation(sign * velocity.normalized, Vector3.up);
        pivot.Rotate(Vector3.right, sign * angularSpeed, Space.Self);

        Vector3 desiredPositionLeft = leftFootTarget.position;
        Vector3 desiredPositionRight = rightFootTarget.position;

        Vector3[] posNormLeft = CastOnSurface(desiredPositionLeft, 2f, Vector3.up);
        if (posNormLeft[0].y > desiredPositionLeft.y)
        {
            leftFootTargetRig.position = posNormLeft[0];
        }
        else
        {
            leftFootTargetRig.position = desiredPositionLeft;
        }
        if (posNormLeft[1] != Vector3.zero)
        {
            leftFootTargetRig.rotation = Quaternion.LookRotation(sign * velocity.normalized, posNormLeft[1]);
        }

        Vector3[] posNormRight = CastOnSurface(desiredPositionRight, 2f, Vector3.up);
        if (posNormRight[0].y > desiredPositionRight.y)
        {
            rightFootTargetRig.position = posNormRight[0];
        }
        else
        {
            rightFootTargetRig.position = desiredPositionRight;
        }
        if(posNormRight[1] != Vector3.zero)
        {
            rightFootTargetRig.rotation = Quaternion.LookRotation(sign * velocity.normalized, posNormRight[1]);
        }

        lastLeftFootPos = leftFootTargetRig.position;
        lastRightFootPos = rightFootTargetRig.position;
        float feetDistance = Mathf.Clamp01(Mathf.Abs(leftFootTargetRig.localPosition.z - rightFootTargetRig.localPosition.z) / (stepLength / 4f));

        float heightReduction = (running ? Mathf.Clamp01(velocity.magnitude) * bounceAmplitude - bounceAmplitude * Mathf.Clamp(velocity.magnitude, 0f, 10f) * feetDistance : bounceAmplitude * Mathf.Clamp(velocity.magnitude, 0f, 10f) * feetDistance);
        transform.localPosition = initBodyPos - heightReduction * Vector3.up;
        scaler.localPosition = new Vector3(0f, heightReduction, 0f);

        lastBodyPos = transform.position;
    }
    
    private void OnDrawGizmosSelected()
    {
        Gizmos.DrawWireSphere(leftFootTarget.position, 0.2f);
        Gizmos.DrawWireSphere(rightFootTarget.position, 0.2f);
    }
}
哇塞这么多代码,头晕,搞不明白,看不太懂

没事的! demo来啦~
点击👇
程序脚Demo

  • 3
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

唐沢

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值