Clojure的;为2D空间中的对象设计高性能数据结构以进行碰撞检测

时间:2017-08-07 00:35:29

标签: c++ performance data-structures clojurescript

我正在用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乘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}]))

这样的事情可能吗? (这只会让我无法打印)

1 个答案:

答案 0 :(得分:1)

查找表的三个选项

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

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

以下是初始化三种数据结构中每一种的示例:

; 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次:

  • plain JS Array:avg。 116,min 100,max 156(msecs)
  • 嵌套向量:平均141,分128,最大194
  • 嵌套地图:平均246,最小232,最大305

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