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();
}}
我想知道我是否做错了什么(很可能)或者 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();
}}