【问题标题】:Clojure; Design performant data structure for objects in 2D Space for collision detection克洛朱尔;为 2D 空间中的对象设计高性能数据结构以进行碰撞检测
【发布时间】:2017-08-07 00:35:28
【问题描述】:

我正在用 clojurescript 编写游戏。

  • 它有一个 2D 游戏区域,例如在 x 和 y 轴上从 0 到 10000。
  • 在这个区域可以有不同大小的点。
  • 如果玩家触摸了一个点,它将被删除。
  • 最多可以有 2500 个点,甚至更多。

由于玩家可以移动每一帧,我必须每秒检查大约 2500 个点 60 次。

如果在哪里有这个数据结构:

(defonce state (atom {:food [{:center {:x 19 :y 51} :radius 1.78 :area 10}
                             {:center {:x 12 :y 34} :radius 1.78 :area 10}]}))

我想它会很慢而且效率很低。

在 C/C++ 中,我可能会创建一个 10000 x 10000 的数组,并将索引用作指向我的对象的指针的 x 和 y 值/键。这样我只需要弄清楚球员目前有多大,以及他的身体覆盖了比赛区域的哪些点。那么我只需要检查这些指标。

dot dots[10000][10000];
// if player where a square of 5 at the moment at x=123 y=456
for (int i = 0; i < 5; i++) {
  for (int j = 0; j < 5; j++) {
    isDot(dots[123+i][456+j]);
  }
}

clojure 和它的数据结构有类似的方法吗?


我尝试了一个“点键”并尝试像这样检索它的值:

(def state (atom {:food { {:x 123 :y 456}{:radius 1.783 :area 9.9}
                          {:x 321 :y 654}{:radius 1.784 :area 10}}}))

(println (get-in @state [:food {:x 123 :y 456}]))

这样的事情可能吗? (这只会给我 nil 打印)

【问题讨论】:

  • 当我需要加速碰撞检测时,我创建了一个空间网格。大约一天就实现了;他们很简单。当然,简单的答案是使用库。 Phaser 是一个非常棒的 JS 游戏引擎。
  • 是的,我会尝试使用 answear 和空间网格。

标签: c++ performance data-structures clojurescript


【解决方案1】:

查找表的三个选项

您的 点键 是一个地图,其中每个键也是一个地图,它确实有效。您在 REPL 中看到的 nil 实际上是调用 println 返回的结果。 (get-in @state [:food {:x 123 :y 456}]) 部分工作正常;它返回与键 {:x 123 :y 456} 关联的值。

对于 CLJS 中的查找表,我可以想到三个可以直接实现的选项:纯 JS 数组、嵌套 ClojureScript 向量和嵌套映射。后者是“点键”的变体,其中第一个映射按行号索引,内部嵌套映射按列号索引(反之亦然)。

下面是初始化三个数据结构的示例:

; plain JS Array
(def js-array (reduce (fn [acc _] (do (.push acc (make-array 10000)) acc)) (array) (range 10000)))

; nested CLJS vectors
(def vec-array (->> false (repeat 10000) vec (repeat 10000) vec))

; nested CLJS map
(def map-grid (reduce #(assoc-in % [(rand-int 10000) (rand-int 10000)] {:radius 1.78 :area 10}) {} (range 2500)))

请注意普通 JS 数组的可变状态如何迫使代码不那么惯用。

测量的查找性能

一项快速而简单的性能测试表明,普通的 JS 数组(仅)比其他两个数组的查找效率略高。以下代码显示了 100k 查找的性能测试:

(defn exec-time [get-fn n]
  (do (time (reduce (fn [x y] (or x y)) ;; stop compiler from optimizing lookups away :-)
                    (repeatedly n get-fn)))
   nil)) ;; Suppress output

(exec-time #(aget js-array (rand-int 10000) (rand-int 10000))
           100000)

(exec-time #(-> (nth vec-array (rand-int 10000))
                (nth (rand-int 10000)))
           100000)

(exec-time #(get-in map-grid [(rand-int 10000) (rand-int 10000)])
           100000)

我在 Figwheel REPL 中重复每个系列的 100k 查找 10 次的结果:

  • 普通 JS 数组:平均。 116,最小 100,最大 156(毫秒)
  • 嵌套向量:平均。 141,最小 128,最大 194
  • 嵌套地图:平均。 246,最小 232,最大 305

性能差异是如此之小,以至于我会根据方便选择您的查找结构。在这种情况下,我更喜欢 CLJS 不可变结构(例如向量)而不是普通 JS。另请注意the performance of cljs data structures compares very favourably to Immutable.js。如果您只执行每帧 25 次查找的规模,那么使用 Clojure(Script) 的持久性数据结构几乎没有什么损失,而且还有很多收获。

【讨论】:

  • 谢谢!在我的机器上它甚至更快。
猜你喜欢
  • 2014-06-06
  • 2018-10-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-06-23
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多