LibGDX游戏引擎-7-演员类的使用(Actor)

上一节已经讲了常见演员的使用,本篇就整体说一下演员.

Actor类
API定义:

在二维场景图中,一个演员拥有的位置,矩形的大小,起点,规模,旋转,和颜色等属性。位置对应且不成比例。演员的位置是相对于演员的父母,它的起点是相对位置,同时可用于缩放和旋转。一个演员也有一个行动,可以操纵的演员在时间段内活动,同时可以加入监听来实现演员接收事件通知。

功能分析:

(1)实现一个可以移动的人物。
(2)人物在不同时间段是播放不同的动画。
(3)人物分3中状态:左走、右走、空闲。
(4)通过2个按钮来实现状态的切换。
(5)添加游戏背景。

编码步骤:

(1)在核心代码项目中,新建一个Person的类,使Person继承Actor类,然后重写其中的draw方法。

public class Person extends Actor

 

(2)声明变量,将坐标、时间、imagebutton、texture、动画、以及枚举类型等变量声明。

public class Person extends Actor {
public float x;
public float y;
public float statetime;
//按钮
ImageButton btn_L;
ImageButton btn_R;
//人物当前状态(枚举类)
STATE state;
//对应的动画
Animation animation_left;
Animation animation_right;
Animation animation_idle;
//实时动画
TextureRegion currentFrame;

enum STATE {
L,R,I;
};
}
 

(3)设置成员变量。

public Person(float x, float y) {
super();
this.x = x;
this.y = y;
this.statetime = 0;
this.show();
state = STATE.I;
}

 

(4)动画设置。

动画设置,都写在了show方法里面,这样其实不太好,很乱思路不清晰,这里作为例子就暂且这样
TextureRegion中有一个flip(boolean x, boolean y) 方法,他有2个参数。
第一个参数是负责确定是否沿X轴方向翻转,
第二个参数是负责确定是否沿Y轴方向翻转。

public void show() {

//分割动画
Texture animation_file = new Texture(Gdx.files.internal(“data/animation2-2.png”));
TextureRegion[][] animation_split_right = TextureRegion.split(animation_file,42,51);
TextureRegion[][] animation_split_left = TextureRegion.split(animation_file,42,51);
Texture animation_file2 = new Texture(Gdx.files.internal(“data/idle.png”));
TextureRegion[][] animation_split_idle = TextureRegion.split(animation_file2,42,51);

//实例化动画图片组
TextureRegion[] textureRegion_right = new TextureRegion[30];
TextureRegion[] textureRegion_left = new TextureRegion[30];
TextureRegion[] textureRegion_idle = new TextureRegion[1];

//向右动画图片组
int i = 0;
for(TextureRegion[] animation_split_right_split:animation_split_right){
for(TextureRegion animation_pic:animation_split_right_split){
textureRegion_right[i] = animation_pic;
i++;
}
}

//向左动画图片组
i = 0;
for(TextureRegion[] animation_split_left_split:animation_split_left){
for(TextureRegion animation_pic:animation_split_left_split){
textureRegion_left[i] = animation_pic;
textureRegion_left[i].flip(true,false);
i++;
}
}

//原地动画图片组
textureRegion_idle[0] = animation_split_idle[0][0];

//合成动画
animation_right = new Animation(0.08f,textureRegion_right);
animation_right.setPlayMode(Animation.LOOP);
animation_left = new Animation(0.08f,textureRegion_left);
animation_left.setPlayMode(Animation.LOOP);
animation_idle = new Animation(1f,textureRegion_idle);
animation_idle.setPlayMode(Animation.LOOP);

//按钮图片读取
Texture button_file = new Texture(Gdx.files.internal(“data/button.png”));
TextureRegion[][] button_split = TextureRegion.split(button_file,100,100);

//按钮实例化
btn_R = new ImageButton(
new TextureRegionDrawable(button_split[0][0]),
new TextureRegionDrawable(button_split[0][1]));
btn_L = new ImageButton(
new TextureRegionDrawable(button_split[0][2]),
new TextureRegionDrawable(button_split[0][3]));

//按钮位置
btn_R.setPosition(150,20);
btn_L.setPosition(20,20);
}
(5)设置按钮同时加入监听。
PS:这里的touchdown()方法重写的时候一定要将后面的super返回值,
一定要修改为return true。因为只有按下方法返回true的情况下,touchup方法才执行,
这个需要大家注意,同时还touchup执行的时候要将状态赋会idle。

