[libgdx游戏开发教程]使用Libgdx进行游戏开发(4)-素材管理
游戏中总是有大量的图像资源,我们通常的做法是把要用的图片做成图片集,这样做的好处就不多说了。直接来看怎么用。
这里我们使用自己的类Assets来管理它们,让这个工具类作为我们的资源管家,从而可以在任何类里使用我们加载的素材。
原始图片素材:https://files.cnblogs.com/mignet/assets-raw.zip
1,使用Libgdx提供的打包程序
在最新版里是叫TexturePacker.process
在任意main方法里添加:
private static boolean rebuildAtlas = true; private static boolean drawDebugOutline = true; public static void main(String[] args) { if (rebuildAtlas) { Settings settings = new Settings(); settings.maxWidth = 1024; settings.maxHeight = 1024; settings.debug = drawDebugOutline; TexturePacker2.process(settings, "../core/assets/images", "/assets/images", "canyonbunny.pack"); } }
如果使用Android Studio,先设置路径
然后代码做如下修改:
TexturePacker.process(settings, "images", "images", "canyonbunny");
说明:这是最新版libGDX 1.9.4的用法
2,使用Libgdx提供的打包工具
http://www.codeandweb.com/texturepacker
现在打包好了,怎么用呢?这样:
TextureAtlas atlas = assetManager.get("images/canyonbunny.pack");
head = atlas.findRegion("bunny_head");
我们都知道一般Texture的用法,通常少不了下面三步:
1. Texture texture = new Texture(Gdx.files.internal("texture.png"));//prepare 2. batch.draw(texture, x, y);//draw 3. texture.dispose();//dispose
但是这样引起的问题是,如果我们有很多很多资源,这样会使代码很杂乱。
根据面向对象的思想,我们这时需要对对象各自处理自己的资源了,为了统一管理资源(包括加载资源,重加载资源,跟踪资源,卸载资源),Libgdx 提供了AssetManager这个类。
它可以异步加载资源,但实际上我们不能简单的给个资源列表给它就完事了。我们需要自己的资源类来组织资源并保证在任何地方可以使用它。
首先定义一个常量来指向我们的atlas文件:
public class Constants { // Visible game world is 5 meters wide public static final float VIEWPORT_WIDTH = 5.0f; // Visible game world is 5 meters tall public static final float VIEWPORT_HEIGHT = 5.0f; // Location of description file for texture atlas public static final String TEXTURE_ATLAS_OBJECTS = "images/canyonbunny.pack";//最新版本里打包之后是使用atlas的文件名:canyonbunny.atlas }
然后创建我们的资源管理类Assets(当然它是单例的)
package com.packtpub.libgdx.canyonbunny.game; import com.badlogic.gdx.Gdx; import com.badlogic.gdx.assets.AssetErrorListener; import com.badlogic.gdx.assets.AssetManager; import com.badlogic.gdx.graphics.g2d.TextureAtlas; import com.badlogic.gdx.utils.Disposable; import com.packtpub.libgdx.canyonbunny.util.Constants; public class Assets implements Disposable, AssetErrorListener { public static final String TAG = Assets.class.getName(); public static final Assets instance = new Assets(); private AssetManager assetManager; // singleton: prevent instantiation from other classes private Assets() { } public void init(AssetManager assetManager) { this.assetManager = assetManager; // set asset manager error handler assetManager.setErrorListener(this); // load texture atlas assetManager.load(Constants.TEXTURE_ATLAS_OBJECTS, TextureAtlas.class); // start loading assets and wait until finished assetManager.finishLoading(); Gdx.app.debug(TAG, "# of assets loaded: " + assetManager.getAssetNames().size); for (String a : assetManager.getAssetNames()) Gdx.app.debug(TAG, "asset: " + a); } @Override public void dispose() { assetManager.dispose(); } @Override public void error(String filename, Class type, Throwable throwable) { Gdx.app.error(TAG, "Couldn't load asset '" + filename + "'", (Exception) throwable); } }
接下来就是从atlas里面取子图了,使用findRegion(“name”),注意这个查找方法是一个很昂贵的功能调用,所以调用之后就把结果存起来吧,避免重复调用该方法造成性能问题。
为了达到这一目的,我们在该类中增加很多小的内部类以便于在逻辑上组织他们并完成持久化存储。以兔子头为例:
public class AssetBunny { public final AtlasRegion head; public AssetBunny(TextureAtlas atlas) { head = atlas.findRegion("bunny_head"); } }
其他的像岩石啊金币啊装饰物啊等等一一添加。
public class AssetRock { public final AtlasRegion edge; public final AtlasRegion middle; public AssetRock(TextureAtlas atlas) { edge = atlas.findRegion("rock_edge"); middle = atlas.findRegion("rock_middle"); } } public class AssetGoldCoin { public final AtlasRegion goldCoin; public AssetGoldCoin(TextureAtlas atlas) { goldCoin = atlas.findRegion("item_gold_coin"); } } public class AssetFeather { public final AtlasRegion feather; public AssetFeather(TextureAtlas atlas) { feather = atlas.findRegion("item_feather"); } } public class AssetLevelDecoration { public final AtlasRegion cloud01; public final AtlasRegion cloud02; public final AtlasRegion cloud03; public final AtlasRegion mountainLeft; public final AtlasRegion mountainRight; public final AtlasRegion waterOverlay; public AssetLevelDecoration(TextureAtlas atlas) { cloud01 = atlas.findRegion("cloud01"); cloud02 = atlas.findRegion("cloud02"); cloud03 = atlas.findRegion("cloud03"); mountainLeft = atlas.findRegion("mountain_left"); mountainRight = atlas.findRegion("mountain_right"); waterOverlay = atlas.findRegion("water_overlay"); } }
现在我们需要Assert类提供外部的对象供系统在其他地方调用
public AssetBunny bunny; public AssetRock rock; public AssetGoldCoin goldCoin; public AssetFeather feather; public AssetLevelDecoration levelDecoration; public void init(AssetManager assetManager) { this.assetManager = assetManager; // set asset manager error handler assetManager.setErrorListener(this); // load texture atlas assetManager.load(Constants.TEXTURE_ATLAS_OBJECTS, TextureAtlas.class); // start loading assets and wait until finished assetManager.finishLoading(); Gdx.app.debug(TAG,"# of assets loaded: " + assetManager.getAssetNames().size); for (String a : assetManager.getAssetNames()) Gdx.app.debug(TAG, "asset: " + a); TextureAtlas atlas = assetManager.get(Constants.TEXTURE_ATLAS_OBJECTS); // enable texture filtering for pixel smoothing for (Texture t : atlas.getTextures()) t.setFilter(TextureFilter.Linear, TextureFilter.Linear); // create game resource objects bunny = new AssetBunny(atlas); rock = new AssetRock(atlas); goldCoin = new AssetGoldCoin(atlas); feather = new AssetFeather(atlas); levelDecoration = new AssetLevelDecoration(atlas); }
注意到程序中使用的参数TextureFilter.Linear,这里的两个参数分别对应图片放大和缩小的模式。默认的模式是TextureFilter.Nearest。看看它们的区别
ok,一切就绪,拉出来遛遛。
使用资源的话,最基本有3个地方:加载,重加载,卸载。
在游戏开始之前加载,那么应该是Main的create里:Assets.instance.init(new AssetManager());
重加载一般在返回游戏的时候(resume方法):Assets.instance.init(new AssetManager());
卸载当然是在dispose里:Assets.instance.dispose();
最后,将我们前面用的TestSprites(还记得那5个箱子吧)替换成现在的Texture:(这里使用了Libgdx的Array的random功能,随机返回Array的项)
private void initTestObjects () { // Create new array for 5 sprites testSprites = new Sprite[5]; // Create a list of texture regions Array<TextureRegion> regions = new Array<TextureRegion>(); regions.add(Assets.instance.bunny.head); regions.add(Assets.instance.feather.feather); regions.add(Assets.instance.goldCoin.goldCoin); // Create new sprites using a random texture region for (int i = 0; i < testSprites.length; i++) { Sprite spr = new Sprite(regions.random()); // Define sprite size to be 1m x 1m in game world spr.setSize(1, 1); // Set origin to sprite's center spr.setOrigin(spr.getWidth() / 2.0f, spr.getHeight() / 2.0f); // Calculate random position for sprite float randomX = MathUtils.random(-2.0f, 2.0f); float randomY = MathUtils.random(-2.0f, 2.0f); spr.setPosition(randomX, randomY); // Put new sprite into array testSprites[i] = spr; } // Set first sprite as selected one selectedSprite = 0; }
跑起,你应该看到类似下面这样的画面了,素材的使用让游戏正式向艺术迈进了。
红色的边框,就是在第一步打包时设置drawDebugOutline=true的效果。