You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Unity中实现程序化蜘蛛腿交替移动:仅对侧腿着地时移动

解决蜘蛛程序化动画的对角腿步态问题

你的核心问题是未通过状态变量正确控制腿的移动时机,且当前代码直接瞬间移动腿的逻辑既不符合自然步态,也难以同步移动状态。以下是针对性的修改方案:

关键思路

  • 将腿分为两组(你提到的前两条为一组,后两条为另一组),同一时间仅允许一组腿移动
  • 每组腿启动移动前,必须确认对侧组的两条腿均处于着地状态
  • 用平滑移动替代瞬间位置赋值,同时通过布尔变量标记移动状态,避免重复触发移动逻辑

修改后的完整代码

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class ProcedrualAnimationController : MonoBehaviour
{
    public Transform target1;
    public Transform target2;
    public Transform target3;
    public Transform target4;

    public Transform point1;
    public Transform point2;
    public Transform point3;
    public Transform point4;

    // 标记每组腿是否正在移动
    bool isGroup1Moving;
    bool isGroup2Moving;
    public float legMoveSpeed = 7f;
    // 触发腿移动的距离阈值
    public float moveThreshold = 5f;

    void Update()
    {
        // 更新所有腿目标点的地面贴合位置
        UpdateLegTargetGroundPosition(target1);
        UpdateLegTargetGroundPosition(target2);
        UpdateLegTargetGroundPosition(target3);
        UpdateLegTargetGroundPosition(target4);

        // 更新所有移动目标点的地面贴合位置
        UpdateMovePointGroundPosition(point1);
        UpdateMovePointGroundPosition(point2);
        UpdateMovePointGroundPosition(point3);
        UpdateMovePointGroundPosition(point4);

        // 计算每条腿到对应移动点的距离
        float dist1 = Vector3.Distance(target1.position, point1.position);
        float dist2 = Vector3.Distance(target2.position, point2.position);
        float dist3 = Vector3.Distance(target3.position, point3.position);
        float dist4 = Vector3.Distance(target4.position, point4.position);

        // 处理第一组腿(target1、target2)的移动触发逻辑
        if (!isGroup1Moving && !isGroup2Moving && (dist1 >= moveThreshold || dist2 >= moveThreshold))
        {
            // 检查对侧组(第二组)腿是否全部着地
            bool isGroup2Grounded = IsLegGrounded(target3) && IsLegGrounded(target4);
            if (isGroup2Grounded)
            {
                StartCoroutine(MoveGroup1());
            }
        }

        // 处理第二组腿(target3、target4)的移动触发逻辑
        if (!isGroup2Moving && !isGroup1Moving && (dist3 >= moveThreshold || dist4 >= moveThreshold))
        {
            // 检查对侧组(第一组)腿是否全部着地
            bool isGroup1Grounded = IsLegGrounded(target1) && IsLegGrounded(target2);
            if (isGroup1Grounded)
            {
                StartCoroutine(MoveGroup2());
            }
        }
    }

    // 复用方法:更新腿目标点的地面贴合位置
    void UpdateLegTargetGroundPosition(Transform target)
    {
        RaycastHit hit;
        if (Physics.Raycast(target.position + new Vector3(0f, 0.75f, 0f), target.TransformDirection(Vector3.down), out hit, Mathf.Infinity))
        {
            target.position = hit.point;
        }
    }

    // 复用方法:更新移动目标点的地面贴合位置
    void UpdateMovePointGroundPosition(Transform point)
    {
        RaycastHit hit;
        if (Physics.Raycast(point.position + new Vector3(0f, 0.75f, 0f), point.TransformDirection(Vector3.back), out hit, Mathf.Infinity))
        {
            point.position = hit.point;
        }
    }

    // 检查单条腿是否着地的方法
    bool IsLegGrounded(Transform legTarget)
    {
        // 短射线检测腿是否接触地面,可根据需求调整检测范围
        RaycastHit hit;
        return Physics.Raycast(legTarget.position + new Vector3(0, 0.1f, 0), Vector3.down, out hit, 0.2f);
    }

    // 协程:平滑移动第一组腿
    IEnumerator MoveGroup1()
    {
        isGroup1Moving = true;

        // 平滑移动target1到point1
        while (Vector3.Distance(target1.position, point1.position) > 0.01f)
        {
            target1.position = Vector3.MoveTowards(target1.position, point1.position, legMoveSpeed * Time.deltaTime);
            yield return null;
        }
        target1.position = point1.position;

        // 平滑移动target2到point2
        while (Vector3.Distance(target2.position, point2.position) > 0.01f)
        {
            target2.position = Vector3.MoveTowards(target2.position, point2.position, legMoveSpeed * Time.deltaTime);
            yield return null;
        }
        target2.position = point2.position;

        isGroup1Moving = false;
    }

    // 协程:平滑移动第二组腿
    IEnumerator MoveGroup2()
    {
        isGroup2Moving = true;

        // 平滑移动target3到point3
        while (Vector3.Distance(target3.position, point3.position) > 0.01f)
        {
            target3.position = Vector3.MoveTowards(target3.position, point3.position, legMoveSpeed * Time.deltaTime);
            yield return null;
        }
        target3.position = point3.position;

        // 平滑移动target4到point4
        while (Vector3.Distance(target4.position, point4.position) > 0.01f)
        {
            target4.position = Vector3.MoveTowards(target4.position, point4.position, legMoveSpeed * Time.deltaTime);
            yield return null;
        }
        target4.position = point4.position;

        isGroup2Moving = false;
    }
}

核心修改说明

  1. 状态管控:新增isGroup1Moving和isGroup2Moving变量,确保同一时间只有一组腿处于移动状态,避免步态冲突
  2. 着地校验:新增IsLegGrounded方法,通过短射线精确判断腿是否着地,作为对侧组移动的前置条件
  3. 平滑移动:使用协程结合Vector3.MoveTowards实现自然的腿移动动画,替代原有的瞬间位置赋值
  4. 代码复用:将重复的射线检测逻辑封装为独立方法,减少冗余代码
  5. 触发逻辑优化:仅当某组腿达到移动距离阈值,且对侧组完全着地时,才启动移动流程

你可以根据蜘蛛的实际尺寸,调整moveThreshold(触发移动的距离)和legMoveSpeed(腿移动速度)参数,让步态更符合生物自然性。

内容的提问来源于stack exchange,提问作者Z_Dev

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.25 05:55:28