//按钮监听 (show 方法中继续添加监听即可)
btn_R.addListener(new InputListener(){
@Override
public boolean touchDown(InputEvent event, float x, float y,
int pointer, int button) {
state = STATE.R;
return true;
}
@Override
public void touchUp(InputEvent event, float x, float y,
int pointer, int button) {
state = STATE.I;
super.touchUp(event, x, y, pointer, button);
}
});

btn_L.addListener(new InputListener(){
@Override
public boolean touchDown(InputEvent event, float x, float y,
int pointer, int button) {
state = STATE.L;
return true;
}
@Override
public void touchUp(InputEvent event, float x, float y,
int pointer, int button) {
state = STATE.I;
super.touchUp(event, x, y, pointer, button);
}
});
 

(6)状态判断与动画选择

由于我们将人物动画分为左走的动画、右走的动画、和空闲的动画,这样我们就要在不同的状态下,使用不同的动画,这样才能符合游戏逻辑,我们创建一个check()方法来实现动画和状态的关联。这里我们用currentFrame来给不同的状态赋值,最后将currentFrame画出来,这样可以节省许多代码。

//状态控制与动画选择
public void check() {
if(this.state==STATE.L){
currentFrame = animation_left.getKeyFrame(statetime,true);
}else if(this.state==STATE.R){
currentFrame = animation_right.getKeyFrame(statetime,true);
}else if(this.state==STATE.I){
currentFrame = animation_idle.getKeyFrame(statetime,true);
}
}

(7)人物移动。

其实人物移动的原理很简单,但是由于人类思考的误区,总是理解的不很清楚。为了构造人物移动的效果,我们只是在不断变化的坐标的上,绘画出我们做好的动画。这个坐标,我们需要和人物的状态联系起来,人物的状态又是按钮改变的,通过这几种联系,最后就实现了,通过按钮来控制动画的效果了。所以,我们通过update()方法来实现状态和坐标的联系,顺便设置一下“空气墙”,其实就是不让人物超过屏幕。

//人物移动
public void update(){
if(state==STATE.L && this.x>20){
this.x -= 1f;
}else if(state==STATE.R && this.x<700){
this.x += 1f;
}
}

(8)draw()方法

演员类的draw方法中,默认提供给我们一个SpriteBatch和parentAlpha (透明度的意思),
然后我们需要在这里调用判断状态和负责移动的方法,即check()和update()方法。特别注意的是,重写draw方法的时候SpriteBatch中已经给我们写好了 batch.begion 和batch.end方法,
所以我们只需要写batch.draw()就可以了。这样我们就可以将我们做好的动画,在绘画出来.

@Override
public void draw(SpriteBatch batch, float parentAlpha) {
statetime += Gdx.graphics.getDeltaTime();
this.update();
this.check();
batch.draw(currentFrame, x, y);
}

(9)修改MyGame类

演员类必须需要使用到舞台,这是大家都知道的,所以我们在主类MyGdxGame里面,
添加一个Image控件作为背景,然后将Marion和Mario中的button控件加入到stage中。

background = new Image(new Texture(Gdx.files.internal(“data/background.jpg”)));
stage = new Stage(800, 480, false);
Gdx.input.setInputProcessor(stage);
Person p = new Person(100,170);
stage.addActor(background);
stage.addActor(p);
stage.addActor(p.btn_L);
stage.addActor(p.btn_R);

stage.act();
stage.draw();
 

完整的例子代码如下:

完整的例子代码如下:

 

主类:

 

package com.qsuron;

import com.badlogic.gdx.ApplicationListener;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.graphics.GL10;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import com.badlogic.gdx.scenes.scene2d.Stage;
import com.badlogic.gdx.scenes.scene2d.ui.Image;

public class MyDemo implements ApplicationListener{
SpriteBatch batch;
Image background;
Stage stage;

@Override
public void create() {

batch = new SpriteBatch();
background = new Image(new Texture(Gdx.files.internal(“data/background.jpg”)));
stage = new Stage(800, 480, false);
Gdx.input.setInputProcessor(stage);
Person p = new Person(100,170);
stage.addActor(background);
stage.addActor(p);
stage.addActor(p.btn_L);
stage.addActor(p.btn_R);
}

@Override
public void dispose() {
batch.dispose();
}

@Override
public void render() {

Gdx.gl.glClearColor(0,0,0, 0);
Gdx.gl.glClear(GL10.GL_COLOR_BUFFER_BIT);
stage.act();
stage.draw();
}

@Override
public void resize(int width, int height) {
}

@Override
public void pause() {
}

@Override
public void resume() {
}

}

 

演员类:

package com.qsuron;

