libgdx游戏引擎教程(四) 游戏正式开始,资源异步加载(附源码)

转自:http://www.apkbus.com/android-57716-1-1.html

今天是Testin Mkey libgdx游戏教程的第四讲。昨天我们在外篇(一)中用很短的时间自定义出了一个样子看起来还不算太糟的自定义进度条,但是这个进度条没有什么实际的意义,仅仅是能够自动的进行进度的变化。那么,今天我们就要利用这个进度条来做一些实际的工作了。如果大家还没有看昨天那篇

libgdx游戏教程外篇()优美的自定义进度条(附源码)


 

建议大家还是先看一下,对我和大家一起完成的那个进度条的工作机制有一个很好的了解,今天这篇文章大家理解起来就很轻松了。


 

请原谅我在考虑教程内容安排上的考虑而把资源载入放在前面先讲。个人窃以为没有资源载入的正确方法和习惯,在游戏的后期是非常麻烦的。作为一个非常棒的游戏开发引擎,Libgdx肯定是拿来做游戏的(哈哈,这好像是废话),而游戏就有一个最大的特点,就是图片资源和音频资源都非常地庞大,越是精美的游戏,越需要强大的图形渲染来支撑。


 

那么,图片的加载就是一个很大的问题。我们不可能直接在UI线程中去加载,在我昨天给大家看的那个我现在正在编写的宠物对战游戏中,我用到了大量的图片来形成宠物的一帧帧的动作,因此加载问题就是一个不得不解决的问题。我曾经尝试过直接加载或者独立新开一个线程去加载图片资源文件,效果都很不理想。


 

直接加载接口启动速度太慢,会有很长时间的黑屏,想来没有人愿意玩这样的游戏。自己独立开一个线程加载也存在很大的问题,由于Android和java复杂的相互制约的初始化机制和垃圾回收机制,资源很可能还没有加载完成就被使用,在Libgdx中的典型症状就是纹理花屏(因此大家若在自己学习的过程中遇见了花屏的症状,一定不要着急,这是资源没有完全加载或者资源被回收的情况,大家可以使用下面要具体分析的AssetManager或者在每次需要显示的时候都重新把资源加载一次,当然这需要资源不太大的情况下才比较合适)。 资源自动回收这个问题就更为复杂,系统会在内存不够的时候回收一些资源,这是我们更难掌控的。


 

好在强大的libgdx引擎是专门为游戏设计的引擎,它提供了一个非常有用的工具,即为AssetManager,简单的说就是一个资源异步加载和资源自动管理类,具体的介绍可以看巴士内的帖子:


 

android游戏开发框架libgdx的使用(二十)资源预加载与AssetManager的使用


 


 

今天大家完全不需要把这个AssetManager完全搞懂,只要先大概知道怎么加载资源并且获得资源就可以了。一些复杂的参数一般也不常用。


 

在加载前,AssetManager需要知道加载什么类型的资源,这个功能通过资源加载器实现。有两个变量,同步资源加载器SynchronourAssetLoader和异步资源加载器AsynchronousAssetLoader。前者加载任何资源都在渲染进程中,后者加载资源在另外一个线程中。比如说,一个Texture需要一个Pixmap,然后加载OpenGL依赖于渲染线程。



 

这里我解释一下,所谓的AssetManager异步加载是指相对于我们开发者而言的异步,因为我们主要的显示操作是在UI线程上,因此AssetManager的加载是异步的。但为什么这里又有同步加载和异步加载之分呢?这里的同步和异步是相对渲染线程而言的,简单的理解就是相对系统专门用来调用render()函数的那个线程而言的。那么这样就很简单了,所谓异步加载就是另开一个线程而不在渲染线程里面,同步就是在render()线程里加载咯…而这两种加载方式相对UI线程都是异步的。



 

下面的资源类型可以由AssetManager直接创建。
· Pixmaps 通过 PixmapLoader
· Textures 通过 TextureLoader
· BitmapFonts 通过BitmapFontLoader
· TextureAtlases 通过TextureAtlasLoader
· TiledAtlases 通过TiledAtlasLoader
· TileMapRenderers 通过 TileMapRendererLoader
· Music instances 通过 MusicLoader
· Sound instances 通过 SoundLoader



 

加载某个资源很简单:
manager.load("data/testin.png", Texture.class);
manager.load("data/ testin.fnt", BitmapFont.class);


 

目前为止,我们只是将资源放入加载队列,AssetManager还没有载入任何东西。我们必须不断调用AssetManager的update()才能将队列中的资源不断加载进来。



 

什么地方能让我们不停调用update()方法呢?难道再新开一个线程?想来大家心中都已经有了答案。没错就是在Progress(还记得吗,这个继承自ApplicationListener)的render()函数中去不停调用update()就可以了,而且这是系统自动调用的,不用我们操心,很省心吧,哈哈。



 

