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

Unity 2D角色右移正常左移卡顿问题排查

问题现象
  • 角色向右移动时运行流畅,向左移动时出现明显卡顿
  • 禁用相机跟随逻辑后卡顿问题未消失,排除相机导致的抖动可能
  • 左右移动的核心移动逻辑代码完全对称,无法直接定位故障点
  • 移动、跳跃相关参数均从仅做变量定义的独立脚本获取,排除参数脚本的业务逻辑干扰

原实现代码如下:

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

public class InputManager : MonoBehaviour
{
    Rigidbody2D rb;
    float jumpForce;
    float downForce;
    float speed;
    float maxSpeed;
    float maxJumps;
    float currentJumps;
    float cutJumpHeight;
    float jumpBufferTimer;
    float jumpBuffer;
    float jumpRememberTimer;
    float jumpRemember;
    float x;
    float y;
    bool isGrounded = false;
    AudioSource jumpSound;
    Jump jumpScript;
    Movement movementScript;
    GameObject[] groundCheckPoints;


    void Start()
    {
        jumpScript = GetComponent<Jump>();
        movementScript = GetComponent<Movement>();
        rb = GetComponent<Rigidbody2D>();
        groundCheckPoints = GameObject.FindGameObjectsWithTag("Check Point");
    }

    void Update()
    {
        MyInput();
        IsGrounded();
        FlipCharacter();
        jumpForce = jumpScript.jumpForce;
        speed = movementScript.speed;
        maxJumps = jumpScript.maxJumps;
        cutJumpHeight = jumpScript.cutJumpHeight;
        jumpRemember = jumpScript.jumpRememberTime;
        jumpBuffer = jumpScript.jumpBufferTime;
        jumpSound = jumpScript.jumpSound;
    }

    void FlipCharacter()
    {
        if (x > 0)
        {
            transform.rotation = Quaternion.Euler(0, 0, 0);
        }else if(x < 0)
        {
            transform.rotation = Quaternion.Euler(0, 180, 0);
        }
    }

    void IsGrounded()
    {
        for (int i = 0; i < groundCheckPoints.Length; i++)
        {
            isGrounded = Physics2D.OverlapCircle(groundCheckPoints[i].transform.position, 0.1f, LayerMask.GetMask("Ground"));
            if (isGrounded)
            {
                break;
            }
        }
        Jump();
    }

    void FixedUpdate()
    {
            MovementPlatformer();
    }

    void MyInput()
    {
            x = Input.GetAxisRaw("Horizontal");
            y = Input.GetAxisRaw("Vertical");
    }

    void Jump()
    {
        jumpBufferTimer -= Time.deltaTime;
        jumpRememberTimer -= Time.deltaTime;

        if (isGrounded)
        {
            currentJumps = maxJumps;
        }

        if (currentJumps > 0)
        {
            jumpBufferTimer = jumpBuffer;
        }
        
        if (Input.GetKeyDown(KeyCode.Space))
        {
            jumpRememberTimer = jumpRemember;
        }

        if (jumpBufferTimer > 0 && jumpRememberTimer > 0)
        {
            jumpBufferTimer = 0;
            jumpRememberTimer = 0;
            jumpSound.Play();

            rb.velocity = new Vector2(rb.velocity.x, jumpForce);
            currentJumps--;
        }


        if (Input.GetKeyUp(KeyCode.Space) && rb.velocity.y > 0)
        {
            rb.velocity = new Vector2(rb.velocity.x, rb.velocity.y * cutJumpHeight);
        }
    }

    void MovementPlatformer()
    {
        rb.velocity = new Vector2(speed * x, rb.velocity.y);
    }

}
故障根因

卡顿核心出在角色翻转逻辑和物理更新不同步,附带的隐藏逻辑bug会放大异常:

  1. 你在Update()(每帧执行,和物理更新频率不匹配)中直接修改transform.rotation实现翻转,没有通过刚体接口操作。Unity2D物理引擎会将所有非Rigidbody接口触发的Transform修改判定为外部强制篡改,每次检测到这类修改都会强制重置刚体内部物理状态、重新计算所有碰撞接触,和物理更新的速度设置产生冲突。
  2. 右移不卡顿是因为角色初始旋转就是Quaternion.Euler(0,0,0),右移时每帧重复设置的旋转值和物理引擎缓存的初始值完全一致,不会触发物理状态重算;左移时设置的Quaternion.Euler(0,180,0)会持续触发物理重算逻辑,最终表现为移动卡顿。
  3. 落地检测存在逻辑bug:循环检测地面时会逐点覆盖isGrounded的值,如果遍历顺序靠后的检测点未检测到地面,即使靠前的检测点已经判定落地,最终isGrounded也会被覆盖为false。左移翻转后如果检测点位置出现偏移,落地状态会频繁在true/false之间跳变,进一步干扰刚体运动状态。
修复方案

按以下步骤修改代码即可解决问题:

  1. 优先使用SpriteRenderer.flipX实现角色翻转,完全不触碰Transform和刚体旋转属性,从根源避免物理同步问题。
  2. 修复IsGrounded()的逻辑bug,检测前先将isGrounded初始化为false,只要任意一个检测点判定落地就保持true状态,不会被后续检测点覆盖。
  3. (可选优化)不要每帧重复设置相同的翻转状态,加状态判断减少不必要的计算。

修复后的核心代码片段:

public class InputManager : MonoBehaviour
{
    // 原有变量保留,新增SpriteRenderer引用
    Rigidbody2D rb;
    SpriteRenderer sr;
    // 其余原有变量不变...

    void Start()
    {
        // 原有Start逻辑保留,新增SpriteRenderer初始化
        sr = GetComponent<SpriteRenderer>();
        jumpScript = GetComponent<Jump>();
        movementScript = GetComponent<Movement>();
        rb = GetComponent<Rigidbody2D>();
        // 注意:FindGameObjectsWithTag会查找全局所有同标签物体,建议改为Inspector直接拖拽赋值当前角色的子检测点,避免误取其他物体的检测点
        groundCheckPoints = GameObject.FindGameObjectsWithTag("Check Point");
    }

    void FlipCharacter()
    {
        if (x > 0)
        {
            sr.flipX = false;
        }else if(x < 0)
        {
            sr.flipX = true;
        }
    }

    void IsGrounded()
    {
        // 检测前先初始化落地状态为false
        isGrounded = false;
        for (int i = 0; i < groundCheckPoints.Length; i++)
        {
            if (Physics2D.OverlapCircle(groundCheckPoints[i].transform.position, 0.1f, LayerMask.GetMask("Ground")))
            {
                isGrounded = true;
                break;
            }
        }
        Jump();
    }

    // 其余原有逻辑不变...
}

如果必须通过旋转实现翻转,不要直接修改transform,将翻转逻辑移到FixedUpdate中,通过刚体接口执行旋转:

void FixedUpdate()
{
    MovementPlatformer();
    FlipCharacter();
}

void FlipCharacter()
{
    if (x > 0)
    {
        rb.MoveRotation(0);
    }else if(x < 0)
    {
        rb.MoveRotation(180);
    }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.30 19:45:58