【问题标题】:C# Timers for game development用于游戏开发的 C# 计时器
【发布时间】:2011-02-26 14:38:27
【问题描述】:

我想找出在游戏中创建基于时间的事件的最佳方式。 让我们以德州扑克为例。服务器可以处理数千张桌子,并且在每张桌子上我们都有计时器:转动计时器、保持座位计时器等等。

为此目的实现计时器的最佳方式是什么?

System.Timers.Timer 类是否可以处理这个问题,或者创建一个具有排序时间队列的单独线程会更合理(例如,一个升序排序列表,其 int 值表示剩余时间,以毫秒为单位)?

提前致谢, 瓦伦丁

【问题讨论】:

标签: c# .net timer


【解决方案1】:

我建议使用System.Timers.TimerSystem.Threading.Timer。您使用排序队列的想法正是 System.Threading.Timer 的实现方式,因此您只会浪费时间并增加不必要的开销。 System.Timers.TimerSystem.Threading.Timer 的包装。

我不建议使用当前时间(即DateTime.Now 或类似的)来提供超时,因为时钟可能会在不可预知的时间以不可预知的方式改变。

如果您想以“当前时间”为基础,请在程序开始时启动Stopwatch,并使用Elapsed 属性作为游戏中的“当前时间”。所以距离当前时间 5 秒的时间是:

var FiveSecondsFromNow = GameTimer.Elapsed + TimeSpan.FromSeconds(5);

在 cmets 中提出的问题是,我是否会建议使用带有 Elapsed 事件的多个计时器,或者是按时间顺序维护并定期检查的操作列表(即在计时器中或在主游戏循环中)。

我的直接反应是为每个需要发生的事件使用一个单独的计时器对象。这样做会在多核可用时利用它们(并发事件可以同时发生),并且使我不必维护该待处理操作的列表。我知道定时器队列实现可以毫无问题地处理数千个单独的定时器,而且几乎可以肯定他们的定时器队列实现会比我的更高效。

使用计时器可以简化代码,除非您遇到多线程问题。我很简单。如果遇到计时器的性能问题,我只会转到手动维护的待处理操作列表。在我努力消除它们之前,我会非常确定计时器是罪魁祸首。

【讨论】:

  • 感谢有关定时器实现的信息!问题仍然存在,您将如何使用Timer。一个全局计时器并检查经过的时间,或者为每个需要发生的“事件”设置一个计时器,并使用 Elapsed 事件。
【解决方案2】:

我经常发现根本不使用计时器是最好的方法。只需存储时间戳,在处理时,检查当前时间与存储的时间戳的差异。

当有其他简单的解决方案可用时,尽量减少线程的使用。

您应该能够经常“处理”所有表格以处理所有逻辑。除非我们谈论的是非常大的数字,否则这应该不是问题。

除非您的游戏中有一些非常繁重的逻辑需要大量处理,很可能不是在德州扑克中,否则您不应该使用线程,它们只会使事情复杂化。当完全有可能在一次短暂的更新中更新游戏的整个状态时,为什么还要运行一些“同时”的东西?

【讨论】:

  • 是的,这是您提供的最佳解决方案,但有时并不合适。例如,在德州扑克中,我有一个回合计时器,所以如果我只在回合移动期间存储时间戳,我应该何时开始处理它以及如何知道何时达到所需时间?当然,我可以运行单独的线程,它将通过所有表运行并例如每秒比较时间戳,但这是我认为最糟糕的方式
  • @Valentin:不,不是,这就是所谓的“游戏循环”。 This 是一篇关于该主题的优秀文章。
  • @Steven Jeuris:当然,游戏循环是每个“客户端”游戏中的心跳,但这个规则并不总是可以应用于游戏服务器恕我直言
  • @Valentin:也许出于安全/稳定性的原因,您可以考虑在不同的进程上运行每个表。或者自动使用多核 CPU。但正如我在更新中指定的那样,如果没有,请不要使事情复杂化。尝试编写一个简单的“服务器”,它简单地迭代“数千个”表并执行例如一些时间戳检查。我认为这不会超过 1 秒。线程并不是让缓慢的事情运行得更快的神奇“修复”。
  • 使用“当前时间”的问题在于,时间可能会以意想不到的(和不可预测的)方式改变。夏令时是罪魁祸首,但用户也可以手动更改时钟。而“不要那样做”并不是一个特别好的回应,因为用户通常不期望(在我看来,也不应该期望)更改时钟会导致游戏崩溃或表现出奇怪的行为。
【解决方案3】:

我会保留一个排序队列,但将System.Timers.Timer 安排到队列中第一个事件的时间。当事件触发时,我一次从队列中选择一个元素,处理该元素,在下一个达到峰值以查看时间是否已过。如果时间已过,则处理该事件,否则为剩余时间安排一个计时器。

当新事件到达时,您将它们插入排序队列。如果它们恰好是队列中的第一个,请重置计时器并以新的第一个元素的间隔重新启动它。

这一切都假设您需要触发某些事情发生。如果您只想知道何时有人询问是否有新事件,您不需要计时器,也不需要额外的线程来处理队列。只需在有人询问时检查您的事件队列即可。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2012-08-08
    • 1970-01-01
    • 2011-07-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多