gpt4 book ai didi

c# - 统一管理时间事件的正确方法

转载 作者:太空宇宙 更新时间:2023-11-03 22:37:26 26 4
gpt4 key购买 nike

我必须做一些基于时间的任务(模拟),比如,游戏开始时:

  • 2 分钟后启动 TrainA。
  • 6 分钟后星号列车 B
  • 12分钟后开始列车C等

但请记住,如果我想快速查看模拟,计时器应该能够加速。

现在我正在考虑两种方法(也欢迎使用其他方法,这就是问题的目的)

每个对象(火车)脚本通过 WaitForSeconds 管理其时间:

void Start()
{
StartCoroutine("MyEvent");
}

private IEnumerator MyEvent()
{

yield return new WaitForSeconds(120f); // wait two minutes
//Launch Train

}

这种脚本附加到每个需要在特定时间后执行操作的对象:

问题:

  1. 如何加快时间?
  2. 也许是性能密集型。由于每个脚本管理自己的协程(也许我错了)

Timer 的一个全局脚本:

    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
}

}

现在使用上面的脚本,我可以在另一个脚本中获取 Timer 变量,并可以在 update 方法中根据时间执行我的任务。

问题是我该如何管理它?第一种方式或第二种方式或第三种方式(由您)?因为我也想加快时间,这在协程注册后似乎是不可能的。

需要你们的帮助!!

最佳答案

您可以使用这两种方式中的任何一种,只需更改 Time.timescale如果您想在示例中通过按 Space 来更快/更慢地查看它:

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;
}
}
}

对于协程,这不需要你改变任何东西,因为 WaitForSecondsTime.timescale 影响:

The actual time suspended is equal to the given time multiplied by 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
}
}

是一个 Update 还是多个 Coroutines 的性能更高在很大程度上取决于具体的用例。 Afaik 直到你有大约 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();
}
}

比起你只有一个被调用的方法,并且可以像这样向那个计时器添加回调

globalTimerReference.onTimerDone.AddListener(() => {
Debug.LogFormat("Timer of {0} seconds is done now.", globalTimerReference.Delay);
});

或通过检查器(如在 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();
}
}

只需确保列表中的每个事件都有延迟。将来您可能想要编写一个合适的 CustomEditor,以便在检查器中将其编辑得更漂亮。

更新

或者你可以拿走我的: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();
}
}

例子

enter image description here

或预览调试延迟

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;
}
}

enter image description here


顺便说一下你的评论

//Time.deltaTime will increase the value with 1 every second.

表述不正确。相反,它应该是:

Time.deltaTime will increase the value every frame about the time passed in seconds since the last frame was rendered.


编辑 - 之后减少延迟

我从问题中了解到您想要加速整个播放。

从我现在了解到的评论中,您反而想减少事后的延迟。所以你不能使用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;
}

注意:如果您使用它,您还必须将它添加到 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();
}

因此您可以在检查器中或也可以通过脚本

exampleScriptReference.EventDelayPairs[0].TimeMultiplicator = 2;

更快地减少延迟。

您可能还想添加一个整体乘法器,例如

// Again you have to add it to the inspector if you use it
public float overallMultiplicator = 1.0f;

//...
timer -= Time.deltaTime * pair.TimeMultiplicator * overallMultiplicator;

关于c# - 统一管理时间事件的正确方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54302191/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com