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(直接坐标修改),两种移动逻辑相互干扰,导致移动行为异常。 - 缺少屏幕边界停止逻辑:原代码未实现玩家移出屏幕后停止移动的判断。
修复方案与优化代码
修复要点
- 用协程替代死循环处理箭头点击后的移动,避免阻塞主线程。
- 修正速度倍率逻辑,将
runSpeed改为速度倍率(如2f),保证速度计算合理。 - 统一使用Rigidbody2D处理所有移动,避免逻辑冲突。
- 添加屏幕边界检测,当玩家移出屏幕时停止移动。
- 优化代码结构,移除冗余逻辑,保持简洁。
修复后的完整代码
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
相关产品推荐
相关产品推荐