import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.g2d.Animation;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import com.badlogic.gdx.graphics.g2d.TextureRegion;
import com.badlogic.gdx.scenes.scene2d.Actor;
import com.badlogic.gdx.scenes.scene2d.InputEvent;
import com.badlogic.gdx.scenes.scene2d.InputListener;
import com.badlogic.gdx.scenes.scene2d.ui.ImageButton;
import com.badlogic.gdx.scenes.scene2d.utils.TextureRegionDrawable;

public class Person extends Actor {
public float x;
public float y;
public float statetime;

//按钮
ImageButton btn_L;
ImageButton btn_R;

//人物当前状态(枚举类)
STATE state;

//对应的动画
Animation animation_left;
Animation animation_right;
Animation animation_idle;

//实时动画
TextureRegion currentFrame;

public Person(float x, float y) {
super();
this.x = x;
this.y = y;
this.statetime = 0;
this.show();
state = STATE.I;
}

public void show() {

//分割动画
Texture animation_file = new Texture(Gdx.files.internal(“data/animation2-2.png”));
TextureRegion[][] animation_split_right = TextureRegion.split(animation_file,42,51);
TextureRegion[][] animation_split_left = TextureRegion.split(animation_file,42,51);
Texture animation_file2 = new Texture(Gdx.files.internal(“data/idle.png”));
TextureRegion[][] animation_split_idle = TextureRegion.split(animation_file2,42,51);

//实例化动画图片组
TextureRegion[] textureRegion_right = new TextureRegion[30];
TextureRegion[] textureRegion_left = new TextureRegion[30];
TextureRegion[] textureRegion_idle = new TextureRegion[1];

//向右动画图片组
int i = 0;
for(TextureRegion[] animation_split_right_split:animation_split_right){
for(TextureRegion animation_pic:animation_split_right_split){
textureRegion_right[i] = animation_pic;
i++;
}
}

//向左动画图片组
i = 0;
for(TextureRegion[] animation_split_left_split:animation_split_left){
for(TextureRegion animation_pic:animation_split_left_split){
textureRegion_left[i] = animation_pic;
textureRegion_left[i].flip(true,false);
i++;
}
}

//原地动画图片组
textureRegion_idle[0] = animation_split_idle[0][0];

//合成动画
animation_right = new Animation(0.08f,textureRegion_right);
animation_right.setPlayMode(Animation.LOOP);
animation_left = new Animation(0.08f,textureRegion_left);
animation_left.setPlayMode(Animation.LOOP);
animation_idle = new Animation(1f,textureRegion_idle);
animation_idle.setPlayMode(Animation.LOOP);

//按钮图片读取
Texture button_file = new Texture(Gdx.files.internal(“data/button.png”));
TextureRegion[][] button_split = TextureRegion.split(button_file,100,100);

//按钮实例化
btn_R = new ImageButton(
new TextureRegionDrawable(button_split[0][0]),
new TextureRegionDrawable(button_split[0][1]));
btn_L = new ImageButton(
new TextureRegionDrawable(button_split[0][2]),
new TextureRegionDrawable(button_split[0][3]));

//按钮位置
btn_R.setPosition(150,20);
btn_L.setPosition(20,20);

//按钮监听
btn_R.addListener(new InputListener(){
@Override
public boolean touchDown(InputEvent event, float x, float y,
int pointer, int button) {
state = STATE.R;
return true;
}
@Override
public void touchUp(InputEvent event, float x, float y,
int pointer, int button) {
state = STATE.I;
super.touchUp(event, x, y, pointer, button);
}
});

btn_L.addListener(new InputListener(){
@Override
public boolean touchDown(InputEvent event, float x, float y,
int pointer, int button) {
state = STATE.L;
return true;
}
@Override
public void touchUp(InputEvent event, float x, float y,
int pointer, int button) {
state = STATE.I;
super.touchUp(event, x, y, pointer, button);
}
});
}

//状态控制与动画选择
public void getFrame() {
if(this.state==STATE.L){
currentFrame = animation_left.getKeyFrame(statetime,true);
}else if(this.state==STATE.R){
currentFrame = animation_right.getKeyFrame(statetime,true);
}else if(this.state==STATE.I){
currentFrame = animation_idle.getKeyFrame(statetime,true);
}
}

//人物移动
public void update(){
if(state==STATE.L && this.x>20){
this.x -= 1f;
}else if(state==STATE.R && this.x<700){
this.x += 1f;
}
}

@Override
public void draw(SpriteBatch batch, float parentAlpha) {
statetime += Gdx.graphics.getDeltaTime();
this.update();
this.getFrame();
batch.draw(currentFrame, x, y);
}

enum STATE {
L,R,I;
};

}