9

I'm wondering if there is any widespread timer solution.

I want to write a simple game engine which would process users input on tick every 20ms (or perform some actions once after 20ms (or any other period)) and basically update "global" state via transactions, and also I plan to use futures so this solution should be able to deal with concurrency caveats.

Can you give me an advice?

4

3 回答 3

16

您实际上在这里遇到了两个不同的问题。

首先是定时器的问题。您在这里有很多选择:

  • 启动一个在动作之间休眠的线程,例如(future (loop [] (do-something) (Thread/sleep 20) (when (game-still-running) (recur))))
  • 使用 Java TimerTask - 易于从 Clojure 调用
  • 使用像我的小实用任务这样的库,其中包含用于重复任务的 DSL
  • 使用您正在使用的任何游戏引擎的计时器功能 - 其中大多数都提供了一些用于设置游戏循环的工具

我可能只使用简单的线程选项 - 如果您需要,它很容易设置并且很容易在以后破解更多功能。

第二个问题是处理游戏状态。这实际上是一个更棘手的问题,您需要围绕您正在制作的特定类型的游戏进行设计,所以我只提供几点建议:

  • 如果您的游戏状态是不可变的,那么您将获得很多优势:您的渲染代码可以在游戏更新计算下一个游戏状态时独立绘制当前游戏状态。不变性有一些性能成本,但如果你能让它工作,并发优势是巨大的。我的两个迷你 Clojure 游戏(Ironclad 和 Alchemy)都使用这种方法
  • 您可能应该尝试将游戏状态存储在单个顶级 var中。我发现这比将游戏状态的不同部分拆分到不同的变量上效果更好。这也意味着您实际上并不需要基于 ref 的事务:原子或代理通常可以解决问题。
  • 您可能希望实现需要由游戏状态更新函数按顺序处理的事件队列。如果您有多个并发事件源(例如玩家动作、计时器滴答声、网络事件等),这一点尤其重要。
于 2013-05-05T13:50:44.507 回答
7

现在我认为 core/async 是一个不错的选择,因为

  • 当涉及可以分成使用通道进行通信的活动的复杂任务时,它可以很好地扩展
  • 避免将活动绑定到线程

这是草图

(require '[clojure.core.async :refer [go-loop]])
(go-loop []
  (do
    (do-something ...)
    (Thread/sleep 1000)
    (recur))))
于 2015-11-24T22:02:14.137 回答
6

此解决方案假定您在 JVM 上编写 Clojure。像这样的东西可以工作:

(import '(java.util TimerTask Timer))

(let [task (proxy [TimerTask] []
             (run [] (println "Here we go!")))]
  (. (new Timer) (schedule task (long 20))))
于 2013-05-05T13:35:38.177 回答