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

Unity中点击箭头控制玩家左右移动的脚本问题求助

问题排查与修复:Unity玩家移动脚本问题

核心问题分析

  • 无限循环导致卡顿崩溃:ArrowClick方法中的while(true)是无限死循环,会完全阻塞Unity主线程,导致游戏无法进行帧更新,直接引发卡顿甚至强制关闭。
  • 速度计算逻辑错误:FixedUpdate中currentSpeed = isRunning ? speed * runSpeed : speed的计算逻辑错误,speed=500f乘以runSpeed=200f会得到100000的超大速度,而未触发奔跑时speed=500f又可能因单位问题显得过慢;同时箭头点击移动时,Vector2.MoveTowards未使用时间增量,导致移动速度不恒定且与物理系统冲突。
  • 移动方式混用冲突:同时使用Rigidbody2D.MovePosition(物理驱动)和直接修改transform.position(直接坐标修改),两种移动逻辑相互干扰,导致移动行为异常。
  • 缺少屏幕边界停止逻辑:原代码未实现玩家移出屏幕后停止移动的判断。

修复方案与优化代码

修复要点

  1. 用协程替代死循环处理箭头点击后的移动,避免阻塞主线程。
  2. 修正速度倍率逻辑,将runSpeed改为速度倍率(如2f),保证速度计算合理。
  3. 统一使用Rigidbody2D处理所有移动,避免逻辑冲突。
  4. 添加屏幕边界检测,当玩家移出屏幕时停止移动。
  5. 优化代码结构,移除冗余逻辑,保持简洁。

修复后的完整代码

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

public class PlayerManager : MonoBehaviour
{
    public float moveSpeed = 5f; // 基础移动速度,单位:单位/秒
    public float runMultiplier = 2f; // 奔跑速度倍率

    public GameObject player;
    public GameObject rightArrow;
    public GameObject leftArrow;

    private Vector2 movement;
    private bool isRunning;
    private bool isArrowMoving; // 标记是否处于箭头点击移动状态
    private Animator anim;
    private Rigidbody2D rb;
    private Camera mainCamera;

    private void Start()
    {
        anim = player.GetComponent<Animator>();
        rb = player.GetComponent<Rigidbody2D>();
        mainCamera = Camera.main;
    }

    private void Update()
    {
        // 仅在非箭头移动状态下处理方向键输入
        if (!isArrowMoving)
        {
            movement.x = Input.GetAxisRaw("Horizontal");
            movement.y = Input.GetAxisRaw("Vertical");
            isRunning = Input.GetKey(KeyCode.LeftShift) || Input.GetKey(KeyCode.RightShift);
        }
        else
        {
            // 箭头移动时重置方向键输入,避免动画异常
            movement = Vector2.zero;
        }

        // 处理箭头点击
        if (Input.GetMouseButtonDown(0))
        {
            Vector2 worldPoint = mainCamera.ScreenToWorldPoint(Input.mousePosition);
            RaycastHit2D hit = Physics2D.Raycast(worldPoint, Vector2.zero);

            if (hit.collider != null)
            {
                if (hit.collider.gameObject == rightArrow)
                {
                    StartCoroutine(MovePlayerToDirection(1));
                }
                else if (hit.collider.gameObject == leftArrow)
                {
                    StartCoroutine(MovePlayerToDirection(-1));
                }
            }
        }
    }

    private void FixedUpdate()
    {
        if (!isArrowMoving)
        {
            // 方向键移动逻辑
            float currentSpeed = isRunning ? moveSpeed * runMultiplier : moveSpeed;
            Vector2 velocity = movement.normalized * currentSpeed;
            rb.MovePosition(rb.position + velocity * Time.fixedDeltaTime);
        }
    }

    private void LateUpdate()
    {
        // 更新动画参数
        anim.SetFloat("DirX", movement.x);
        anim.SetFloat("DirY", movement.y);
        anim.SetBool("Idle", movement.magnitude == 0);
    }

    // 协程处理箭头点击后的定向移动
    private IEnumerator MovePlayerToDirection(int direction)
    {
        isArrowMoving = true;
        float moveSpeed = this.moveSpeed; // 使用基础移动速度

        while (true)
        {
            // 计算玩家当前屏幕位置
            Vector3 screenPos = mainCamera.WorldToViewportPoint(player.transform.position);
            // 检测是否移出屏幕左右边界
            if (direction == 1 && screenPos.x > 1.1f) // 右侧超出屏幕
            {
                break;
            }
            if (direction == -1 && screenPos.x < -0.1f) // 左侧超出屏幕
            {
                break;
            }

            // 通过Rigidbody2D移动玩家
            Vector2 moveVelocity = new Vector2(direction * moveSpeed, 0);
            rb.MovePosition(rb.position + moveVelocity * Time.fixedDeltaTime);

            yield return null; // 等待下一帧,避免阻塞
        }

        isArrowMoving = false;
    }
}

代码说明

  • 协程移动:MovePlayerToDirection协程逐帧处理移动,不会阻塞主线程,同时通过yield return null保证游戏正常运行。
  • 屏幕边界检测:使用WorldToViewportPoint将世界坐标转换为屏幕视口坐标(范围0-1),当玩家超出视口范围时停止移动。
  • 移动状态互斥:通过isArrowMoving标记避免方向键移动与箭头点击移动同时触发,防止逻辑冲突。
  • 速度逻辑修正:将runSpeed改为runMultiplier作为倍率,基础速度设为合理的5f(单位/秒),符合Unity常规移动速度设置。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.21 04:36:05