噢,对了,这里还有一个小问题要提醒一下大家,AssetManager在API里也有,大家写import的时候不要写错了。
  1. public Progress implements ApplicationListener {

  2. public void render() {
  3. if(manager.update()) {
  4. // 完成加载,进行跳转
  5. }

  6. }
  7. }
复制代码
为了画出动画,我们先新建一个类,AnimalActor ,这是一个Actor,也就是libgdx中最常说的演员了。这里建议大家养成良好的习惯,一般这种持有较多资源的类建议都继承Disposable接口,便于最后资源的释放和回收。



 

至于其中用到的动画,由Texture和Animation配合,大家可以看一下官方的API,可以在下载好的libgdx文件夹下的docs/api/index里面查到。或者参加巴士内一文,其中也有Actor和Animation的使用


 

android游戏开发框架libgdx的使用(四)--舞台和演员


 

下面是AnimalActor的代码:


 

  1. public class AnimalActor extends Actor implements Disposable{
  2. ArrayList<Texture> TexArray=new ArrayList<Texture>();
  3. ArrayList<TextureRegion> TexReArray=new ArrayList<TextureRegion>();
  4. Animation animation;

  5. TextureRegion[] walksFrame;
  6. float stateTime;
  7. TextureRegion currentFrame;//当前帧
  8. AssetManager manager;//保存Progress里面保存的manager实例
  9. @Override
  10. public void draw(SpriteBatch arg0, float arg1) {
  11. // TODO Auto-generated method stub
  12. stateTime += Gdx.graphics.getDeltaTime();
  13. //得到下一帧
  14. currentFrame = animation.getKeyFrame(stateTime, true);
  15. //以(0,0)绘制为起点(左下角为0,0)画出动画,大小128*128
  16. arg0.draw(currentFrame,0, 0,128,128);
  17. }


  18. public AnimalActor(AssetManager manager) {
  19. super();
  20. //关联Progress内的AssetManager
  21. this.manager=manager;
  22. }


  23. @Override
  24. public Actor hit(float x, float y) {
  25. // TODO Auto-generated method stub
  26. return this;
  27. }

  28. @Override
  29. public boolean touchDown(float arg0, float arg1, int arg2) {
  30. // TODO Auto-generated method stub
  31. return false;
  32. }

  33. @Override
  34. public void touchDragged(float arg0, float arg1, int arg2) {
  35. // TODO Auto-generated method stub

  36. }

  37. @Override
  38. public void touchUp(float arg0, float arg1, int arg2) {
  39. // TODO Auto-generated method stub

  40. }

  41. //初始化方法,在Progress中的AssetManager初始化完成后通知AnimalActor初始化
  42. public void iniResource(){
  43. Texture tex;
  44. int j;
  45. for(int i=1;i<30;i++){
  46. TexArray.add(manager.get("animal/"+i+".png", Texture.class));
  47. }
  48. // TODO Auto-generated constructor stub

  49. for(int i=0;i<TexArray.size();i++){
  50. tex=TexArray.get(i);
  51. TextureRegion temtex=new TextureRegion(tex);
  52. TexReArray.add(temtex);
  53. }

  54. j=TexReArray.size();
  55. walksFrame=new TextureRegion[j];
  56. for(int i=0;i<j;i++)
  57. walksFrame[i]=TexReArray.get(i);

  58. //设置的是0.06s一帧
  59. animation = new Animation(0.06f, walksFrame);

  60. }


  61. @Override
  62. public void dispose() {
  63. // TODO Auto-generated method stub

  64. //释放AnimalActor中持有的资源
  65. for(int i=0;i<TexArray.size();i++)
  66. TexArray.get(i).dispose();
  67. }
  68. }
