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

转载声明:http://www.cnblogs.com/htynkn/archive/2011/11/15/libgdx_4.html

我们先看几个游戏截图再来理解何为舞台。

12942M3Q423F-45025

请仔细观察图片中的元素,有些东西是不能动,有些可以动,有些有特效,有些没有。有些是按钮,有些是图片,但是其实它们都可以统一称为演员(Actor)。

而整个游戏界面就是我们的舞台。

12942M3Q423F-45025 - 副本

再看一个射击类游戏

f2316c3c29ce2660f93dce72a0944453

而其中的演员是

f2316c3c29ce2660f93dce72a0944453

演员是游戏设计中常用的一个对象,它接受舞台的统一管理,拥有一些公共的事件,比如触摸,点击,但是同时还有自身的响应和属性。

而舞台就是容纳演员的场所。它统一管理所有演员,接受输入,同时提供一个方便的框架操作演员的时间变化。

我们来看一下Stage类:

1 protected final Group root;
2 protected final SpriteBatch batch;
3 protected Camera camera;

它拥有一个Group,一个SpriteBatch,还有一个相机。

SpriteBatch我们在前几篇说过,这里就不再重复了。

Group是一个类,用于容纳和控制演员。但是这里要注意Group本身其实也是继承自Actor。

相机我们这里跳过,以后再说,可以暂时理解成一个控制观察视角和指标转化的工具。

当我们拥有一个演员后就可以调用addActor方法加入舞台。

舞台可以获取输入,但是需要设置。

1 Gdx.input.setInputProcessor(stage);

下面来个列子,控制一个人物前进。

actor1

控制人物的按钮:

narrow

将所需的图片放到assert中

所需资源

新建三个类:

FirstGame,实现接口ApplicationListener

FirstActor,继承Actor

NarrowButton,继承Actor

先看一下FirstGame

声明一个Stage,然后实例化FirstActor和NarrowButton,将二者加入舞台中,最后设置输入响应为Stage。

 1 package com.cnblogs.htynkn.listener;
 2  
 3 import java.util.Date;
 4 import java.util.Random;
 5  
 6 import javax.microedition.khronos.opengles.GL;
 7  
 8 import android.util.Log;
 9  
10 import com.badlogic.gdx.ApplicationListener;
11 import com.badlogic.gdx.Gdx;
12 import com.badlogic.gdx.graphics.GL10;
13 import com.badlogic.gdx.graphics.g2d.BitmapFont;
14 import com.badlogic.gdx.scenes.scene2d.Stage;
15 import com.cnblogs.htynkn.domain.FirstActor;
16 import com.cnblogs.htynkn.domain.NarrowButton;
17  
18 public class FirstGame implements ApplicationListener {
19  
20     private Stage stage;
21     private FirstActor firstActor;
22     private NarrowButton button;
23  
24     @Override
25     public void create() {
26         stage = new Stage(Gdx.graphics.getWidth(), Gdx.graphics.getHeight(),
27                 true);
28         firstActor = new FirstActor("renwu");
29         button = new NarrowButton("narrow");
30         stage.addActor(firstActor);
31         stage.addActor(button);
32         Gdx.input.setInputProcessor(stage);
33     }
34  
35     @Override
36     public void dispose() {
37         stage.dispose();
38     }
39  
40     @Override
41     public void pause() {
42         // TODO Auto-generated method stub
43  
44     }
45  
46     @Override
47     public void render() {
48         Gdx.gl.glClear(GL10.GL_COLOR_BUFFER_BIT);
49         stage.act(Gdx.graphics.getDeltaTime());
50         stage.draw();
51     }
52  
53     @Override
54     public void resize(int width, int height) {
55         // TODO Auto-generated method stub
56  
57     }
58  
59     @Override
60     public void resume() {
61         // TODO Auto-generated method stub
62  
63     }
64 }

再看一下FirstActor。

声明一个Texture用于绘制。在构造方法中获取到高度和宽度,以便于后期的hit时间判断。

 1 package com.cnblogs.htynkn.domain;
 2  
 3 import com.badlogic.gdx.Gdx;
 4 import com.badlogic.gdx.graphics.Texture;
 5 import com.badlogic.gdx.graphics.g2d.SpriteBatch;
 6 import com.badlogic.gdx.scenes.scene2d.Actor;
 7  
 8 public class FirstActor extends Actor {
 9  
10     Texture texture;
11  
12     @Override
13     public void draw(SpriteBatch batch, float parentAlpha) {
14         batch.draw(texture, this.x, this.y);
15     }
16  
17     @Override
18     public Actor hit(float x, float y) {
19         if (x > 0 && y > 0 && this.height > y && this.width > x) {
20             return this;
21         } else {
22             return null;
23         }
24     }
25  
26     @Override
27     public boolean touchDown(float x, float y, int pointer) {
28         // TODO Auto-generated method stub
29         return false;
30     }
31  
32     @Override
33     public void touchDragged(float x, float y, int pointer) {
34         // TODO Auto-generated method stub
35  
36     }
37  
38     @Override
39     public void touchUp(float x, float y, int pointer) {
40         // TODO Auto-generated method stub
41  
42     }
43  
44     public FirstActor(String name) {
45         super(name);
46         texture = new Texture(Gdx.files.internal("actor1.gif"));
47         this.height = texture.getHeight();
48         this.width = texture.getWidth();
49     }
50 }

NarrowButton中代码绘制部分和上面的以下,主要是有个点击后控制人物行动的问题。

修改touchDown事件:

通过Group获取到FirstActor,控制x值。

