You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Kinematic Rigidbody2D角色穿墙问题:近距离向墙移动碰撞失效

问题分析与解决方案

核心问题

  1. Raycast检测方向滞后:原代码中Raycast使用的是上一次的direction变量,而非用户当前输入的移动方向,导致紧贴墙体时,输入朝向墙体的方向后,Raycast未更新到新方向,无法检测到障碍物,进而错误允许移动。
  2. 移动逻辑绕过物理检测:直接通过transform.position插值移动Kinematic刚体,未利用Rigidbody2D的物理系统,且协程移动过程中未做碰撞校验,一旦启动移动就会执行到底。
  3. 状态重置不完整:仅在检测到障碍物时修改canMove状态,无碰撞场景下未主动重置canMove = true,可能导致状态异常。

修复方案

1. 实时用输入方向做Raycast检测

将Raycast的方向替换为当前用户输入的moveInput,确保每次检测都是针对即将移动的方向。

2. 使用Rigidbody2D.MovePosition处理移动

对于Kinematic刚体,官方推荐使用rb.MovePosition而非直接修改transform.position,这样能更好地与2D物理系统交互,同时在移动过程中可以加入碰撞检测。

3. 完善状态管理

  • 每次检测后主动重置canMove状态,避免状态残留。
  • 在协程移动过程中加入二次碰撞检测,防止移动途中出现碰撞遗漏。

修改后的完整代码

private bool isMoving;
public bool canMove;
private Vector2 originPos, targetPos;
public float timeToMove = 0.3f;
public Transform rayOrigin;
public float size;
public Vector2 direction;
private Rigidbody2D rb;

// assign the actions asset to this field in the inspector:
public InputActionAsset actions;
// private field to store move action reference
private InputAction moveAction;

void Awake()
{
    // find the "move" action, and keep the reference to it, for use in Update
    moveAction = actions.FindActionMap("gameplay").FindAction("move");
}

void Start()
{
    canMove = true;
    rb = GetComponent<Rigidbody2D>();
}

void FixedUpdate()
{
    P_Input();
}

private void P_Input()
{
    // Read the move input first
    Vector2 moveInput = moveAction.ReadValue<Vector2>();
    moveInput = moveInput.normalized; // 确保输入方向是单位向量

    // 用当前输入方向做Raycast检测
    RaycastHit2D hit = Physics2D.Raycast(rayOrigin.position, moveInput, size, ~(1 << 8));
    Debug.DrawRay(rayOrigin.position, moveInput * size, Color.green);

    // 重置canMove为默认允许,再根据碰撞修改
    canMove = true;
    if (hit.collider != null && hit.collider.CompareTag("obstacle"))
    {
        // 仅当输入方向朝向障碍物时,禁止移动
        if (Vector2.Dot(moveInput, hit.normal) <= -0.9f) // 方向几乎相反(朝向障碍物)
        {
            canMove = false;
        }
    }

    if (canMove && !isMoving && moveInput.magnitude > 0.1f) // 输入有效时才处理
    {
        direction = moveInput;
        StartCoroutine(MovePlayer(moveInput));
    }
}

private IEnumerator MovePlayer(Vector2 moveDirection)
{
    isMoving = true;
    float elapseTime = 0;
    originPos = rb.position;
    targetPos = originPos + moveDirection;

    while (elapseTime < timeToMove)
    {
        // 移动过程中再次检测碰撞,防止中途出现障碍物
        RaycastHit2D midHit = Physics2D.Raycast(rayOrigin.position, moveDirection, size, ~(1 << 8));
        if (midHit.collider != null && midHit.collider.CompareTag("obstacle"))
        {
            canMove = false;
            break;
        }

        // 用Rigidbody2D.MovePosition移动Kinematic刚体
        float t = elapseTime / timeToMove;
        Vector2 newPos = Vector2.Lerp(originPos, targetPos, t);
        rb.MovePosition(newPos);
        
        elapseTime += Time.deltaTime;
        yield return null;
    }

    // 如果移动被中断,回到起点;否则到达目标点
    if (!canMove)
    {
        rb.MovePosition(originPos);
    }
    else
    {
        rb.MovePosition(targetPos);
    }
    
    isMoving = false;
    canMove = true; // 重置移动状态
}

void OnEnable()
{
    actions.FindActionMap("gameplay").Enable();
}

void OnDisable()
{
    actions.FindActionMap("gameplay").Disable();
}

关键修改说明

  • Raycast方向同步:现在每次检测都使用当前的moveInput,确保检测的是用户想要移动的方向,解决了滞后问题。
  • 物理移动方式:改用rb.MovePosition处理Kinematic刚体的移动,符合Unity物理规范,避免直接修改transform导致的物理交互失效。
  • 移动中碰撞校验:在协程的移动循环中加入二次Raycast,即使移动中途出现障碍物也能及时中断。
  • 状态逻辑优化:每次输入检测时先重置canMove为true,再根据碰撞结果修改,避免状态残留;移动结束后主动重置状态。

内容的提问来源于stack exchange,提问作者TakumiTakumi

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.29 19:23:18