我有一个10秒钟的倒计时脚本,该脚本在DeltaTime上运行。
在我的Update函数中,我尝试使其仅打印一次,每当它到达第二个8时就打“ Hello”。
问题在于,deltaTime会在挂起8秒时重复打印“ Hello”,而不是仅打印一次,而且我不知道如何停止这种行为。
我一直试图在进入该块的if块中立即引入触发器,将其设置为0,但是只要计时器在第二个8上,它仍会持续打印“ Hello”。
倒数计时器
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class CountdownTimer : MonoBehaviour
{
float currentTime = 0f;
float startingTime = 10f;
public int n = 0;
public int switcher = 0;
// Start is called before the first frame update
void Start()
{
currentTime = startingTime;
}
// Update is called once per frame
void Update()
{
currentTime -= 1 * Time.deltaTime; //does it each frame
n = Convert.ToInt32(currentTime);
if (n == 8)
{
switcher = 1;
}
}
}
不同类中的更新方法
if (CountdownTimer.switcher == 1)
{
CountdownTimer.switcher = 0;
print("hey");
}
关于如何进行print(“ hey”)的任何想法只会发生一次?这很重要,因为以后我将用一种重要的方法替换打印代码,并且需要确保该方法仅发生一次。
答案 0 :(得分:2)
这是您要实现具有事件/侦听器的订户系统的地方。
向倒数计时添加一个事件,如果倒数计时具有唯一性,您甚至可以将其设为静态。另外,如果在将switcher设置为1之后不再需要更新,则可以将其转换为协程
public class CountdownTimer : MonoBehaviour
{
float currentTime = 0f;
float startingTime = 10f;
public static event Action RaiseReady;
// Start is called before the first frame update
void Start()
{
currentTime = startingTime;
StartCoroutine(UpdateCoroutine());
}
// Update is called once per frame
IEnumerator UpdateCoroutine()
{
while(true)
{
currentTime -= 1 * Time.deltaTime; //does it each frame
int n = Convert.ToInt32(currentTime);
if (n == 8)
{
RaiseReady?.Invoke();
RaiseReady = null; // clean the event
yield break; // Kills the coroutine
}
yield return null;
}
}
}
任何需要了解的组件:
public class MyClass : MonoBehaviour
{
void Start()
{
CountdownTimer.RaiseReady += CountdownTimer_RaiseReady;
}
private void CountdownTimer_RaiseReady()
{
Debug.Log("Done");
// Remove listener though the other class is already clearing it
CountdownTimer.RaiseReady -= CountdownTimer_RaiseReady;
}
}
答案 1 :(得分:1)
由于Updtade()每帧被调用一次,因此切换器在第8秒内被设置为每帧一次(并且在1秒内有很多帧)。
答案可能是这样的,以防止再次打印:
if (CountdownTimer.switcher == 1)
{
if (!AlreadyDisplayed)
{
print("hey");
AlreadyDisplayed = true;
}
}
AlreadyDisplayed
是声明时设置为false的布尔值。
这应该做您想要实现的。 :)
答案 2 :(得分:1)
@Everts提供的解决方案非常好,但是当您使用unity时,我建议您进行一些调整以更好地使用编辑器。我建议使用event
名称空间中的UnityEvent
而不是通用的UnityEngine.Events
。由于统一如何跨场景序列化它们,我也建议不要使用静态。如果您不熟悉unity如何处理其序列化,可能会遇到一些奇怪的情况。如果您只需要向同一场景中的另一个对象发送消息,我实际上会推荐一名游戏管理员。您可以安全地在GameObject.Find()
中执行onvalidate()
并链接变量,以避免在运行时执行查找会影响性能。如果该消息需要将数据传送到其他场景,请改用ScriptableObject
。如下图所示。
将此组件放置在场景的“游戏管理器” GameObject
上
public class CountingPassthrough : MonoBehaviour
{
public CountdownTimer countdownTimer;
}
将此组件放在场景的“计时器” GameObject
public class CountdownTimer : MonoBehaviour
{
public float startingTime = 10f;
public UnityEvent timedOut = new UnityEvent();
private void OnValidate()
{
if(FindObjectOfType<CountingPassthrough>().gameObject.scene == gameObject.scene && FindObjectOfType<CountingPassthrough>() != new UnityEngine.SceneManagement.Scene())
FindObjectOfType<CountingPassthrough>().countdownTimer = this;
}
private void Start()
{
StartCoroutine(TimerCoroutine());
}
// Coroutine is called once per frame
private IEnumerator TimerCoroutine()
{
float currentTime = 0f;
while (currentTime != 0)
{
currentTime = Mathf.Max(0, currentTime - Time.deltaTime);
yield return null;//wait for next frame
}
timedOut.Invoke();
}
}
将此组件放在要使用计时器的GameObject
上
public class user : MonoBehaviour
{
[SerializeField, HideInInspector]
private CountingPassthrough timerObject;
private void OnValidate()
{
if(FindObjectOfType<CountingPassthrough>().gameObject.scene == gameObject.scene && FindObjectOfType<CountingPassthrough>() != new UnityEngine.SceneManagement.Scene())
timerObject = FindObjectOfType<CountingPassthrough>();
}
private void OnEnable()
{
timerObject.countdownTimer.timedOut.AddListener(DoSomething);
}
private void OnDisable()
{
timerObject.countdownTimer.timedOut.RemoveListener(DoSomething);
}
private void DoSomething()
{
//do stuff here...
}
}
该工作流程对预制件也很友好,因为您可以将find()
中的onvalidate()
用if(FindObjectOfType<CountingPassthrough>().gameObject.scene == gameObject.scene)
包装起来,以防止从其他加载的场景中抓取错误的资产。再一次,如果您需要它来跨场景传输数据,则从CountingPassthrough
继承ScriptableObject
而不是MonoBehaviour
,将新的ScriptableObject
创建到项目文件夹中的某个位置,然后忽略如果检查以约束场景匹配,那么这是额外的。然后,如果使用跨场景ScriptableObject
方法,则只需确保使用函数来查找包含资产的函数即可。
编辑:忘记了统一的2018+版本中的嵌套预制件边条。您需要为此添加帐户:&& FindObjectOfType<CountingPassthrough>() != new UnityEngine.SceneManagement.Scene()
我已经更新了上面的代码段。抱歉。