Muh*_*han 1 c# time events unity-game-engine
我必须做一些基于时间的任务(模拟),例如,当游戏开始时:
但请记住,如果我想快速查看模拟,计时器应该能够加快速度。
现在我正在考虑两种方法(也欢迎其他方法,这就是问题的目的)
每个对象(Train)脚本通过 WaitForSeconds 管理其时间:
void Start()
{
StartCoroutine("MyEvent");
}
private IEnumerator MyEvent()
{
yield return new WaitForSeconds(120f); // wait two minutes
//Launch Train
}
Run Code Online (Sandbox Code Playgroud)
这种脚本附加到每个需要在一定时间后执行操作的对象:
问题:
计时器的一个全局脚本:
function Update ()
{
Timer += Time.deltaTime; //Time.deltaTime will increase the value with 1 every second.
if (timer>= 120){
//launch train, an so one conditions
//Or get timer variable in other script and compare time on update
}
}
Run Code Online (Sandbox Code Playgroud)
现在使用上面的脚本,我可以在另一个脚本中获取 Timer 变量,并可以根据方法中的时间执行我的任务update
。
问题是我该如何管理它?第一种方式还是第二种方式还是第三种方式(由你决定)?因为我也想加快时间,这在注册后的协同例程中似乎是不可能的。
需要你们的帮助!
您可以使用这两种方法中的任何一种,如果您想在示例中查看更快/更慢等,只需更改Time.timescaleSpace即可按:
public class Example : MonoBehaviour
{
// Toggles the time scale between 1 (normal) and 0.5 (twice as fast)
// whenever the user hits the Space key.
private void Update()
{
if (Input.GetKeyDown(KeyCode.Space))
{
if (Time.timeScale == 1.0f)
{
Time.timeScale = 0.5f;
}
else
{
Time.timeScale = 1.0f;
}
// Also adjust fixed delta time according to timescale
// The fixed delta time will now be 0.02 frames per real-time second
Time.fixedDeltaTime = 0.02f * Time.timeScale;
}
}
}
Run Code Online (Sandbox Code Playgroud)
对于协程来说,无需更改任何内容即可实现此功能,因为WaitForSeconds受以下因素影响Time.timescale
:
实际暂停时间等于给定时间乘以Time.timeScale。
Time.deltaTime
afaik不受此影响,Time.timescale
因此为了更快地重播,您必须这样做
private void Update ()
{
Timer += Time.deltaTime * (1 / Time.timescale);
if (timer >= 120)
{
//launch train, an so one conditions
//Or get timer variable in other script and compare time on update
}
}
Run Code Online (Sandbox Code Playgroud)
一个Update
或多个协程的性能是否更高在很大程度上取决于特定的用例。老实说,直到您运行大约 10.000 个协程时,这一点才真正引人注目(不要在这里用数字来指责我;))。
在您仅引发一个或多个事件的情况下,最好坚持使用一种Update()
方法并调用一个事件或类似的事件。
但是- 为什么不简单地使用一个协程而不是一个协程Update
:
public class GlobalTimer : MonoBehaviour
{
public float Delay = 2.0f;
public UnityEvent onTimerDone;
// Unity allows to use Start as IEnumerator instead of a void
private IEnumerator Start()
{
yield return new WaitforSeconds(delay);
onTimerDone.Invoke();
}
}
Run Code Online (Sandbox Code Playgroud)
比您只有一个调用的方法并且可以向该计时器添加回调,例如
globalTimerReference.onTimerDone.AddListener(() => {
Debug.LogFormat("Timer of {0} seconds is done now.", globalTimerReference.Delay);
});
Run Code Online (Sandbox Code Playgroud)
或通过检查员(如a中UI.Button.onClick
)。
对于多个事件,我刚刚想出了一个快速而肮脏的解决方案,因此您可以简单地通过检查器定义多个事件并添加各种回调和内容:
public class GlobalTimer : MonoBehaviour
{
public List<UnityEvent> events;
public List<float> delays;
private void Start()
{
var validPairs = Mathf.Min(events.Count, delays.Count);
for (int i = 0; i < validPairs; i++)
{
StartCoroutine(InvokeDelayed(events[i], delays[i]));
}
}
private IEnumerator InvokeDelayed(UnityEvent unityEvent, float delay)
{
yield return new WaitForSeconds(delay);
unityEvent.Invoke();
}
}
Run Code Online (Sandbox Code Playgroud)
只需确保列表中的每个事件都有延迟即可。将来您可能想要编写一个合适的自定义编辑器,以便在检查器中编辑得更漂亮。
更新
或者你可以拿我的:D
public class ExampleScript : MonoBehaviour
{
[SerializeField] private List<EventDelayPair> EventDelayPairs;
private void Start()
{
foreach (var eventDelayPair in EventDelayPairs)
{
StartCoroutine(InvokeDelayed(eventDelayPair.unityEvent, eventDelayPair.Delay));
}
}
private IEnumerator InvokeDelayed(UnityEvent unityEvent, float delay)
{
yield return new WaitForSeconds(delay);
unityEvent.Invoke();
}
[Serializable]
private class EventDelayPair
{
public UnityEvent unityEvent;
public float Delay;
}
}
[CustomEditor(typeof(ExampleScript))]
public class ExampleInspector : Editor
{
private SerializedProperty EventDelayPairs;
private ReorderableList list;
private ExampleScript _exampleScript;
private void OnEnable()
{
_exampleScript = (ExampleScript)target;
EventDelayPairs = serializedObject.FindProperty("EventDelayPairs");
list = new ReorderableList(serializedObject, EventDelayPairs)
{
draggable = true,
displayAdd = true,
displayRemove = true,
drawHeaderCallback = rect =>
{
EditorGUI.LabelField(rect, "DelayedEvents");
},
drawElementCallback = (rect, index, sel, act) =>
{
var element = EventDelayPairs.GetArrayElementAtIndex(index);
var unityEvent = element.FindPropertyRelative("unityEvent");
var delay = element.FindPropertyRelative("Delay");
EditorGUI.PropertyField(new Rect(rect.x, rect.y, rect.width, EditorGUIUtility.singleLineHeight), delay);
rect.y += EditorGUIUtility.singleLineHeight;
EditorGUI.PropertyField(new Rect(rect.x, rect.y, rect.width, EditorGUI.GetPropertyHeight(unityEvent)), unityEvent);
},
elementHeightCallback = index =>
{
var element = EventDelayPairs.GetArrayElementAtIndex(index);
var unityEvent = element.FindPropertyRelative("unityEvent");
var height = EditorGUI.GetPropertyHeight(unityEvent) + EditorGUIUtility.singleLineHeight;
return height;
}
};
}
public override void OnInspectorGUI()
{
DrawScriptField();
serializedObject.Update();
list.DoLayoutList();
serializedObject.ApplyModifiedProperties();
}
private void DrawScriptField()
{
// Disable editing
EditorGUI.BeginDisabledGroup(true);
EditorGUILayout.ObjectField("Script", MonoScript.FromMonoBehaviour(_exampleScript), typeof(ExampleScript), false);
EditorGUI.EndDisabledGroup();
EditorGUILayout.Space();
}
}
Run Code Online (Sandbox Code Playgroud)
例子
或预览调试延迟
public class ExampleScript : MonoBehaviour
{
public List<EventDelayPair> EventDelayPairs;
private void Start()
{
foreach (var eventDelayPair in EventDelayPairs)
{
StartCoroutine(InvokeDelayed(eventDelayPair));
}
}
private IEnumerator InvokeDelayed(EventDelayPair pair)
{
var timer = pair.Delay;
do
{
timer -= Time.deltaTime * (1 / Time.timeScale);
pair.Delay = timer;
yield return null;
} while (timer > 0);
pair.Delay = 0;
pair.unityEvent.Invoke();
}
[Serializable]
public class EventDelayPair
{
public UnityEvent unityEvent;
public float Delay;
}
}
Run Code Online (Sandbox Code Playgroud)
顺便说一句你的评论
//Time.deltaTime每秒将值增加1。
表述不正确。相反,它应该是:
Time.deltaTime
将增加自上一帧渲染以来经过的时间(以秒为单位)的每一帧的值。
编辑 - 减少事后延迟
从你问的问题我就明白你想要speed up
整个回放。
从评论中我现在了解到,相反,您希望减少事后的延迟。所以你不能使用Time.timescale
.
为此,您可以使用稍作修改的第二个示例:
[Serializable]
public class EventDelayPair
{
public UnityEvent unityEvent;
public float Delay;
// add a time multiplicator for each delay with default 1
public float TimeMultiplicator = 1.0f;
}
Run Code Online (Sandbox Code Playgroud)
注意:如果您使用它,您还必须将其添加到 EditorScript - 我将其作为您的作业;)
private IEnumerator InvokeDelayed(EventDelayPair pair)
{
var timer = pair.Delay;
do
{
timer -= Time.deltaTime * pair.TimeMultiplicator;
pair.Delay = timer;
yield return null;
} while (timer > 0);
pair.Delay = 0;
pair.unityEvent.Invoke();
}
Run Code Online (Sandbox Code Playgroud)
所以你可以在检查器中或者也可以通过脚本
exampleScriptReference.EventDelayPairs[0].TimeMultiplicator = 2;
Run Code Online (Sandbox Code Playgroud)
更快地减少延迟。
您可能还想添加一个整体乘数,例如
// Again you have to add it to the inspector if you use it
public float overallMultiplicator = 1.0f;
//...
timer -= Time.deltaTime * pair.TimeMultiplicator * overallMultiplicator;
Run Code Online (Sandbox Code Playgroud)