本文链接: http://blog.csdn.net/xietansheng/article/details/50187151
1. 概述
输入(触屏/鼠标点击,键盘按键)的监听是用户与游戏交互的基础,游戏中通过监听用户的输入来改变游戏状态(主角跳跃,发射子弹,选择关卡等)。LibGDX 处理输入捕获的类为 com.badlogic.gdx.Input,其实例为 Gdx.input。Input 中接收一个 InputProcessor 接口实例,用于处理捕获到的输入事件。
2. InputProcessor
InputProcessor 是输入处理器接口,一个 InputProcess 实例被用来接收输入(触屏/鼠标点击,键盘按键)。通过 Gdx.input.setInputProcessor(InputProcessor) 方法将一个输入处理器注册到 Input 中,Input 便将所有的输入交给当前注册的 InputProcess 实例进行处理。而舞台类(Stage)正好是实现了 InputProcess 接口,因此游戏的输入事件监听通常把一个 Stage 对象注册到 Input 中进行接收输入并根据输入分发事件。
3. EventListener
EventListener 是事件监听器接口,一般输入处理器(InputProcessor)接收到后,将输入转换成不同的事件(Event),然后回调相应事件监听器的方法通知监听者。
EventListener 接口是所有事件的统一监听(只有一个回调方法),不够细致,在代码中一般使用它的实现类,常用实现类有:
(1)InputListener: 输入监听器,将输入事件分的更加细致,包括多个回调方法,例如:
- keyDown(): 键盘按键按下时被调用(键盘按键事件分为“按下”和“弹起”两个动作)
- keyUp(): 键盘按键弹起时被调用
- touchDown(): 触摸屏幕,手指/鼠标按下时被调用(触摸事件可分为手指/鼠标“按下”,“拖动”,“抬起”三个动作)
- touchDragged(): 触摸屏幕,手指/鼠标按下后在屏幕上拖动时被调用
- touchUp(): 触摸屏幕,手指/鼠标按下后抬起时被调用
(2)ClickListener: 点击监听器,通常用于舞台/演员被点击的事件监听。“点击”的定义:按下 并 抬起后 视为“点击”。通常使用 其中的 clicked() 方法监听回调:
- clicked(): 当注册监听的对象(舞台/演员)被点击时调用
4. 代码示例
引用前面章节自定义的演员:
package com.libgdx.test;
import com.badlogic.gdx.graphics.g2d.Batch;
import com.badlogic.gdx.graphics.g2d.TextureRegion;
import com.badlogic.gdx.scenes.scene2d.Actor;
/**
* 自定义演员
*/
public class MyActor extends Actor {
private TextureRegion region;
public MyActor(TextureRegion region) {
super();
this.region = region;
setSize(this.region.getRegionWidth(), this.region.getRegionHeight());
}
public TextureRegion getRegion() {
return region;
}
public void setRegion(TextureRegion region) {
this.region = region;
setSize(this.region.getRegionWidth(), this.region.getRegionHeight());
}
@Override
public void act(float delta)
super.act(delta);
}
@Override
public void draw(Batch batch, float parentAlpha) {
super.draw(batch, parentAlpha);
if (region == null || !isVisible()) {
return;
}
batch.draw(
region,
getX(), getY(),
getOriginX(), getOriginY(),
getWidth(), getHeight(),
getScaleX(), getScaleY(),
getRotation()
);
}
}
游戏程序的启动入口类:
package com.libgdx.test;
import com.badlogic.gdx.Application;
import com.badlogic.gdx.ApplicationAdapter;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.Input;
import com.badlogic.gdx.graphics.GL20;
import com.badlogic.gdx.graphics.Texture;
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.Stage;
import com.badlogic.gdx.scenes.scene2d.utils.ClickListener;
/**
* 游戏主程序的启动入口类
*/
public class MainGame extends ApplicationAdapter {
private static final String TAG = MainGame.class.getSimpleName();
// 纹理
private Texture texture;
// 演员
private MyActor actor;
// 舞台
private Stage stage;
@Override
public void create() {
// 设置 Log 输出级别
Gdx.app.setLogLevel(Application.LOG_DEBUG);
// 创建纹理 和 演员
texture = new Texture(Gdx.files.internal("badlogic.jpg"));
actor = new MyActor(new TextureRegion(texture));
// 设置演员的位置
actor.setPosition(20, 40);
// 创建舞台, 并添加演员
stage = new Stage();
stage.addActor(actor);
/* 事件初始化 */
// 首先必须注册输入处理器(stage), 将输入的处理设置给 舞台(Stage 实现了 InputProcessor 接口)
// 这样舞台才能接收到输入事件, 分发给相应的演员 或 自己处理。
Gdx.input.setInputProcessor(stage);
// 给舞台添加输入监听器(包括触屏, 鼠标点击, 键盘按键 的输入)
stage.addListener(new MyInputListener());
// 给演员添加一个 点击 监听器(只包括 手指点击 或 鼠标点击)
actor.addListener(new MyClickListener());
// 只有需要监听输入的舞台/演员才需要添加监听器
// 如果要移除指定监听器, 可以调用相应的 removeListener(listener) 方法
}
@Override
public void render() {
// 黑色清屏
Gdx.gl.glClearColor(0, 0, 0, 1);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
// 更新舞台逻辑
stage.act();
// 绘制舞台
stage.draw();
}
@Override
public void dispose() {
// 释放资源
if (texture != null) {
texture.dispose();
}
if (stage != null) {
stage.dispose();
}
}
/**
* 输入事件监听器(包括触屏, 鼠标点击, 键盘按键 的输入)
*/
private class MyInputListener extends InputListener {
/**
* 当有键盘按键被按下时调用, 参数 keycode 是被按下的按键的键值,
* 所有键盘按键的键值常量定义在 com.badlogic.gdx.Input.Keys 类中
*/
@Override
public boolean keyDown(InputEvent event, int keycode) {
switch (keycode) {
case Input.Keys.UP: {
Gdx.app.log(TAG, "被按下的按键: 方向上键");
break;
}
case Input.Keys.DOWN: {
Gdx.app.log(TAG, "被按下的按键: 方向下键 ");
break;
}
case Input.Keys.A: {
Gdx.app.log(TAG, "被按下的按键: A键");
break;
}
case Input.Keys.ENTER: {
Gdx.app.log(TAG, "被按下的按键: 回车键");
break;
}
default: {
Gdx.app.log(TAG, "其他按键, KeyCode: " + keycode);
break;
}
}
return false;
}
/**
* 手指/鼠标 按下时调用
*
* @param x
* 按下时的 X 轴坐标, 相对于被触摸对象(监听器注册者)的左下角
*
* @param y
* 按下时的 Y 轴坐标, 相对于被触摸对象(监听器注册者)的左下角
*
* @param pointer
* 按下手指的ID, 用于多点触控时辨别按下的是第几个手指,
* 一般情况下第一只手指按下时 pointer 为 0, 手指未抬起前又有一只手指按下, 则后按下的手指 pointer 为 1。
* 同一只手指的 按下(touchDown), 拖动(touchDragged), 抬起(touchUp)属于同一次序列动作(pointer 值相同),
* pointer 的值在 按下 时被确定, 之后这只手指产生的的 拖动 和 抬起 动作将会把该已确定的 pointer 值传递给其事件方法
* touchDragged() 和 touchUp() 方法。
*
* @return
* 返回值为 boolean 类型, 用于告诉上一级当前对象(演员/舞台)是否需要处理该次事件。 <br/><br/>
*
* 返回 true: 表示当前对象需要处理该次事件, 则之后这只手指产生的 拖动(touchDragged)和 抬起(touchUp)事件
* 也会传递到当前对象。<br/><br/>
*
* 返回 false: 表示当前对象不处理该次事件, 既然不处理, 则之后这只手指产生的 拖动(touchDragged)和 抬起(touchUp)事件
* 也将不会再传到到当前对象。<br/><br/>
*
* PS: 当前对象是否处理一只手指的触摸事件(按下, 拖动, 抬起)只在 按下时(touchDown)确定,
* 所以之后的 touchDragged() 和 touchUp() 方法中就不再判断, 因此返回类型为 void。
*/
@Override
public boolean touchDown(InputEvent event, float x, float y, int pointer, int button) {
Gdx.app.log(TAG, "touchDown: " + x + ", " + y + "; pointer: " + pointer);
return true;
}
/**
* 手指/鼠标 按下后拖动时调用
*/
@Override
public void touchDragged(InputEvent event, float x, float y, int pointer) {
Gdx.app.log(TAG, "touchDragged: " + x + ", " + y + "; pointer: " + pointer);
}
/**
* 手指/鼠标 抬起时调用
*/
@Override
public void touchUp(InputEvent event, float x, float y, int pointer, int button) {
Gdx.app.log(TAG, "touchUp: " + x + ", " + y + "; pointer: " + pointer);
}
}
/**
* 点击 监听器(只包括 手指点击 或 鼠标点击)
*/
private class MyClickListener extends ClickListener {
/**
* 对象(演员/舞台)被点击时调用
*
* @param x
* 点击时(手指抬起时)的 X 轴坐标, 相对于被点击对象(监听器注册者)的左下角
*
* @param y
* 点击时(手指抬起时)的 Y 轴坐标, 相对于被点击对象(监听器注册者)的左下角
*/
@Override
public void clicked(InputEvent event, float x, float y) {
// 获取响应这个点击事件的演员
Actor actor = event.getListenerActor();
Gdx.app.log(TAG, "被点击: " + x + ", " + y + "; Actor: " + actor.getClass().getSimpleName());
}
}
}
运行程序:
按下相应按键和点击屏幕,看控制台 log 的输出
5. 直接判断事件的输入
有些时候某些游戏界面只需要监听某个按键是否按下 或者 是否点击了屏幕即可,这样可以不需要动用较为复杂的整个事件输入处理监听分发系统。Gdx.input 中的提供了简易的方法判读指定的按键是否按下和当前屏幕是否被触摸:
- Gdx.input.isTouched(): 屏幕当前是否被触摸
- Gdx.input.justTouched(): 屏幕刚刚是否被触摸
- Gdx.input.isKeyPressed(int keyCode): 指定按键当前是否处于按下状态
- Gdx.input.isKeyJustPressed(int keyCode): 指定按键刚刚是否处于按下状态
触屏/按键按下的判断分别有上面两种方法,他们之间的区别和使用参考如下代码:
package com.libgdx.test;
import com.badlogic.gdx.Application;
import com.badlogic.gdx.ApplicationAdapter;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.Input;
import com.badlogic.gdx.graphics.GL20;
/**
* 游戏程序的启动入口类
*/
public class MainGame extends ApplicationAdapter {
private static final String TAG = MainGame.class.getSimpleName();
@Override
public void create() {
// 设置 Log 输出级别
Gdx.app.setLogLevel(Application.LOG_DEBUG);
}
@Override
public void render() {
// 黑色清屏
Gdx.gl.glClearColor(0, 0, 0, 1);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
// 判读当前是否有有触摸屏幕, 手指/鼠标按下后, 直到抬起前, Gdx.input.isTouched() 将始终返回 true
if (Gdx.input.isTouched()) {
// Gdx.input.getX(): 表示当前触屏按下时的幕 X轴 原始屏坐标, 相对于屏幕左下角
// Gdx.input.getY(): 表示当前触屏按下时的幕 Y原 始屏轴坐标, 相对于屏幕左下角
Gdx.app.log(TAG, "isTouched: " + Gdx.input.getX() + Gdx.input.getY());
}
// 判读当前指定的按键是否被按下, 指定按键被按下后, 直到弹起前, Gdx.input.isKeyPressed(KeyCode) 将始终返回 true
if (Gdx.input.isKeyPressed(Input.Keys.B)) {
Gdx.app.log(TAG, "isKeyPressed: B键被按下");
}
/*
* 通过上面的方法判读输入事件, 通常会导致监听后需要执行的代码在连续多帧中被持续执行,
* 更多时候当有按键按下一次或触摸一次屏幕只需要执行一次, 使用 Gdx.input 中的带有 just 的方法可以满足这个需求,
* 如下所示:
*/
/*
* 判断刚刚是否有一个新的触屏按下事件,
* 如果有, Gdx.input.justTouched() 则会在当前渲染帧范围内返回 true。
* 如果手指/鼠标一直按在屏幕上没有抬起过, 下一帧开始即使手指/鼠标依然是按着屏幕的,
* 但由于本次触摸事件已在上一帧中判断过, 已不能算是新事件, Gdx.input.justTouched() 将返回 false。
*/
if (Gdx.input.justTouched()) {
Gdx.app.log(TAG, "justTouched: " + Gdx.input.getX() + Gdx.input.getY());
if (Gdx.input.getX() < Gdx.graphics.getWidth() / 2) {
Gdx.app.log(TAG, "左半屏被按下, 主角跳跃一次~~");
} else {
Gdx.app.log(TAG, "右半屏被按下, 发射一颗子弹**");
}
}
/*
* 判断刚刚是否有指定的键盘按键按下, isKeyJustPressed() 方法的返回值规则和 justTouched() 方法一样,
*/
if (Gdx.input.isKeyJustPressed(Input.Keys.B)) {
Gdx.app.log(TAG, "isKeyJustPressed: B键被按下");
}
}
@Override
public void dispose() {
}
}