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

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    
}

问题根源分析

  1. 双击Alpha1的双跳问题:

    • LaunchDices()在Update()每帧调用,双击时两次Input.GetKeyDown(KeyCode.Alpha1)都会触发,StopDiceMovement()仅重置速度,未阻止重复施加力,导致双跳。
    • 直接修改dice.position后立刻加力,物理引擎可能未同步位置,导致力的作用位置异常。
  2. 骰子从初始位置发射:

    • 骰子运动时,StopDiceMovement()直接设置dice.rotation = Quaternion.identity,未等物理引擎确认状态就设置位置,可能被物理引擎的碰撞或状态回滚覆盖。
  3. 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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.30 04:10:56