1 public boolean touchDown(float x, float y, int pointer) {
2         Actor actor = this.parent.findActor("renwu");
3         actor.x += 10;
4         return false;
5     }

效果:

ui类1ui类2

到此为止一个最简单的人物控制我们已经实现了。但是这个有实例还有很多可以改进的地方,比如方向按钮没有点击效果,人物没有移动效果。

我们可以使用Animation来实现。添加一张图片

actor2

具体的原理我们看一下Animation类:

 1 public class Animation {
 2     final TextureRegion[] keyFrames;
 3     public float frameDuration;
 4  
 5     /** Constructor, storing the frame duration and key frames.
 6      *
 7      * @param frameDuration the time between frames in seconds.
 8      * @param keyFrames the {@link TextureRegion}s representing the frames. */
 9     public Animation (float frameDuration, List keyFrames) {
10         this.frameDuration = frameDuration;
11         this.keyFrames = new TextureRegion[keyFrames.size()];
12         for(int i = 0, n = keyFrames.size(); i < n; i++) {
13             this.keyFrames[i] = (TextureRegion)keyFrames.get(i);
14         }
15     }
16      
17     /** Constructor, storing the frame duration and key frames.
18      *
19      * @param frameDuration the time between frames in seconds.
20      * @param keyFrames the {@link TextureRegion}s representing the frames. */
21     public Animation (float frameDuration, TextureRegion... keyFrames) {
22         this.frameDuration = frameDuration;
23         this.keyFrames = keyFrames;
24     }
25  
26     /** Returns a {@link TextureRegion} based on the so called state time. This is the amount of seconds an object has spent in the
27      * state this Animation instance represents, e.g. running, jumping and so on. The mode specifies whether the animation is
28      * looping or not.
29      * @param stateTime the time spent in the state represented by this animation.
30      * @param looping whether the animation is looping or not.
31      * @return the TextureRegion representing the frame of animation for the given state time. */
32     public TextureRegion getKeyFrame (float stateTime, boolean looping) {
33         int frameNumber = (int)(stateTime / frameDuration);
34  
35         if (!looping) {
36             frameNumber = Math.min(keyFrames.length - 1, frameNumber);
37         } else {
38             frameNumber = frameNumber % keyFrames.length;
39         }
40         return keyFrames[frameNumber];
41     }
42 }

可以看出所谓的动画其实是一张一张的图片不断切换(其实所有的动画都是这个样子的)。

我们构造一个图片列表然后根据事件变动不停取出,重新绘制就形成动画了。

注意一下传入的时间和图片列表大小的问题,修改FirstActor代码如下:

 1 package com.cnblogs.htynkn.domain;
 2  
 3 import com.badlogic.gdx.Gdx;
 4 import com.badlogic.gdx.graphics.Texture;
 5 import com.badlogic.gdx.graphics.g2d.Animation;
 6 import com.badlogic.gdx.graphics.g2d.SpriteBatch;
 7 import com.badlogic.gdx.graphics.g2d.TextureRegion;
 8 import com.badlogic.gdx.scenes.scene2d.Actor;
 9  
10 public class FirstActor extends Actor {
11  
12     Texture texture1;
13     Texture texture2;
14     Animation animation;
15     TextureRegion[] walksFrame;
16     float stateTime;
17  
18     @Override
19     public void draw(SpriteBatch batch, float parentAlpha) {
20         stateTime += Gdx.graphics.getDeltaTime();
21         TextureRegion currentFrame = animation.getKeyFrame(stateTime, true);
22         batch.draw(currentFrame, this.x, this.y);
23     }
24  
25     @Override
26     public Actor hit(float x, float y) {
27         Gdx.app.log("INFO", x + " " + this.width);
28         if (x > 0 && y > 0 && this.height > y && this.width > x) {
29             return this;
30         } else {
31             return null;
32         }
33     }
34  
35     @Override
36     public boolean touchDown(float x, float y, int pointer) {
37         // TODO Auto-generated method stub
38         return false;
39     }
40  
41     @Override
42     public void touchDragged(float x, float y, int pointer) {
43         // TODO Auto-generated method stub
44  
45     }
46  
47     @Override
48     public void touchUp(float x, float y, int pointer) {
49         // TODO Auto-generated method stub
50  
51     }
52  
53     public FirstActor(String name) {
54         super(name);
55         texture1 = new Texture(Gdx.files.internal("actor1.gif"));
56         texture2 = new Texture(Gdx.files.internal("actor2.gif"));
57         this.height = texture1.getHeight();
58         this.width = texture1.getWidth();
59         TextureRegion region1;
60         TextureRegion region2;
61         region1 = new TextureRegion(texture1);
62         region2 = new TextureRegion(texture2);
63         walksFrame = new TextureRegion[30];
64         for (int i = 0; i < 30; i++) {
65             if (i % 2 == 0) {
66                 walksFrame[i] = region1;
67             } else {
68                 walksFrame[i] = region2;
69             }
70         }
71         animation = new Animation(0.25f, walksFrame);
72     }
73 }

效果:

2011111423585672

这里注意一下,为什么我们要Texture转为TextureRegion。这是因为在实际开发中的图片是集成在一起的,比如所有角色要用的图片都是放在一张图里,然后分割截取的,对应的辅助方法TextureRegion.split。

另外我们可以发现NarrowButton和FirstActor中有大量代码重复了,可能有朋友觉得应该提取一下,其实libgdx已经帮我们做了,可以参考

ui类

这里有一些常用的UI控件,估计下一篇可以讲到。

posted @ 2013-02-04 20:36  王世桢  阅读(198)  评论(0)    收藏  举报