Unity玩家带碰撞体仍穿墙,网格移动遇墙反弹问题求助
解决Tilemap网格逐格移动的穿墙与碰撞反弹问题
核心问题分析
- 带Composite Collider 2D时穿墙:大概率是Composite Collider 2D的"Used By Composite"勾选逻辑错误,或是Tilemap碰撞体设置与玩家瞬间位移的移动逻辑不匹配,导致碰撞检测未及时触发。
- 移除Composite Collider 2D后反弹:直接修改
Transform.position会触发物理引擎的碰撞响应,刚体被被动弹开,而非预期的"遇墙即停"。
具体修复步骤
1. 恢复并正确配置Composite Collider 2D
- 给墙体Tilemap添加Tilemap Collider 2D,勾选Inspector面板中的
Used By Composite选项。 - 添加Composite Collider 2D,设置
Geometry Type为Outlines(适配Tilemap规整碰撞形状),确保Is Trigger未勾选。 - 玩家对象添加Circle Collider 2D(匹配白色圆形外观)和Rigidbody 2D,将Rigidbody 2D的
Body Type设为Kinematic(完全由代码控制移动,避免物理引擎主动推动)。
2. 修改逐格移动逻辑:先检测再移动
放弃直接修改Transform.position,改为先检测目标格子是否可通行,再执行平滑移动。示例代码框架如下:
using UnityEngine; using UnityEngine.Tilemaps; public class PlayerMovement : MonoBehaviour { public Tilemap obstacleTilemap; // 赋值墙体所在Tilemap public float moveTimePerGrid = 0.2f; private bool isMoving = false; private Vector3Int currentGridPos; void Start() { currentGridPos = obstacleTilemap.WorldToCell(transform.position); } void Update() { if (isMoving) return; Vector3Int targetGridPos = currentGridPos; if (Input.GetKeyDown(KeyCode.W)) targetGridPos += Vector3Int.up; else if (Input.GetKeyDown(KeyCode.S)) targetGridPos += Vector3Int.down; else if (Input.GetKeyDown(KeyCode.A)) targetGridPos += Vector3Int.left; else if (Input.GetKeyDown(KeyCode.D)) targetGridPos += Vector3Int.right; // 检测目标格子是否存在墙体Tile if (targetGridPos != currentGridPos && !obstacleTilemap.HasTile(targetGridPos)) { StartCoroutine(MoveToTargetGrid(targetGridPos)); } } IEnumerator MoveToTargetGrid(Vector3Int targetGridPos) { isMoving = true; // 将格子坐标转换为世界空间的中心位置 Vector3 targetWorldPos = obstacleTilemap.CellToWorld(targetGridPos) + obstacleTilemap.cellSize / 2; float elapsedTime = 0f; Vector3 startPos = transform.position; while (elapsedTime < moveTimePerGrid) { transform.position = Vector3.Lerp(startPos, targetWorldPos, elapsedTime / moveTimePerGrid); elapsedTime += Time.deltaTime; yield return null; } transform.position = targetWorldPos; currentGridPos = targetGridPos; isMoving = false; } }
3. 校验碰撞矩阵与Layer设置
- 进入
Edit > Project Settings > Physics 2D,确认玩家Layer与墙体Layer的碰撞矩阵处于互相碰撞状态。 - 玩家和墙体的碰撞体均不要勾选
Is Trigger(除非有特殊触发需求,此场景无需开启)。
4. 物理碰撞模式下的反弹修复(可选)
若坚持使用物理碰撞而非格子检测,需调整Rigidbody 2D参数并改用rb.MovePosition()控制移动:
- 将玩家Rigidbody 2D的
Linear Drag设为10以上,减少碰撞后的动量残留。 - 移动前添加射线检测,示例代码片段:
public LayerMask obstacleLayer; private Rigidbody2D rb; void Start() { rb = GetComponent<Rigidbody2D>(); } void Update() { if (isMoving) return; Vector2 direction = Vector2.zero; if (Input.GetKeyDown(KeyCode.W)) direction = Vector2.up; // 其余方向判断逻辑略 float gridSize = obstacleTilemap.cellSize.x; // 检测前方是否有墙体 RaycastHit2D hit = Physics2D.Raycast(transform.position, direction, gridSize, obstacleLayer); if (hit.collider == null) { StartCoroutine(MoveWithPhysics(direction * gridSize)); } } IEnumerator MoveWithPhysics(Vector2 moveDistance) { isMoving = true; Vector2 targetPos = rb.position + moveDistance; float elapsedTime = 0f; while (elapsedTime < moveTimePerGrid) { rb.MovePosition(Vector2.Lerp(rb.position, targetPos, elapsedTime / moveTimePerGrid)); elapsedTime += Time.deltaTime; yield return null; } rb.MovePosition(targetPos); isMoving = false; }
内容的提问来源于stack exchange,提问作者user26436276
相关产品推荐
相关产品推荐

