Unity中解决可推动刚体卡在不可推动刚体的问题
推箱子场景玩家、箱子与障碍物卡死问题解决方案
问题根源
- 直接修改
transform.position移动玩家,绕过了Unity物理引擎的碰撞响应逻辑,导致碰撞时无法正确触发物理反馈 IsWalkable方法仅判断目标点是否存在碰撞,未区分可推动箱子和静态障碍物,当箱子被障碍物挡住时,玩家仍会尝试移动,三者互相挤压卡死- 原代码中
Move协程定义在Update方法内部,属于语法错误,会导致运行异常
解决方案步骤
1. 分层管理碰撞对象
- 在Unity编辑器新建
Pushable层,把所有可移动箱子设置到该层 - 原
collisionsLayer仅保留静态障碍物(比如碰撞瓦片地图),在PlayerController面板新增pushableLayer变量并赋值为Pushable层
2. 修正代码逻辑与结构
将Move协程移到Update方法外,改用刚体移动替代直接修改transform,同时新增推箱子的判断逻辑:
using System.Collections; using System.Collections.Generic; using UnityEngine; public class PlayerController : MonoBehaviour { public float moveSpeed; public LayerMask collisionsLayer; public LayerMask pushableLayer; // 新增:可推动物体层 private bool isMoving; private Vector2 input; private Animator animator; private Rigidbody2D rb; // 新增:引用玩家刚体 private void Awake() { animator = GetComponent<Animator>(); rb = GetComponent<Rigidbody2D>(); // 获取玩家刚体组件 } private void Update() { if (!isMoving) { input.x = Input.GetAxisRaw("Horizontal"); input.y = Input.GetAxisRaw("Vertical"); // 禁止斜向移动 if (input.x != 0) input.y = 0; if (input != Vector2.zero) { // 更新动画参数 animator.SetFloat("moveX", input.x); animator.SetFloat("moveY", input.y); Vector3 playerTarget = transform.position + new Vector3(input.x, input.y, 0); // 检查目标点是否有可推动箱子 Collider2D hitBox = Physics2D.OverlapCircle(playerTarget, 0.1f, pushableLayer); if (hitBox != null) { // 计算箱子的目标位置 Vector3 boxTarget = hitBox.transform.position + new Vector3(input.x, input.y, 0); // 检查箱子目标位置是否无障碍物 if (IsWalkable(boxTarget)) { // 同时移动玩家和箱子 StartCoroutine(MoveWithPush(playerTarget, hitBox.transform, boxTarget)); } // 箱子被挡住时,不允许玩家移动 } else if (IsWalkable(playerTarget)) { // 无箱子,正常移动玩家 StartCoroutine(Move(playerTarget)); } } } // 更新移动状态动画 animator.SetBool("isMoving", isMoving); } // 普通移动协程 IEnumerator Move(Vector3 targetPos) { isMoving = true; while ((targetPos - transform.position).sqrMagnitude > Mathf.Epsilon) { // 使用刚体移动,让物理引擎处理碰撞 rb.MovePosition(Vector3.MoveTowards(transform.position, targetPos, moveSpeed * Time.deltaTime)); yield return null; } rb.MovePosition(targetPos); isMoving = false; } // 带推箱子的移动协程 IEnumerator MoveWithPush(Vector3 playerTarget, Transform boxTrans, Vector3 boxTarget) { isMoving = true; while ((playerTarget - transform.position).sqrMagnitude > Mathf.Epsilon) { rb.MovePosition(Vector3.MoveTowards(transform.position, playerTarget, moveSpeed * Time.deltaTime)); boxTrans.position = Vector3.MoveTowards(boxTrans.position, boxTarget, moveSpeed * Time.deltaTime); yield return null; } rb.MovePosition(playerTarget); boxTrans.position = boxTarget; isMoving = false; } // 检查目标位置是否无静态障碍物 private bool IsWalkable(Vector3 targetPos) { return Physics2D.OverlapCircle(targetPos, 0.1f, collisionsLayer) == null; } }
3. 调整刚体配置
- 玩家刚体:保持
Dynamic类型,碰撞检测模式改为Continuous,避免快速移动穿透 - 箱子刚体:设置为
Dynamic类型,碰撞检测模式改为Continuous,勾选Freeze Rotation Z防止旋转 - 静态障碍物(碰撞瓦片地图):保持
Static类型不变
内容的提问来源于stack exchange,提问作者Nes Quake
相关产品推荐
相关产品推荐

