【问题标题】:How to accelerate a lagging Swing minesweeper board?如何加速落后的 Swing 扫雷板?
【发布时间】:2016-06-30 13:29:31
【问题描述】:

我最近使用seesaw 框架创建了一个小扫雷器UI,它基本上是一个漂亮的clojure 包装器。相关代码可以在here找到。

到目前为止,基本上一切正常,唯一的问题是当您选择专家级别的游戏时,用户体验非常糟糕。原因是每次单击单元格时都会重新绘制整个 ui,这需要很长时间(平均 850 毫秒)。

负责重绘的代码如下:

(defn- update-fields
  [cell-states]
  (doseq [[idx state] (map-indexed vector cell-states)
        :let [field (select-field idx)]]
    (config! field :icon (icons/cell-icons state))))

(defn- update-board
  [snapshot face]
  (do
    (change-smiley face)
    (update-fields (:cells snapshot))
    (repaint! ui)))

图标处理的代码如下所示

(ns minesweeper.icons
  (:require
    [clojure.java.io :as io]
    [clojure.string  :as str]
    [seesaw.icon :as icon]))

(def ^:private cell-icons-path "minesweeper/icons/cell")
(def ^:private face-icons-path "minesweeper/icons/face")

(defn- file-name
  [file]
  (str/replace-first
   (.getName file) #"\.[^.]+$" ""))

(def ^:private init-icons
  (memoize
   (fn [res]
     (let [parent (rest (file-seq (io/file (io/resource res))))]
       (reduce
        #(assoc %1 (keyword (file-name %2)) (icon/icon %2))
        {}
        parent)))))

(defn cell-icons
  [id]
  (let [icons (init-icons cell-icons-path)]
    (get icons id)))

(defn face-icons
  [id]
  (let [icons (init-icons face-icons-path)]
    (get icons id)))

所以我的问题是,如何更有效地解决这个问题?我考虑只更新受点击影响的单元格(由 JButton 表示),但如果自动清除会打开很多相邻的单元格,这也可能需要相当长的时间。

一般来说,使用带有按钮的 mig 布局来表示板子是合理的选择吗?

【问题讨论】:

  • 据我所知,使用按钮的方法对于游戏开发来说并不常见。也许您应该尝试在 JPanel 的图形上绘制字段? stackoverflow.com/questions/6118737/…
  • 听起来很合理,我只是想知道是否可以在某处配置一些缓冲以加快速度,或者图标处理是否错误。
  • 您是否计算过 update-board 中这三个调用中的哪一个导致了延迟?如果重新绘制单个单元格“快速”,您可以重新组织代码以仅更新更改的单元格并在显示一个(或几个)单元格后重新绘制。然后,您可以玩弄以不同模式显示的单元格,例如螺旋,有序,对角线。

标签: swing user-interface clojure minesweeper seesaw


【解决方案1】:

通过使用 clojure.core/time,我发现 UI 逻辑中的瓶颈是使用 (select ui [(keyword (str "#field_" idx))]) 查找按钮,因为每次更新板时,跷跷板必须通过过滤层次结构中的所有组件来进行名称搜索。

最快的解决方法是将您的select-field 函数包装到memoize 中,但是当您重新启动游戏时它将不起作用(将创建新按钮,因此记忆select-field 将返回上一个游戏的按钮)。

另一种可能的解决方案是将所有按钮放入一个向量中,并将其保存在全局atom

(def items (atom []))
(defn select-field
  [idx]
  (@items idx))

并更改创建板的方式:

(defn- make-board-panel
  [snapshot]
  (let [bg    (button-group)
        [n m] (:dimension snapshot)
        buttons (into [] (for [idx (range (* n m))]
                       (make-button idx bg)))]
    (reset! items buttons)
    (mig-panel
     :constraints [(str "gap 0, wrap" n) "[]" "[]" ]
     :items       (map #(vector % "w 24px!, h 24px!") buttons))))

测试

我已将你的update-board 身体包裹在clojure.core/time 中,并通过播放10 次(new-game 50 50 1) 得到以下结果:

修复前

"Elapsed time: 7020.756206 msecs"
"Elapsed time: 6766.130362 msecs"
"Elapsed time: 6616.715565 msecs"
"Elapsed time: 6628.383521 msecs"
"Elapsed time: 6657.386279 msecs"
"Elapsed time: 6588.50692 msecs"
"Elapsed time: 6554.704587 msecs"
"Elapsed time: 6650.864132 msecs"
"Elapsed time: 6610.557065 msecs"
"Elapsed time: 6671.02469 msecs"

修复后

"Elapsed time: 92.491489 msecs"
"Elapsed time: 60.236867 msecs"
"Elapsed time: 32.254729 msecs"
"Elapsed time: 29.551383 msecs"
"Elapsed time: 29.383067 msecs"
"Elapsed time: 25.768517 msecs"
"Elapsed time: 25.724915 msecs"
"Elapsed time: 45.869723 msecs"
"Elapsed time: 25.898016 msecs"
"Elapsed time: 26.254874 msecs"

【讨论】:

  • 我决定采用第二种解决方案,效果非常好。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-12-25
  • 2013-11-12
  • 2021-06-10
  • 2014-08-27
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多