如何避免ClojureScript游戏引擎运行时阻塞浏览器事件?
问题:游戏循环阻塞Three.js渲染与按键事件处理
背景
我使用以下技术构建小型ECS游戏引擎:
- Clojure
- Three.js作为图形渲染目标之一
- shadow-cljs用于项目构建
问题描述
浏览器加载应用后,游戏引擎的主游戏循环会阻塞Three.js通过requestAnimationFrame进行的屏幕渲染,同时阻止按键事件处理,直到循环结束后两者才恢复正常。
典型运行流程:
- 刷新浏览器窗口
- 控制台可见主游戏循环执行
- 循环期间,屏幕无内容显示,按键操作无日志输出
- 游戏结束后,所有按键事件批量触发并一次性记录
- 最后Three.js渲染器完成渲染
已尝试的无效方案
- 在
go表单中运行游戏 - 先执行
:start-up阶段,再用setTimeout运行剩余游戏循环 - 不在
init中调用run函数,尝试从配置文件指定加载后运行
预期效果
游戏循环运行时,按键事件能被实时处理,且Three.js渲染器每次在游戏循环中调用draw-scene!系统时都能完成渲染。
代码示例
src/app.cljs
(ns app (:require ["three" :as three] [snake :refer [add-snake-plugin]] [chaos.plugins.core :refer [add-core-plugins]] [chaos.plugins.timer :as timer] [chaos.engine.world :as chaos :refer [create-world defsys add-system add-system-dependency add-stage-dependency]])) defsys setup-threejs {} (let [w (.-innerWidth js/window) h (.-innerHeight js/window) aspect (/ w h) camera (three/PerspectiveCamera. 75 aspect 0.1 1000) renderer (three/WebGLRenderer.)] ;; Setup renderer and dom elements. (.setSize renderer w h) (.. js/document -body (appendChild (.-domElement renderer))) ;; Move camera back (set! (.. camera -position -z) 5) (println "Cam Z:" (.. camera -position -z)) (println renderer) [[:add [:resources :camera] camera] [:add [:resources :scene] (three/Scene.)] [:add [:resources :renderer] renderer]]) defsys add-cube {:resources [:scene]} (let [scene (:scene resources) geometry (three/BoxGeometry. 1 1 1) material (three/MeshBasicMaterial. #js {:color 0x00ff00}) cube (three/Mesh. geometry material)] (.add scene cube) []) defsys draw-scene! {:resources [:renderer :scene :camera] :events :tick} (println "Drawing...") (let [{:keys [:renderer :scene :camera]} resources render-scene #(.render renderer scene camera)] (.. js/window (requestAnimationFrame render-scene)) []) defsys capture-key-down {} (let [raw (atom []) add-event (fn [event] (println "Keydown event!") (swap! raw conj event))] (.addEventListener js/window "keydown" add-event) [[:add [:resources :key-down-events] raw]]) defsys handle-key-down {:resources [:key-down-events]} (println "KEYS" (:key-down-events resources)) ;; ... Pruned some irrelevant systems ... defn ^:dev/after-load run [] (-> (create-world) add-core-plugins ;; Main engine plugins (removing has no effect) add-snake-plugin ;; The snake game library from another example (as above) (add-system :start-up setup-threejs) (add-system :start-up add-cube) (add-system-dependency add-cube setup-threejs) (add-system :render draw-scene!) ;; Set of irrelevant systems which essentially just exit the game after 5 seconds. (add-system :start-up add-exit-timer) (add-system :pre-step pass-time) (add-system exit-after-5) ;; Gets key events from window (add-system :start-up capture-key-down) (add-stage-dependency :render :update) chaos/play) ;; shadow-cljs entry point defn init [] (println "Refresh.") (run))
shadow-cljs.edn
;; shadow-cljs configuration {:deps true :dev-http {8080 "public"} :builds {:app {:target :browser :modules {:main {:init-fn app/init}}}}}
内容的提问来源于stack exchange,提问作者hjk
相关产品推荐
相关产品推荐

