0

我正在尝试在 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 代码的链接。多谢你们。

4

3 回答 3

0

我的猜测是您正在尝试在它们被 libgdx 框架初始化之前对其进行初始化。如果您发布堆栈跟踪会很有帮助。

作为另一个clojure新手,也许要做的是探索deftype?iirc 将为您提供方法和字段,这正是您所需要的。let 仅将变量初始化为当前范围。

另一种方法可能是绑定当前线程的值,但我不确定哪个更正确。祝你好运!(我也对 libgdx 和 clojure 感兴趣)

于 2013-09-11T19:45:13.360 回答
0

let将尝试在创建应用程序实例之前加载资产。libGDX 模仿 android 应用程序的生命周期,因此您不应该在create调用之前尝试加载资产。

由于 libGDX 是一个 Java 框架,我们不能指望它与 Clojure 的纯函数式方法相匹配。某处一定有某种状态。您可能想看看AssetManager

于 2013-09-12T02:50:25.273 回答
0

如果人们在使用 Clojure 和示例游戏时遇到问题(就像我所做的那样),这里是一个工作实现的链接(虽然没有音乐/声音):

http://clojuregames.blogspot.com/2013/10/the-libgdx-simple-game-demo-in-clojure.html

我也才刚刚开始学习 Clojure,所以如果实现中的某些事情做得不好,请在评论中告诉我。

于 2013-10-13T10:52:37.017 回答