Unity中3D地形球体沿2D路径点移动穿模问题求助
解决Unity球体沿路径移动时穿地形及Rigidbody无法移动的问题
一、球体穿入地形的核心原因
- 高度采样逻辑错误:你当前用
SampleHeight(transform.position)采样球体当前位置的地形高度,但如果球体已经穿入地形,采样值会偏低;且移动目标点的高度应基于路径点的X/Z坐标计算,而非球体当前位置。 - 固定偏移量不合理:硬编码的
+2f没有考虑球体自身半径,导致高度计算无法精准贴合地面。
二、添加Rigidbody后无法移动的原因
直接修改transform.position会绕过Unity物理引擎,和Rigidbody的控制逻辑冲突,导致球体无法响应移动指令。若要使用物理交互,必须采用引擎提供的物理移动方法。
三、两种可行的实现方案
方案1:不使用Rigidbody(纯动画式移动)
修改高度计算逻辑,用球体半径作为偏移量,基于路径点坐标采样地形高度:
using System.Collections; using System.Collections.Generic; using UnityEngine; using System.IO; public class WaypointMover : MonoBehaviour { [SerializeField] private float _WaypointDistance = 0.01f; // 到达下一个路点的判定距离 [SerializeField] private float _MovementSpeed = 6f; // 移动速度 private List<Vector2> _Waypoints; // 路点集合 private int _CurrentWaypointIndex; // 当前路点索引 private Vector2 _CurrentWaypoint; // 当前目标路点 private float _sphereRadius; // 球体碰撞体半径 void Start() { _Waypoints = new List<Vector2>(); // 补充你的路点加载逻辑(从Python导入数据) _CurrentWaypointIndex = 0; // 确保路点列表不为空再初始化 if (_Waypoints.Count > 0) { _CurrentWaypoint = _Waypoints[_CurrentWaypointIndex]; // 获取球体碰撞体实际半径(考虑缩放) _sphereRadius = GetComponent<SphereCollider>().radius * transform.localScale.y; // 初始化位置到第一个路点的正确高度 float terrainHeight = Terrain.activeTerrain.SampleHeight(new Vector3(_CurrentWaypoint.x, 0, _CurrentWaypoint.y)); transform.position = new Vector3(_CurrentWaypoint.x, terrainHeight + _sphereRadius, _CurrentWaypoint.y); _CurrentWaypointIndex++; } } void Update() { if (_Waypoints.Count == 0) return; // 计算当前球体在XZ平面到目标路点的距离 Vector2 currentXZ = new Vector2(transform.position.x, transform.position.z); if (Vector2.Distance(_CurrentWaypoint, currentXZ) < _WaypointDistance) { _CurrentWaypointIndex++; // 循环路点 _CurrentWaypointIndex %= _Waypoints.Count; _CurrentWaypoint = _Waypoints[_CurrentWaypointIndex]; } else { // 基于目标路点坐标采样地形高度 float targetTerrainHeight = Terrain.activeTerrain.SampleHeight(new Vector3(_CurrentWaypoint.x, 0, _CurrentWaypoint.y)); Vector3 targetPosition = new Vector3(_CurrentWaypoint.x, targetTerrainHeight + _sphereRadius, _CurrentWaypoint.y); // 平滑移动到目标位置 transform.position = Vector3.MoveTowards(transform.position, targetPosition, _MovementSpeed * Time.deltaTime); } // 让球体水平看向目标路点(避免上下摆动) Vector3 lookTarget = new Vector3(_CurrentWaypoint.x, transform.position.y, _CurrentWaypoint.y); transform.LookAt(lookTarget); } }
方案2:使用Rigidbody(支持物理交互)
改用Rigidbody.MovePosition控制移动,在FixedUpdate中处理物理逻辑:
using System.Collections; using System.Collections.Generic; using UnityEngine; using System.IO; public class WaypointMover : MonoBehaviour { [SerializeField] private float _WaypointDistance = 0.01f; // 到达下一个路点的判定距离 [SerializeField] private float _MovementSpeed = 6f; // 移动速度 private List<Vector2> _Waypoints; // 路点集合 private int _CurrentWaypointIndex; // 当前路点索引 private Vector2 _CurrentWaypoint; // 当前目标路点 private float _sphereRadius; // 球体碰撞体半径 private Rigidbody _rb; // 刚体组件 void Start() { _rb = GetComponent<Rigidbody>(); _Waypoints = new List<Vector2>(); // 补充你的路点加载逻辑(从Python导入数据) _CurrentWaypointIndex = 0; if (_Waypoints.Count > 0) { _CurrentWaypoint = _Waypoints[_CurrentWaypointIndex]; _sphereRadius = GetComponent<SphereCollider>().radius * transform.localScale.y; // 初始化刚体位置 float terrainHeight = Terrain.activeTerrain.SampleHeight(new Vector3(_CurrentWaypoint.x, 0, _CurrentWaypoint.y)); _rb.position = new Vector3(_CurrentWaypoint.x, terrainHeight + _sphereRadius, _CurrentWaypoint.y); _CurrentWaypointIndex++; } // 可选:不需要重力则关闭 // _rb.useGravity = false; } // 物理逻辑需在FixedUpdate中处理 void FixedUpdate() { if (_Waypoints.Count == 0) return; Vector2 currentXZ = new Vector2(_rb.position.x, _rb.position.z); if (Vector2.Distance(_CurrentWaypoint, currentXZ) < _WaypointDistance) { _CurrentWaypointIndex++; _CurrentWaypointIndex %= _Waypoints.Count; _CurrentWaypoint = _Waypoints[_CurrentWaypointIndex]; } else { float targetTerrainHeight = Terrain.activeTerrain.SampleHeight(new Vector3(_CurrentWaypoint.x, 0, _CurrentWaypoint.y)); Vector3 targetPosition = new Vector3(_CurrentWaypoint.x, targetTerrainHeight + _sphereRadius, _CurrentWaypoint.y); // 用刚体方法移动,兼容物理引擎 _rb.MovePosition(Vector3.MoveTowards(_rb.position, targetPosition, _MovementSpeed * Time.fixedDeltaTime)); } // 水平看向目标路点 Vector3 lookTarget = new Vector3(_CurrentWaypoint.x, _rb.position.y, _CurrentWaypoint.y); transform.LookAt(lookTarget); } }
额外注意事项
- 确保地形已启用
Terrain Collider(Unity地形默认自带,无需额外添加) - 若路点密度低,可对相邻路点做线性插值,提升移动平滑度
- 地形凹凸区域可适当增大
_WaypointDistance,避免频繁切换路点导致抖动
内容的提问来源于stack exchange,提问作者Xerrum
相关产品推荐
相关产品推荐

