LibGDX 皮肤附加资源依赖问题

LibGDX Skin additional resource dependencies issue

我想知道我是否做错了什么(很可能)或者 LibGDX SkinLoader 是否存在关于额外资源依赖性的问题。

根据SkinLoader.SkinParameter的文档,您可以传递一个额外的ObjectMap来定义皮肤所依赖的资源。

我想将其用于位图字体,因为我在运行时使用 .ttf 文件创建它们,以便能够为目标设备显示的正确 density/size 创建正确的字体大小。

这是导致问题的示例程序:

public class Test extends Game {
  private AssetManager assetManager;
  private Skin skin;
  private Batch batch;

  @Override
  public void create() {
    this.assetManager = new AssetManager();
    final FileHandleResolver resolver = new InternalFileHandleResolver();
    assetManager.setLoader(FreeTypeFontGenerator.class, new FreeTypeFontGeneratorLoader(resolver));
    assetManager.setLoader(BitmapFont.class, ".ttf", new FreetypeFontLoader(resolver));

    final ObjectMap<String, Object> resources = new ObjectMap<String, Object>();
    final FreetypeFontLoader.FreeTypeFontLoaderParameter fontParam = new FreetypeFontLoader.FreeTypeFontLoaderParameter();
    fontParam.fontFileName = "ui/font.ttf";
    fontParam.fontParameters.size = (int) (16 * Gdx.graphics.getDensity());
    assetManager.load("font16.ttf", BitmapFont.class, fontParam);
    assetManager.finishLoading();
    resources.put("font_16", assetManager.get("font16.ttf", BitmapFont.class));

    assetManager.load("ui/ui.json", Skin.class, new SkinLoader.SkinParameter(resources));
    assetManager.finishLoading();
    skin = assetManager.get("ui/ui.json", Skin.class);

    batch = new SpriteBatch();
  }

  @Override
  public void render() {
    Gdx.gl.glClearColor(0, 0, 0, 1);
    Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);

    batch.begin();
    skin.get("font_16", BitmapFont.class).draw(batch, "Test123", 10, 10);
    batch.end();
  }

  @Override
  public void dispose() {
    super.dispose();
    // enabling the next line will get rid of the Pixmap already disposed exception
    // skin.remove("font_16", BitmapFont.class);
    assetManager.dispose();
    batch.dispose();
  }
}

现在,当在程序结束时处理 assetManager 时,我得到了一个异常 "Pixmap already disposed",因为位图字体被处理了多次(一次是在处理皮肤时,一次是在处理位图字体本身时).

为了暂时解决这个问题,我在 assetManager.dispose() 之前调用了 skin.remove(fontName, BitMapFont.class) 以仅释放位图字体一旦 BUT imo 这不是一个好方法,我希望 assetManager 的依赖处理应该处理这个问题。

我检查了 SkinLoader 的代码 class,在我看来,那些传递的资源似乎没有添加为皮肤的依赖项,这就是发生此错误的原因。


我现在的问题是: 我是不是做错了什么,或者有人有工作代码来说明应该如何以正确的方式使用资源图?


找到一个与此相关的 topic,但似乎从未得到真正的答案

我得到了我需要的答案。实际上这不是资产管理人的问题。这是皮肤问题 class。

查看dispose方法:

public void dispose () {
    if (atlas != null) atlas.dispose();
    for (ObjectMap<String, Object> entry : resources.values()) {
        for (Object resource : entry.values())
            if (resource instanceof Disposable) ((Disposable)resource).dispose();
    }
}

它直接处理任何资源,当然不会反映在资产管理器中,因此资产管理器也会处理创建的位图字体。

为了解决这个问题,我编写了自己的加载程序和皮肤 class。如果有人对此感兴趣,请查看代码:

public class SkinLoader extends AsynchronousAssetLoader<Skin, SkinLoader.SkinParameter> {
public static class SkinParameter extends AssetLoaderParameters<Skin> {
    private final String fontPath;
    private final int[] fontSizesToCreate;

    public SkinParameter(final String fontPath, final int... fontSizesToCreate) {
        if (fontPath == null || fontPath.trim().isEmpty()) {
            throw new GdxRuntimeException("fontPath cannot be null or empty");
        }
        if (fontSizesToCreate.length == 0) {
            throw new GdxRuntimeException("fontSizesToCreate has to contain at least one value");
        }

        this.fontPath = fontPath;
        this.fontSizesToCreate = fontSizesToCreate;
    }
}

public SkinLoader(final FileHandleResolver resolver) {
    super(resolver);
}

@Override
@SuppressWarnings("unchecked")
public Array<AssetDescriptor> getDependencies(final String fileName, final FileHandle file, final SkinParameter parameter) {
    if (parameter == null) {
        throw new GdxRuntimeException("SkinParameter cannot be null");
    }

    // texture atlas dependency
    final Array<AssetDescriptor> dependencies = new Array<AssetDescriptor>();
    dependencies.add(new AssetDescriptor(file.pathWithoutExtension() + ".atlas", TextureAtlas.class));

    // bitmap font dependencies
    for (int fontSize : parameter.fontSizesToCreate) {
        final FreetypeFontLoader.FreeTypeFontLoaderParameter fontParam = new FreetypeFontLoader.FreeTypeFontLoaderParameter();
        fontParam.fontFileName = parameter.fontPath;
        // enable anti-aliasing
        fontParam.fontParameters.minFilter = Texture.TextureFilter.Linear;
        fontParam.fontParameters.magFilter = Texture.TextureFilter.Linear;
        // create font according to density of target device display
        fontParam.fontParameters.size = (int) (fontSize * Gdx.graphics.getDensity());
        dependencies.add(new AssetDescriptor("font" + fontSize + ".ttf", BitmapFont.class, fontParam));
    }

    return dependencies;
}

@Override
public Skin loadSync(final AssetManager manager, final String fileName, final FileHandle file, final SkinParameter parameter) {
    // load atlas and create skin
    final String textureAtlasPath = file.pathWithoutExtension() + ".atlas";
    final TextureAtlas atlas = manager.get(textureAtlasPath, TextureAtlas.class);
    final Skin skin = new Skin(atlas);

    // add bitmap fonts to skin
    for (int fontSize : parameter.fontSizesToCreate) {
        skin.add("font_" + fontSize, manager.get("font" + fontSize + ".ttf"));
    }

    // load skin now because the fonts in the json file are now available
    skin.load(file);
    return skin;
}

@Override
public void loadAsync(final AssetManager manager, final String fileName, final FileHandle file, final SkinParameter parameter) {

}}

public class Skin extends com.badlogic.gdx.scenes.scene2d.ui.Skin {
Skin(final TextureAtlas atlas) {
    super(atlas);
}

@Override
public void dispose() {
    for (String bitmapFontKey : this.getAll(BitmapFont.class).keys()) {
        remove(bitmapFontKey, BitmapFont.class);
    }
    super.dispose();
}}