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; } }
核心修改说明
- 状态管控:新增
isGroup1Moving和isGroup2Moving变量,确保同一时间只有一组腿处于移动状态,避免步态冲突 - 着地校验:新增
IsLegGrounded方法,通过短射线精确判断腿是否着地,作为对侧组移动的前置条件 - 平滑移动:使用协程结合
Vector3.MoveTowards实现自然的腿移动动画,替代原有的瞬间位置赋值 - 代码复用:将重复的射线检测逻辑封装为独立方法,减少冗余代码
- 触发逻辑优化:仅当某组腿达到移动距离阈值,且对侧组完全着地时,才启动移动流程
你可以根据蜘蛛的实际尺寸,调整moveThreshold(触发移动的距离)和legMoveSpeed(腿移动速度)参数,让步态更符合生物自然性。
内容的提问来源于stack exchange,提问作者Z_Dev
相关产品推荐
相关产品推荐