复制代码
其中大部分难点都在源码中做出了注释。
ProgressBar不需要做任何的修改,这就是封装的好处了。
当然Progress类需要做一些修改,比如用于加载的AssetManger,AnimalActor对象。
  1. public class Progress implements ApplicationListener {
  2. ProgressBar bar;
  3. AnimalActor animal;
  4. Stage stage;
  5. AssetManager manager;
  6. boolean hasini;
  7. @Override
  8. public void create() {
  9. // TODO Auto-generated method stub
  10. bar=new ProgressBar(0,0);
  11. //新建一个舞台
  12. stage=new Stage(Gdx.graphics.getWidth(),Gdx.graphics.getHeight(), true);
  13. stage.addActor(bar);
  14. //记得初始化一下AssetManager实例
  15. manager=new AssetManager();
  16. //传入AssetManger的引用,便于animal的资源初始化,但是注意了,只有在调用iniResourse()后资源才被初始化
  17. animal=new AnimalActor(manager);
  18. //把资源加入载入列表,这里我放了一个29帧的动画,在asset文件夹下animal下有29张图片
  19. for(int i=1;i<30;i++){
  20. manager.load("animal/"+i+".png", Texture.class);
  21. }
  22. }

  23. @Override
  24. public void dispose() {
  25. // TODO Auto-generated method stub
  26. //同样的,在结束时释放资源
  27. bar.dispose();
  28. animal.dispose();
  29. manager.clear();
  30. manager.dispose();
  31. }

  32. @Override
  33. public void pause() {
  34. // TODO Auto-generated method stub

  35. }

  36. @Override
  37. public void render() {
  38. // TODO Auto-generated method stub
  39. Gdx.gl.glClear(GL10.GL_COLOR_BUFFER_BIT);
  40. Gdx.gl.glClearColor(1f,1f,1f,0f);
  41. stage.act(Gdx.graphics.getDeltaTime());
  42. stage.draw();
  43. if(!manager.update()){
  44. bar.setProgress(manager.getProgress()*100);
  45. }
  46. //加载完成且之前没有初始化过AnimalActor,且在手触摸屏幕时初始化AnimalActor,并将进度条从舞台中移除,并加入AnimalActor对象
  47. if(!hasini&&manager.update()&&Gdx.input.isTouched()){
  48. stage.removeActor(bar);
  49. animal.iniResource();
  50. stage.addActor(animal);
  51. hasini=true;
  52. }
  53. //我们做一个标记,看看未加载(Queued)完成的资源和已载入完成的资源的数量(Loaded)
  54. if(!manager.update()){
  55. System.out.println("QueuedAssets:"+manager.getQueuedAssets());
  56. System.out.println("LoadedAssets:"+manager.getLoadedAssets());
  57. System.out.println("Progress:"+manager.getProgress());
  58. }
  59. }

  60. @Override
  61. public void resize(int arg0, int arg1) {
  62. // TODO Auto-generated method stub

  63. }

  64. @Override
  65. public void resume() {
  66. // TODO Auto-generated method stub

  67. }

  68. }
复制代码
按照老规矩,我会对其中的难点做出一些解释。manager.load()方法只是将资源放入加载列表,而真正的加载要不停地调用manager.update()才可以,且update()函数有一个返回值,载入完成返回true,未完成返回false.



 

为了使大家更直观一些,我加了几条语句:
  1. if(!manager.update()){
  2. System.out.println("QueuedAssets:"+manager.getQueuedAssets());
  3. System.out.println("LoadedAssets:"+manager.getLoadedAssets());
  4. System.out.println("Progress:"+manager.getProgress());
  5. }
复制代码
从名字我们就可以看出,manager.getQueuedAssets()是获得还在载入列表中也就是还没有完成载入的资源的个数,manager.getLoadedAssets()则是已经载入完成的资源的个数。我们可以在输出中看看未加载(Queued)完成的资源和已载入完成的资源的数量(Loaded)的个数的变化。


dispose()方法中记得把animal和manager一并回收了。


老样子,启动Progress:


 

  1. public class LibgdxActivity extends AndroidApplication {
  2. Progress progress;
  3. @Override
  4. public void onCreate(Bundle savedInstanceState) {
  5. super.onCreate(savedInstanceState);
  6. progress=new Progress();
  7. initialize(progress, false);
  8. }
  9. }
复制代码
接下来我们来看看效果


Screenshot_2012-07-05-14-08-46.png

2012-7-5 14:15 上传
下载附件(26.56 KB)



加载完成了,记得要点一下屏幕才能继续哦~!咱们在代码中用一两句就实现了加载完成,触摸继续游戏的功能,是不是很简单呢?如果不记得了,回头看看上面的源码哈!


Screenshot_2012-07-05-14-08-52.png

2012-7-5 14:15 上传
下载附件(36.4 KB)


这货其实是动画=。=大家实际看看就知道了,很快有没有!Libgdx的效率!



 

看看输出吧





 

未命名.jpg
2012-7-5 14:15 上传
下载附件(284.27 KB)
这就是第四讲,希望大家继续支持Testin杯——Mkey libgdx游戏引擎系列教程!


Testin ID:ilovemkey@gmail.com 总的而言这个平台真的非常好用!我正是用这个平台发现了现在写的这个应用的一些内存管理方面的问题。另外建议能够在测试报告中看到某一台机型在整个执行过程中的cpu和内存占用的变化情况,这样可以更快地锁定问题。感谢Itestin让测试变得轻松了太多太多!

这里是源代码哦!

edu.nju.wsj.libgdx.rar(3.52 MB, 下载次数: 846)

2012-7-5 14:26 上传
点击文件名下载附件
下载积分: 下载豆 -2

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值