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

Unity 2D玩家碰撞墙体时抖动及自动移动问题求助

解决2D URP顶视角射击游戏玩家碰撞墙体抖动、无输入漂移及朝向偏移问题

问题描述

我正在Unity中制作一款2D URP顶视角射击游戏。当玩家以特定方式(沿墙体滑动或碰撞墙体角落)与墙体碰撞时,会出现抖动现象,且无输入时会缓慢移动,还会朝向鼠标方向偏移。

玩家脚本

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.InputSystem;

public class PlayerMovement : MonoBehaviour
{
    [SerializeField]
    private float movespeed;

    [SerializeField]
    private float rotspeed;

    [SerializeField]
    private Camera cam;

    [SerializeField]
    private Animator animator;

    private Rigidbody2D rb;
    private Vector2 moveInput;

    private void Awake()
    { 
        rb = GetComponent<Rigidbody2D>();
    }

    private void Update()
    {
        animator.SetFloat("Speed", rb.velocity.sqrMagnitude);
    }
    private void FixedUpdate()
    {
        SetPlayerVel();
        RotateInDirectionofCursor();
    }

    private void SetPlayerVel()
    {
        rb.velocity = moveInput * movespeed * Time.deltaTime;
    }

    private void RotateInDirectionofCursor()
    {
       Vector2 mouseWorldPos = cam.ScreenToWorldPoint(Input.mousePosition);
        transform.up = (mouseWorldPos - new Vector2(transform.position.x, transform.position.y)) * -1;
    }

    private void OnMove(InputValue inputValue)
    {
        moveInput = inputValue.Get<Vector2>();
    }
}

相关组件设置

玩家

  • Rigidbody2D:Body Type (Dynamic)、Collision Detection (Continuous)、Interpolate (Interpolate)
  • Box Collider 2D
  • Player Input

墙体(位于Tilemap网格下)

  • Tilemap Collider 2D
  • Rigidbody2D:Body Type (Static)

我尝试改用rb.MovePosition实现移动,也尝试编写脚本处理墙体碰撞,但都无效。由于我对Unity不够熟练,可能没找到问题根源。


问题根源分析

  1. 移动逻辑错误:直接给rb.velocity赋值时乘以Time.deltaTime是错误的——Rigidbody2D的velocity本身是基于每秒的速度,乘以deltaTime会导致速度被错误缩放,无输入时可能残留微小速度导致漂移。
  2. 旋转与物理帧冲突:在FixedUpdate中直接修改transform.up会干扰物理引擎的碰撞处理,导致碰撞时抖动;同时直接赋值旋转会忽略物理约束,和刚体运动脱节。
  3. 碰撞检测与插值配合问题:Tilemap Collider的网格特性加上刚体插值设置,若移动逻辑不匹配,容易在角落或滑动时出现抖动。

具体修复方案

1. 修正移动逻辑

移除Time.deltaTime,并在无输入时直接归零速度,避免漂移:

private void SetPlayerVel()
{
    // 无输入时直接归零速度,防止漂移
    if (moveInput.magnitude < 0.01f)
    {
        rb.velocity = Vector2.zero;
        return;
    }
    // 直接设置速度(velocity本身是每秒单位,无需乘deltaTime)
    rb.velocity = moveInput.normalized * movespeed;
}

注意:如果需要平滑加速/减速,可以用rb.AddForce替代直接赋值velocity,或者用Lerp过渡速度,避免瞬间速度变化导致的碰撞抖动。

2. 分离旋转逻辑到Update,并使用平滑旋转

将旋转移到Update(因为鼠标输入是帧同步的),并使用Quaternion.RotateTowards实现平滑旋转,避免直接修改transform干扰物理:

private void Update()
{
    animator.SetFloat("Speed", rb.velocity.sqrMagnitude);
    RotateInDirectionofCursor(); // 移到Update处理
}

private void FixedUpdate()
{
    SetPlayerVel(); // 物理相关逻辑留在FixedUpdate
}

private void RotateInDirectionofCursor()
{
    Vector2 mouseWorldPos = cam.ScreenToWorldPoint(Input.mousePosition);
    Vector2 direction = mouseWorldPos - (Vector2)transform.position;
    // 计算目标朝向的角度(适配up方向的偏移)
    float targetAngle = Mathf.Atan2(direction.y, direction.x) * Mathf.Rad2Deg - 90f;
    // 平滑旋转到目标角度
    transform.rotation = Quaternion.RotateTowards(transform.rotation, Quaternion.Euler(0, 0, targetAngle), rotspeed * Time.deltaTime);
}

3. 优化碰撞设置

  • 给玩家的Box Collider 2D添加小范围的Edge Radius(比如0.05-0.1),减少角落碰撞时的尖锐接触,缓解抖动。
  • 若Tilemap Collider是复合碰撞,确保启用Used By Composite,并检查Tilemap的碰撞瓦片是否有重叠或异常。
  • 尝试将玩家Rigidbody2D的Interpolate改为Extrapolate,或者暂时关闭插值测试是否是插值导致的抖动。

4. 额外优化:处理碰撞后的速度修正

如果滑动时仍有抖动,可以在OnCollisionStay2D中修正速度,避免刚体被墙体"弹回"导致的抖动:

private void OnCollisionStay2D(Collision2D collision)
{
    if (collision.gameObject.CompareTag("Wall"))
    {
        // 获取碰撞法线,将速度沿法线方向的分量归零
        Vector2 normal = collision.contacts[0].normal;
        rb.velocity = Vector2.Project(rb.velocity, Vector2.Perpendicular(normal));
    }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.26 20:12:02