【问题标题】:clojure to java interop with libgdxclojure 到 java 与 libgdx 的互操作
【发布时间】:2013-09-11 16:40:54
【问题描述】:

我正在尝试在 clojure 中从 libgdx tutorial 复制示例应用程序,并且很难让 java 互操作部分正常工作。

在 libgdx 中创建桌面游戏的方式是使用实现 ApplicationListener 接口的对象以及一些配置选项来实例化 LwjglApplication 类,如下所示:

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' 仅被实例化一次,并且 the documentation 指定在应用程序的生命周期中,“create”方法也只会被调用一次。虽然我仍然想知道是否有更好的方法来做到这一点。

至于使用“deftype”,我的理解是“defrecord”会更容易使用,因为它允许传递附加字段的映射,而无需在构造函数中声明它们。但无论哪种情况,代码最终看起来都非常混乱,因为您必须将所有资产传递给构造函数。除此之外,定义命名类型会掩盖代码正在做什么,并且不能很好地解决问题。

我将完成本教程的其余部分,并在完成后发布指向 clojure 代码的链接。谢谢大家。

【问题讨论】:

    标签: clojure interop libgdx


    【解决方案1】:

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

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

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

    【讨论】:

    • 是的,这是有道理的。我假设我的应用程序侦听器函数在调用时隐式返回“this”对象,并将 let 绑定变量作为字段,但我猜这就是“reify”和“proxy”实例化匿名对象的含义?我没有做太多java,所以当我阅读它时,这部分没有多大意义。无论如何,我会尝试一下并报告。堆栈跟踪已启动here 您还可以通过绑定当前线程的值来解释您的意思吗?谢谢
    【解决方案2】:

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

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

    【讨论】:

    • 谢谢,我会尝试一下,看看哪种方法最适合 clojure。将发布我发现的内容
    【解决方案3】:

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

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

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2015-06-12
      • 1970-01-01
      • 2019-07-18
      • 1970-01-01
      • 1970-01-01
      • 2014-03-31
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多