从静态类引用样式,字体和图像是不是很糟糕?

时间:2016-05-18 16:16:43

标签: java libgdx

我已经阅读了很多关于使用静态类的不同缺陷,所以我从来没有真正打扰过使用静态类。

但是我可以看到创建一个包含所有样式,图像,字体等的静态类的主要优点。方便参考。这会对性能或糟糕的设计造成不利影响吗?

为了帮助解释我的意思,我有一些代码,这是来自静态类:

    button2Style = new TextButtonStyle();
    button2Style.up = new NinePatchDrawable(buttonAtlas.createPatch("button2"));
    button2Style.down = new NinePatchDrawable(buttonAtlas.createPatch("button2")).tint(Color.GRAY);
    button2Style.font = font_rr_65;
    button2Style.fontColor = Colors.PURPLE;

然后我想在另一个类中引用它:

TextButton txtB = new TextButton("Hello", StaticClass.button2Style);

2 个答案:

答案 0 :(得分:3)

尽管通常使用静态通常很好地表明面向对象设计不良,但请注意: 对资产进行静态引用尤其糟糕,因为静态的生命周期可能与创建资源的上下文的生命周期不同

让我们以@BasimKhajwal的答案为例:

class Assets { /*...*/
    private static final AssetManager assetManager = new AssetManager();

此代码new AssetManager()何时实际执行?初始化VM后,它实际上是直接执行的。这可能是在实际创建应用程序窗口之前,甚至在加载所有本机库并创建OpenGL上下文之前。

public static void loadAll() { /* load things */ }

这段代码什么时候执行?你可能在ApplicationListener的create方法中调用它。因此,的OpenGL上下文,即应用程序的特定实例在此时创建并处于活动状态,并在该应用程序的呈现线程上调用。

现在考虑在Android上运行的以下情况:

您的VM静态资产已加载到该应用程序上下文中,您可以在该上下文中使用它们。然后,您会收到一个电话,导致您的应用程序在后台推送。过了一会儿,Android决定它需要释放内存并关闭你的应用程序,包括它的OpenGL上下文等。

完成通话后,您将返回游戏。因此,Android会启动游戏的新实例。为此,它重新使用应用程序的前一个实例的相同VM,包括相同的AssetManager和它已加载的所有资产。您的应用程序会创建一个新的OpenGL上下文等,但资产仍然指向旧的无效上下文。从而使您的资产无效并导致不良行为。

当然,在正常测试期间,您不会轻易找到此类问题。并且无法保证Android何时或何时关闭您的应用程序并回收VM。但是如果你尝试一下,你应该可以重现上述步骤,这样你就可以自己看看你的资源是如何变得无效的(例如纹理变黑)。您的应用程序的多个实例在同一个VM中运行(例如,从Play商店和启动屏幕启动时)甚至可以said

你可以解决这个问题。例如,libGDX在内部通过为每个应用程序实例保留Map所有资源来实现这一点(here是一个例子,如果你想看看它是如何工作的)。但是,由于您很可能没有正确实施Object Oriented Design,所以最好重新考虑您的方法。

如果你仍然决定使用static,那么请确保完全理解他们的生命周期(不要假设他们不会比你的应用程序的特定实例更长久或独有)。

请注意,这可能是最常犯的错误之一(使用“虚拟”像素旁边)。如果你搜索一下,你会发现一些使用静态问题的人的例子,特别是在Android上。但请记住,虽然这在Android上展示最多,但它是一个设计问题。这并不意味着当您不定位Android时它不适用于您。完全可以设计您的应用程序,以便您不需要任何静态资源(或任何重要的静态变量)。

答案 1 :(得分:1)

编辑 - 结果我错了:(,请参阅Xoppa的回答

就个人而言,我发现使用静态类对libGDX和游戏开发特别有用。

如果您阅读this等文章,人们会对静态变量的使用感到不满。重点在于变量,因为通常具有变化的全局状态可能更难以调试和推理。当变量是静态的时,代码的任何部分都可能导致这些更改导致错误/崩溃,导致程序完全不同的部分。

对于您的资产,它们将在游戏的大部分时间内保持不变,这意味着它们不会干扰您的调试。此外,您的资产可能必须在整个游戏期间加载,因为大多数游戏在启动时加载所有资产。保持静态将使内存使用量几乎没有差异。

对于我的游戏,我通常倾向于使用一个名为Assets的静态类,它使用内置的AssetManager类。然后,我可以使用存储为常量的文件路径轻松引用任何资产。通过这样做,您可以确保其他类无法编辑静态纹理,您可以轻松地遍历纹理。

示例:

class Assets {

    /* Use built-in libGDX asset manager to load/store all assets */
    private static final AssetManager assetManager = new AssetManager();

    /* Use private constructor to prevent instantiation */
    private Assets () { }

    /*
        Public constants to be referenced by other classes
    */
    public static final String BAD_GUY = "images/bad_guy.png";
    public static final String GOOD_GUY = "images/good_guy.png";

    /*
        Private arrays of all textures/sounds/fonts for loading
    */
    private static final String[] TEXTURES = {
        BAD_GUY,
        GOOD_GUY;
    };

    /* Load method for all assets called by your main game handler at start */
    public static void loadAll() {

        //Setup the load queue for all textures 
        for (String texture: TEXTURES)
            assetManager.load(texture, Texture.class);

        //Load everything
        assetManager.finishLoading();

        /* Do all the post processing here */
    }

    /* Access method for textures the parameter name is from above e.g. Assets.BAD_GUY */
    public Texture getTexture(String name) {
        return assetManager.get(name, Texture.class);
    }
}

如果你想看一个真实的例子,你可以看到我自己的游戏文件: https://github.com/basimkhajwal/LSD/blob/master/core/src/net/net63/codearcade/LSD/managers/Assets.java

请注意,它有点复杂,因为它包含声音,动画和一些特定于游戏的代码,但主要原则是相同的。