看下如何使用MVC中的控制层来控制演员的动作。
首先需要修改下演员类,演员类中我们没有定义演员的速度等相关信息。
也没定义如何改变它的位置等等信息。
修改如下:
package com.me.mygdxgame.actor;
import com.badlogic.gdx.math.Rectangle;
import com.badlogic.gdx.math.Vector2;
public class GirlActor {
//定义一个演员状态的枚举方法
//这里我们只定义两种状态,站立状态,跑动状态
public enum State {
IDLE, RUNING
}
//演员的位置
private Vector2 position = new Vector2();
//这个演员的大小,因为纹理是方形或长方形的
private Rectangle bounds = new Rectangle();
//默认状态是跑动的
private State state = State.RUNING;
private float stateTime = 0;
//演员的朝向
private boolean facingRight = true;
//演员的x,y移动速度
private float speed_x = 2f;
private float speed_y = 0;
//演员在二维空间中的x,y速度向量值
//这里是为了便于进行向量的加减乘除运算,所以定义个向量速度
public Vector2 velocity = new Vector2();
/**
* 演员的构造器
* 我们初始化的时候需要指定演员的位置和大小
*
* @param position 位置
* @param size 演员大小
*/
public GirlActor(Vector2 position, Vector2 size) {
this.position = position;
//演员的大小
this.bounds.width = size.x;
this.bounds.height = size.y;
}
/**
* 改变当前演员状态的方法
*
* @param deltaTime 帧间隔时间
*/
public void update(float deltaTime) {
stateTime += deltaTime;
//根据时间和速度来运算当前的演员位置信息。
position.add(velocity.scl(deltaTime));
}
//get set 方法,是不是很像我们的JavaBean
public float getSpeed_x() {
return speed_x;
}
public void setSpeed_x(float speed_x) {
this.speed_x = speed_x;
}
public float getSpeed_y() {
return speed_y;
}
public void setSpeed_y(float speed_y) {
this.speed_y = speed_y;
}
public Vector2 getVelocity() {
return velocity;
}
public void setVelocity(Vector2 velocity) {
this.velocity = velocity;
}
public Vector2 getPosition() {
return position;
}
public void setPosition(Vector2 position) {
this.position = position;
}
public Rectangle getBounds() {
return bounds;
}
public void setBounds(Rectangle bounds) {
this.bounds = bounds;
}
public State getState() {
return state;
}
public void setState(State state) {
this.state = state;
}
public float getStateTime() {
return stateTime;
}
public void setStateTime(float stateTime) {
this.stateTime = stateTime;
}
public boolean isFacingRight() {
return facingRight;
}
public void setFacingRight(boolean facingRight) {
this.facingRight = facingRight;
}
}
重点说下update方法。
position.add(velocity.scl(deltaTime));
这是实时改变演员位置的代码。
这里涉及到二维向量的加运算,因为这里主要是x轴的运算,y轴为0,所以实际上就x坐标的相加。具体可参考上篇推荐的向量在游戏中的应用那篇博文。
velocity.scl(deltaTime)。我们看下Vector2 scl方法的源码
public Vector2 scl (float scalar) {
x *= scalar;
y *= scalar;
return this;
}
很简单的运算,当前的x,y的值乘以帧时间间隔。
假设x为2f,那么在0.016秒内(参考前几篇内容),它的值就是2f*0.016=0.032f,我们再乘以实际每单位屏幕像素数,就可以推算出它每刷新一次屏幕移动的距离。
0.032*80=2.56 也就是说,每刷新一次屏幕坐标x的值增加2.56像素。记得前几篇介绍帧率的概念吧。上次计算出每秒大概刷新62.5次,2.56*62.5=160,这是每秒移动的距离。
我们前面假设的是2f的速度。2f*80=160。两个值相等。
所以这段代码的意思就是把速度值跟屏幕刷新次数结合起来,实现平滑增加x,y的值。
这里Y值为0所以实际上是增加x的值。如果实现跳,飞等空中效果就需要改变Y的值了。
有兴趣的可以先试试。
接下来在control包下新建GirlControl类。
package com.me.mygdxgame.control;
import com.me.mygdxgame.actor.GirlActor;
import com.me.mygdxgame.actor.World;
/**
* control层,演员的控制
*/
public class GirlControl {
private World<GirlActor> world;
private GirlActor girl;
/**
* 枚举类型的按键状态
*/
public enum KEY {
LEFTDOWN, LEFTUP, RIGHTDOWN, RIGHTUP, NON
}
/**
* 构造器
*
* @param world 游戏世界
*/
public GirlControl(World<GirlActor> world) {
this.world = world;
girl = world.getActors("girl");
}
/**
* 默认状态为什么都没做
*/
private KEY keyState = KEY.NON;
public KEY getKeyState() {
return keyState;
}
public void setKeyState(KEY keyState) {
this.keyState = keyState;
}
/**
* 不断执行的方法
* 在MyGame的render方法中调用来不断判断和更新演员状态
*/
public void update() {
state();
//边界检查,是否超出屏幕右边。世界的宽度-人物纹理的宽度
//这里就体现出我们定义世界网格的好处了。我根本不需要知道屏幕纹理的具体大小。
if (girl.getPosition().x > world.getWorldWidth() - girl.getBounds().getWidth()) {
girl.getPosition().x = world.getWorldWidth() - girl.getBounds().getWidth();
}
//左边界判断,这个更简单些可以直接判断x是否小于0,也就是是否移动到原点的左边
if (girl.getPosition().x < girl.getBounds().x) {
girl.getPosition().x = 0;
}
}
/**
* 判断当前按下的按键状态
*/
private void state() {
switch (getKeyState()) {
case LEFTDOWN:
girl.setState(GirlActor.State.RUNING);
girl.setFacingRight(false);
girl.getVelocity().x = -girl.getSpeed_x();
break;
case LEFTUP:
break;
case RIGHTDOWN:
girl.setState(GirlActor.State.RUNING);
girl.setFacingRight(true);
girl.getVelocity().x = girl.getSpeed_x();
break;
case RIGHTUP:
break;
case NON:
girl.setState(GirlActor.State.IDLE);
girl.getVelocity().x = 0;
default:
break;
}
}
}
state()方法不停判断当前的输入状态,实时修改演员的,行动状态,朝向和位置信息。
向左移动使用负值不停减少x的值,当没有任何按键的时候,设置人物为站立状态,移动速度为0.
别的代码注释写的很清楚了,这里就不解释了。一定要有这么一个概念。游戏屏幕是个无限循环,我们改变状态也好,获取状态也好,要想实时获取,那么这个方法必须在这个循环中。
修改我们的MyGame方法
package com.me.mygdxgame;
import com.badlogic.gdx.ApplicationListener;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.InputProcessor;
import com.badlogic.gdx.graphics.GL20;
import com.badlogic.gdx.math.Vector2;
import com.me.mygdxgame.actor.GirlActor;
import com.me.mygdxgame.actor.World;
import com.me.mygdxgame.control.GirlControl;
import com.me.mygdxgame.view.GirlView;
public class MyGame implements ApplicationListener, InputProcessor {
private World<GirlActor> world;
private GirlView girlView;
private GirlControl control;
@Override
public void create() {
world = new World<GirlActor>();
GirlActor girlsActor = new GirlActor(new Vector2(0, 0), new Vector2(2, 2));
world.addActors("girl", girlsActor);
girlView = new GirlView(world);
control = new GirlControl(world);
//游戏输入注册当前这个界面
Gdx.input.setInputProcessor(this);
}
@Override
public void render() {
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
Gdx.gl.glClearColor(0.57f, 0.41f, 0.55f, 1.0f);
girlView.render();
control.update();
}
@Override
public void dispose() {
girlView.dispose();
}
@Override
public void resize(int width, int height) {
world.setPixelsXY(width, height);
}
@Override
public void pause() {
}
@Override
public void resume() {
}
@Override
public boolean keyDown(int keycode) {
return false;
}
@Override
public boolean keyUp(int keycode) {
return false;
}
@Override
public boolean keyTyped(char character) {
return false;
}
@Override
public boolean touchDown(int screenX, int screenY, int pointer, int button) {
//点击屏幕左半边就左移,右半边就右移
if (screenX < world.getWidth() / 2) {
control.setKeyState(GirlControl.KEY.LEFTDOWN);
} else {
control.setKeyState(GirlControl.KEY.RIGHTDOWN);
}
return false;
}
@Override
public boolean touchUp(int screenX, int screenY, int pointer, int button) {
control.setKeyState(GirlControl.KEY.NON);
return false;
}
@Override
public boolean touchDragged(int screenX, int screenY, int pointer) {
return false;
}
@Override
public boolean mouseMoved(int screenX, int screenY) {
return false;
}
@Override
public boolean scrolled(int amount) {
return false;
}
}
我们实现了InputProcessor,这个是监听输入事件的。
我们看下说明
/** An InputProcessor is used to receive input events from the keyboard and the touch screen (mouse on the desktop). For this it
* has to be registered with the {@link Input#setInputProcessor(InputProcessor)} method. It will be called each frame before the
* call to {@link ApplicationListener#render()}. Each method returns a boolean in case you want to use this with the
* {@link InputMultiplexer} to chain input processors.
*
* @author mzechner */
其中
has to be registered with the {@link Input#setInputProcessor(InputProcessor)} method
我们必须使用setInputProcessor注册下。
Gdx.input.setInputProcessor(this);
它的方法中包含一些keydown等的,这是实体按键和pc按键。暂时不用理会。我们看下它的实现类。
其实就9个,其中有个手势类,里边实现了一些复杂的手势操作的方法,例如,缩放,滑动等,这么我不使用手势类,暂时还用不上那么复杂的操作。
touchDown方法前两个就是当前点击的位置信息。pointer点击了几个点,比如说3个手指同时点击屏幕,它的值一般为0,1,2这三个值。button是跟键盘有关的。忽略它。
我们根据点击的位置信息来判断让演员往那边走。
运行下看看效果吧。
演员的控制其实就是这么简单。
下篇来实现演员的跳动飞行等动作,有兴趣的可以自己先试试怎么实现。
源码因为今天更新maven的项目依赖库卡住了。所以今天没有编译新的源文件。并且今天改动也不大。源码就不提供了。