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

Unity 3D轮盘赌游戏:如何避免函数单帧执行引发结算异常?

解决3D轮盘赌游戏落球数字延迟更新的问题

看起来你踩了Unity单线程逻辑的典型坑——同步阻塞导致物理更新被卡住,数字检测的时序完全错了。咱们一步步拆解问题,然后给你具体的修复方案:

问题根源

你的Spin()方法里用了while (ballController.isMoving)循环,这会直接阻塞Unity主线程。Unity的物理模拟(球的移动、碰撞检测)都是在每帧的逻辑更新之后才执行的,这个循环会死死占用主线程,直到isMoving变为false——但此时球根本没机会移动(物理更新还没跑),所以循环结束时你拿到的还是旧的落球数字,等这个方法执行完,后续帧球才真正开始运动,这就导致了"第一次显示0、第二次显示第一次数字"的延迟问题。

解决方案:用协程替代同步阻塞

Unity里处理这种需要等待异步事件(比如物理运动结束)的场景,**协程(Coroutine)**是标准解法,它不会阻塞主线程,能让物理系统正常运行。

步骤1:重构Spin为协程

把同步的Spin()改成返回IEnumerator的协程,用yield return来等待球停止,而不是死循环:

private IEnumerator SpinAndWaitForResult()
{
    audioSource.Stop();
    audioSource.Play();
    // 重置球状态并施加力
    ballController.SpinBall();

    // 等待球停止运动,这一步不会阻塞主线程
    yield return new WaitWhile(() => ballController.isMoving);

    // 球停稳后再获取正确的落球数字
    if (ballController.hasBallEnteredCollider)
    {
        lastNumbers = ballController.numberFallen;
        print(lastNumbers);

        // 把原来在BetSpinWheel里的后续逻辑移到这里,确保用最新数字计算
        int earnings = CalculateEarnings(currentBet.ToArray(), lastNumbers);
        UpdateBalance(earnings);
        lastBet.Clear();
        foreach(Bet bet in currentBet)
        {
            lastBet.Add(bet);
        }
        currentBet.Clear();
        updateUI();
        uiController.ChangeLastNumberText();
    }
}

步骤2:修改BetSpinWheel方法

原来的同步调用改成启动协程:

public void BetSpinWheel()
{
    uiController.repeatButton.interactable = true;
    // 启动协程,让物理系统能正常处理球的运动
    StartCoroutine(SpinAndWaitForResult());
}

步骤3:完善BallController的状态管理

你需要确保isMoving和hasBallEnteredCollider的状态更新逻辑正确,比如:

// 在BallController类中补充
private Rigidbody rb;
public bool isMoving;
public bool hasBallEnteredCollider;
public int numberFallen;
public Vector3 ballStartPosition;
public Vector3 forceToAdd;

private void Start()
{
    rb = GetComponent<Rigidbody>();
}

public void SpinBall()
{
    rb.constraints = RigidbodyConstraints.None;
    transform.position = ballStartPosition;
    rb.velocity = Vector3.zero;
    transform.rotation = Quaternion.Euler(0f, 0f, 0f);
    rb.AddForce(forceToAdd, ForceMode.Impulse);
    
    // 重置状态,避免沿用旧数据
    isMoving = true;
    hasBallEnteredCollider = false;
    numberFallen = 0;
}

private void FixedUpdate()
{
    // 检测球的速度,当速度足够小时判定为停止
    if (isMoving && rb.velocity.magnitude < 0.05f)
    {
        isMoving = false;
    }
}

// 假设数字区域用的是触发器Collider,根据你的实际情况调整
private void OnTriggerEnter(Collider other)
{
    if (other.CompareTag("NumberCollider")) // 给数字Collider加个标签
    {
        // 从Collider名称或其他方式解析数字
        if (int.TryParse(other.name, out int number))
        {
            numberFallen = number;
            hasBallEnteredCollider = true;
        }
    }
}

为什么这样能解决问题?

协程的yield return会暂停当前方法的执行,把主线程交还给Unity去处理物理更新、渲染等操作,直到球真正停止运动后,才会继续执行后续的数字检测和收益计算逻辑,完美解决了时序错位的问题。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.14 08:54:08