Unity骰子游戏Alpha1/Alpha2按键触发异常问题求助
骰子游戏Bug排查与修复
问题描述
开发Unity骰子游戏时遇到三个异常:
- 双击Alpha1键时骰子出现“双跳”,
StopDiceMovement()似乎未生效 - 偶尔骰子从初始静止位置而非随机投掷位置发射
- 按下Alpha2键时骰子仅停止运动,无法回到初始位置
核心问题脚本如下:
using System.Collections.Generic; using UnityEngine; public class BoardController : MonoBehaviour { #region VAR [SerializeField] private List<Transform> diceRestPositions = new List<Transform>(); [SerializeField] private List<Transform> dices = new List<Transform>(); [SerializeField] private Transform throwPosition; [SerializeField] private float throwRange; #endregion #region MONO private void Update() { LaunchDices(); if(Input.GetKeyDown(KeyCode.Alpha2)) { ResetDicesPos(); } } private void Start() { } #endregion #region CUSTOM private Vector3 GetRandomThrowPos() { return new Vector3(Random.Range(throwPosition.position.x- throwRange, throwPosition.position.x + throwRange), 1.2f, Random.Range(throwPosition.position.z - throwRange, throwPosition.position.z + throwRange)); } private void ResetDicesPos() { StopDiceMovement(); for (int i = 0; i < diceRestPositions.Count; i++) { dices[i].position = diceRestPositions[i].localPosition; dices[i].rotation = diceRestPositions[i].localRotation; } } private void StopDiceMovement() { foreach(Transform dice in dices) { if(dice.TryGetComponent<Rigidbody>(out Rigidbody rb)) { rb.velocity = Vector3.zero; dice.rotation = Quaternion.identity; rb.angularVelocity = Vector3.zero; } } } private void LaunchDices() { if(Input.GetKeyDown(KeyCode.Alpha1)) { StopDiceMovement(); foreach (Transform dice in dices) { dice.position = GetRandomThrowPos(); if(dice.TryGetComponent<DiceAddForce>(out DiceAddForce comp)) { comp.ApplyRandomForce(); } } } } #endregion }
问题根源分析
双击Alpha1的双跳问题:
LaunchDices()在Update()每帧调用,双击时两次Input.GetKeyDown(KeyCode.Alpha1)都会触发,StopDiceMovement()仅重置速度,未阻止重复施加力,导致双跳。- 直接修改
dice.position后立刻加力,物理引擎可能未同步位置,导致力的作用位置异常。
骰子从初始位置发射:
- 骰子运动时,
StopDiceMovement()直接设置dice.rotation = Quaternion.identity,未等物理引擎确认状态就设置位置,可能被物理引擎的碰撞或状态回滚覆盖。
- 骰子运动时,
Alpha2无法回到初始位置:
ResetDicesPos()使用diceRestPositions[i].localPosition(本地坐标)赋值给dices[i].position(世界坐标),若diceRestPositions父物体非世界原点,坐标会完全错误。
修复后的代码
using System.Collections.Generic; using UnityEngine; public class BoardController : MonoBehaviour { #region VAR [SerializeField] private List<Transform> diceRestPositions = new List<Transform>(); [SerializeField] private List<Transform> dices = new List<Transform>(); [SerializeField] private Transform throwPosition; [SerializeField] private float throwRange; // 标记是否正在处理投掷,防止重复触发 private bool isThrowing = false; #endregion #region MONO private void Update() { if(Input.GetKeyDown(KeyCode.Alpha1) && !isThrowing) { LaunchDices(); } if(Input.GetKeyDown(KeyCode.Alpha2)) { ResetDicesPos(); } } #endregion #region CUSTOM private Vector3 GetRandomThrowPos() { return new Vector3(Random.Range(throwPosition.position.x - throwRange, throwPosition.position.x + throwRange), 1.2f, Random.Range(throwPosition.position.z - throwRange, throwPosition.position.z + throwRange)); } private void ResetDicesPos() { StopDiceMovement(); for (int i = 0; i < diceRestPositions.Count; i++) { // 使用世界坐标而非本地坐标 dices[i].position = diceRestPositions[i].position; dices[i].rotation = diceRestPositions[i].rotation; } } private void StopDiceMovement() { foreach(Transform dice in dices) { if(dice.TryGetComponent<Rigidbody>(out Rigidbody rb)) { // 先设置刚体为运动学,避免物理引擎干扰位置设置 rb.isKinematic = true; rb.velocity = Vector3.zero; rb.angularVelocity = Vector3.zero; } } } private void LaunchDices() { isThrowing = true; StopDiceMovement(); foreach (Transform dice in dices) { if(dice.TryGetComponent<Rigidbody>(out Rigidbody rb)) { // 恢复动力学状态,允许物理模拟 rb.isKinematic = false; } dice.position = GetRandomThrowPos(); if(dice.TryGetComponent<DiceAddForce>(out DiceAddForce comp)) { comp.ApplyRandomForce(); } } // 延迟标记投掷结束,防止快速双击触发 Invoke(nameof(ResetThrowingFlag), 0.1f); } private void ResetThrowingFlag() { isThrowing = false; } #endregion }
关键修复点说明
- 防止重复触发:添加
isThrowing标记,配合Invoke延迟重置,避免双击Alpha1时重复执行投掷逻辑。 - 坐标系统修正:
ResetDicesPos()中改用diceRestPositions[i].position(世界坐标)设置骰子位置,解决无法回到初始位置的问题。 - 刚体状态控制:
StopDiceMovement()中先将刚体设为运动学(isKinematic = true),避免物理引擎干扰位置和旋转的设置;投掷前恢复动力学状态,确保力能正常作用。 - 移除错误旋转设置:删除
StopDiceMovement()中dice.rotation = Quaternion.identity的硬编码,改用初始位置的旋转,避免干扰后续位置设置。
内容的提问来源于stack exchange,提问作者Wiktor Mazurkiewicz
相关产品推荐
相关产品推荐

