unity3d 需要对每一帧进行布尔检查(更新调用),但是从布尔检查调用的函数应该只调用一次

thigvfpy  于 2022-12-27  发布在  其他
关注(0)|答案(3)|浏览(151)

所以我正在用摄像头做一个项目。如果我离开屏幕(摄像头视图),计时器会启动,跟踪我离开视图的时间,如果超过某个最大值,我的游戏会暂停。同样,对于屏幕内视图(摄像头内视图),计时器会启动,在达到最大值后,它会恢复游戏。

void Update()
    {
        if (InFrontOfScreen()) //This is a condition 
        {
            isInFront = true;
        }
        else 
        {
            isInFront = false;
        }
        InView();
        Pausing();
    }

    private void Pausing()
    {
        if (isPaused)
            Time.timeScale = 0f;
        else
            Time.timeScale = 1f;
    }

    private void InView()
    {
        if (isInFront)
        {
            inViewTime += Time.unscaledDeltaTime;
            if (inViewTime >= maxInViewTime)
            {
                isPaused = false;
                
                
            }
            outOfViewTime = 0f;
        }
        else
        {
            outOfViewTime += Time.unscaledDeltaTime;
            if (outOfViewTime >= maxOutOfViewTime)
            {
                isPaused = true;
               
                
            }
            inViewTime = 0f;
        }
    }

现在,在另一个脚本中,我必须调用依赖于游戏是否暂停的函数。

void Update()
    {
        SwitchCameras();
    }

    private void SwitchCameras()
    {
        if(PauseController.isPaused)
        {
            gameCam.enabled = false;
            mainCam.cullingMask = pausedLayerMask;
        }
        else
        {
            gameCam.enabled = true;
            mainCam.cullingMask = originalLayerMask;
        }
    }

问题是,当布尔值isPaused改变时,gameCam.enabled和mainCam应该只被调用一次。它在每一帧都被调用。我得到了正确的答案,但我希望它只被调用一次,而不是每一帧。有办法实现这一点吗?虽然isPaused可以来回改变,但当isPaused改变时,函数应该被调用一次,然后再调用一次。谢谢

dgjrabp2

dgjrabp21#

从我所看到的,您只需要缓存值,当调用新的更新时,只需检查值是否更改

// Somewhere have a field that saves previous state
private bool _wasPaused;

void Update()
{
    SwitchCameras();
}

private void SwitchCameras()
{
    if (_wasPaused != PauseController.isPaused)
    {
        if (PauseController.isPaused)
        {
            gameCam.enabled = false;
            mainCam.cullingMask = pausedLayerMask;
        }
        else
        {
            gameCam.enabled = true;
            mainCam.cullingMask = originalLayerMask;
        }
    }
    _wasPaused = PauseController.isPaused;
}
gfttwv5a

gfttwv5a2#

我们可以使用一个私有变量和一个属性。这样我们就可以检查属性何时改变值,并且在值改变时只处理一个操作。
很快,这应该是一种实现你想要做的事情的方法:

private bool _isInFront;
public bool isInFront
{
    get => _isInFront;
    set
    {
        if ( _isInFront == value )
            return;
        _isInFront = value;
        InView();
    }

}

private void Update ()
{
    isInFront = InFrontOfScreen(); //This is a condition 
    Pausing();
}
mzaanser

mzaanser3#

问题是gameCam.enabled和mainCam应该只在bool isPaused改变时调用一次
在这种情况下(一般情况下),您根本不应该轮询Update中的检查标志,而是让您的代码React式事件驱动,并且只在值实际更改时做出一次React。
所以在你的第一个剧本里我会加一个

public event Action<bool> pauseStateChanged;

无论何时设置isPaused的状态,都要相应地调用事件,并在负责类中执行更改检查,而不是所有侦听器都必须自己跟踪它

...
// Check makes sure the event is really only called when the state is changed
if(!isPaused)
{
    isPaused = true;
    pauseStateChanged?.Invoke(true);
}

然后在其他脚本中附加一个侦听器,该侦听器仅在调用事件时-〉状态实际更改时才被调用

private void Start()
{
    // start listening to the event
    PauseController.pauseStateChanged += SwitchCameras;

    // initially call the callback once with the current state
    SwitchCameras(PauseController.isPaused);
}

private void OnDestroy()
{
    // remove the listener once not needed anymore to avoid exceptions
    PauseController.pauseStateChanged -= SwitchCameras;
}

private void SwitchCameras(bool isPaused)
{
    gameCam.enabled = !isPaused;
    // Using a ternary here makes this way easier to read imho
    mainCam.cullingMask = isPaused ? pausedLayerMask : originalLayerMask;
}

遵循同样的原则,您还可以使第一个脚本的其余部分以相同的方式React,而不是在Update中调用每一帧的内容,我可能会使用Coroutine并执行以下操作

private Coroutine routine;

void Update()
{     
    var currentIsInFront = InFrontOfScreen();

    // Did state change?
    if(isInFront != currentIsInFront)
    {
        if(routine != null)
        {
            StopCoroutine(routine);
        }

        isInFront = currentIsInFront;
        routine = StartCoroutine(PauseRoutine);       
    }
}

private IEnumerator PauseRoutine()
{
    var newPausedState = !isInFront;

    if(isPaused != newPausedState)
    {
        var delay = isInFront ? maxInViewTime : maxOutOfViewTime;

        yield return new WaitForSecondsRealtime(delay);

        isPaused = newPausedState;  

        pauseStateChanged?.Invoke(newPausedState);

        Time.timeScale = newPausedState ? 0f : 1f;
    }
}

相关问题