我正在尝试在 clojure 中重现此libgdx 教程中的示例应用程序,并且很难让 java 互操作部分正常工作。
在 libgdx 中创建桌面游戏的方式是LwjglApplication
使用实现接口的对象ApplicationListener
以及一些配置选项来实例化类,如下所示:
import com.badlogic.gdx.backends.lwjgl.LwjglApplication;
public class Main {
public static void main(String[] args) {
new LwjglApplication(new DropGame(), cfg);
}
public class DropGame implements ApplicationListener {
Texture dropImage;
Sound dropSound;
... rest of assets
public void create() {
dropImage = new Texture(Gdx.files.internal("droplet.png"));
dropSound = Gdx.audio.newSound(Gdx.files.internal("drop.wav"));
}
... rest of interface methods
}
}
在阅读了 clojure/java interop 之后,这是我想出的 clojure 等价物:
(ns dropgame.core
(:gen-class)
(:import (com.badlogic.gdx.backends.lwjgl LwjglApplication))
(defn app-listener []
(reify ApplicationListener
(create [this] (let [dropImage (Texture. (.internal Gdx/files "droplet.png"))
dropSound (.newSound Gdx/audio (.internal Gdx/files "drop.wav"))])
(defn App []
(LwjglApplication. (app-listener) cfg))
我遗漏了很多代码,但这很好,因为应用程序使用这种结构工作。问题是这段代码并不完全相同。libgdx 框架选择全局声明所有资产(在本例中为 dropImage 和 dropSound),以便其他方法中的代码可以轻松访问它们而无需传递它们。如果我尝试重组我的代码来做同样的事情,通过在外部 let 绑定而不是在方法内部声明资产,它不再起作用。
这样做会给我一个 nullPointerException:
(defn app-listener []
(let [dropImage (Texture. (.internal Gdx/files "droplet.png"))
dropSound (.newSound Gdx/audio (.internal Gdx/files "drop.wav"))]
(reify ApplicationListener
(create [this] ( ; try accessing dropImage or dropSound)))))
所以 let 绑定创建了相同的对象,但是从任何方法内部访问这些对象似乎不再起作用。有什么想法我在这里做错了吗?
编辑:如前所述,我得到 nullPointerException 的原因可能是因为在应用程序准备好之前加载了资产。但无论如何,为简单游戏之外的任何内容加载资产的正确方法是使用 AssetManager,正如 muhuk 建议的那样。所以我最终为资产做了这样的事情:
(def asset-manager (doto (AssetManager.)
(.load "rain.mp3" Music)
(.load "droplet.png" Texture))
但是,除了需要全局访问的资产之外,还有其他东西,例如相机。所以我为这些做的是:
(defn app-listener []
(reify ApplicationListener
(create [this]
(def camera (-> (OrthographicCamera.) (.setToOrtho false 800 480))))))
虽然这可能会在任何普通的 clojure 代码中引发警报,因为您有一个定义全局值的函数,但在这种特殊情况下,它实际上是有意义的,因为“app-listener”只被实例化一次,并且文档指定“create”方法在应用程序的生命周期中也只被调用一次。虽然我仍然想知道是否有更好的方法来做到这一点。
至于使用“deftype”,我的理解是“defrecord”会更容易使用,因为它允许传递附加字段的映射,而无需在构造函数中声明它们。但无论哪种情况,代码最终看起来都非常混乱,因为您必须将所有资产传递给构造函数。除此之外,定义命名类型会模糊代码正在做什么,并且不能很好地解决问题。
我将完成本教程的其余部分,并在完成后发布指向 clojure 代码的链接。多谢你们。