Unity中如何在无物理系统下实现带碰撞的3D立方体跳跃移动
无物理系统的3D平台跳跃实现方案
核心思路
完全摒弃Unity物理引擎(如Rigidbody),通过射线检测判断地面接触状态,手动计算跳跃位移,结合BoxCast实现移动碰撞检测,模拟3D平台游戏的跳跃与移动效果。
关键实现步骤
1. 地面检测(跳跃触发前提)
通过向立方体下方发射短射线,判断是否接触地面:
- 射线起点:立方体Collider的中心偏下位置(避免边角误判)
- 射线长度:略大于Collider半高+地面误差值(比如
collider.bounds.extents.y + 0.1f) - 检测层:指定地面层,过滤其他碰撞体
2. 跳跃逻辑实现
采用固定跳跃高度+时间控制的方式,模拟跳跃的上升与下落:
- 记录跳跃状态(
isJumping),仅在地面时允许触发跳跃 - 用计时器(
jumpTimer)控制跳跃周期,根据时间计算Y轴位移(比如用二次曲线模拟平滑跳跃) - 跳跃结束后自动回到地面状态
3. 移动与碰撞检测
使用Physics.BoxCast检测移动方向上的障碍物,避免穿模:
- 以立方体Collider为基准,向移动方向发射BoxCast
- 若检测到障碍物,则取消该方向的移动
- 移动时保持Y轴位置不变(除非跳跃中)
完整代码示例
using UnityEngine; public class PlatformerController : MonoBehaviour { [Header("移动设置")] public float moveSpeed = 5f; public LayerMask groundLayer; public LayerMask obstacleLayer; [Header("跳跃设置")] public float jumpHeight = 2f; public float jumpDuration = 0.5f; private BoxCollider boxCollider; private bool isGrounded; private bool isJumping; private float jumpTimer; private float startYPos; private void Awake() { boxCollider = GetComponent<BoxCollider>(); } private void Update() { // 地面检测 CheckGround(); // 跳跃输入 if (Input.GetKeyDown(KeyCode.Space) && isGrounded && !isJumping) { StartJump(); } // 处理跳跃过程 if (isJumping) { UpdateJump(); } // 移动输入 Vector3 input = new Vector3(Input.GetAxis("Horizontal"), 0, Input.GetAxis("Vertical")); if (input.magnitude > 0.1f) { input = input.normalized; Move(input); } } private void CheckGround() { // 计算射线起点和长度 Vector3 rayOrigin = transform.position + Vector3.down * (boxCollider.bounds.extents.y - 0.05f); float rayLength = 0.15f; // 发射射线检测地面 isGrounded = Physics.Raycast(rayOrigin, Vector3.down, rayLength, groundLayer); // 若落地则重置跳跃状态 if (isGrounded && isJumping) { isJumping = false; transform.position = new Vector3(transform.position.x, startYPos, transform.position.z); } } private void StartJump() { isJumping = true; jumpTimer = 0f; startYPos = transform.position.y; } private void UpdateJump() { jumpTimer += Time.deltaTime; float progress = jumpTimer / jumpDuration; // 用二次曲线计算Y轴位移,实现平滑跳跃 float yOffset = jumpHeight * 4 * progress * (1 - progress); transform.position = new Vector3(transform.position.x, startYPos + yOffset, transform.position.z); // 跳跃结束 if (progress >= 1f) { isJumping = false; // 强制触发一次地面检测,避免悬空 CheckGround(); } } private void Move(Vector3 direction) { // 计算移动后的目标位置 Vector3 targetPos = transform.position + direction * moveSpeed * Time.deltaTime; // 用BoxCast检测移动路径上的障碍物 Vector3 castCenter = boxCollider.bounds.center; Vector3 castSize = boxCollider.bounds.extents; float castDistance = Vector3.Distance(transform.position, targetPos); if (!Physics.BoxCast(castCenter, castSize, direction, transform.rotation, castDistance, obstacleLayer)) { // 无障碍物则移动 transform.position = targetPos; } } }
注意事项
- 确保地面和障碍物分别设置对应Layer,避免检测混乱
- 调整
jumpHeight和jumpDuration可以改变跳跃的手感 - 移动速度
moveSpeed可根据需求调整,建议配合输入平滑处理(如用Mathf.Lerp) - 射线长度和BoxCast参数需根据立方体Collider大小微调,避免误判或漏判
内容的提问来源于stack exchange,提问作者Redis
相关产品推荐
相关产品推荐

