您的位置:首页 > 其它

Game设计中的一些问题

2010-01-08 11:39 232 查看
一、 轮询键盘

轮询键盘会带来新的问题。首先,一次按键可能带来两次响应的问题。尽管移动设备上的FPS很低,但对于按键来说还是太快了。以下是常见的解决方法:

方法一:使用时间阈值技术。设定一个时间阈值。比如100ms,比较同一个按键两次按下的时间间隔。若小于这个时间阈值则不做处理,若大于这个时间阈值则执行相关的逻辑。

private long leftLastPressedTime=0;//记录上次左键按下的时间

private long limit=100;//定100ms为时间阈值

private void input(){

int keyState=getKeyStates();

if((keyState&LEFT_PRESSED)!=0)

long pastime=System.currentTimeMillis()-leftLastPressedTime;

if(pastTime>limit){

leftLastPressedTime+=pastime;

doSomething();

}

}


法二:设置标志变量。通过检查一个标志变量来确定按键的状态。当检测到按键按下后,若标记为false,则说明第一次按下,应处理逻辑。之后将标记变量改
为true,说明已经处理过了,下次就不再处理了;当按键抬起时,标记变量被重置,这样以可以进行下一次处理了。以下是运用标志变量思想的代码。

private boolean leftFlag=false;//标记变量

private void input(){//处理输入

int keyState=getKeyStates();

if((keyState&LEFT_PRESSED)!=0){

doSomething();

leftFlag=true;

}else{

leftFlag=false;

}

}


一个重要的问题是如何清除已经记录的按键缓冲区。通常为了界面的美观,游戏的主画面和菜单都在同一个Canvas上绘制,当从游戏主画面切换到菜单,或反
过来从菜单切换到游戏时,可能上一个画面的按键没有被处理便遗留到了新的画面。这时希望清除在上一个画面没有处理的残余按键。方法是两次调用
getKeyStates()。getKeyStates()有一个附加的作用是清空键盘缓冲区,所以两次调用getKeyStates()从理论上讲会
返回当前的按键状态。

二、 实现自己的键盘映射机制

设计一种键盘响应机制,既要基于轮询又要拥有传统回调的灵活性。此外最好能简捷的解决轮询中的常见问题。这个输入组件应该提供的功能有:

n 将键盘 keyCode映射到通用的输入action上;

n 能够模拟按键;

n 用户可自定义按键;

n 可清除按键状态;

n 一部分按键遵循只要按下就起作用的模式(支持重复按键);

n 一部分按键遵循按下后只起一次作用的模式。


个输入组件由两部分组成:InputAction类和InputManager类。InputAction类代表了按键的逻辑映射;
InputManager类负责将keyCode映射到哪一个InputAction上。先来看看InputAction具有的两种检测模式:
MODE_NORMAL和MODE_INTAL_ONLY。前者遵循只要按下就起作用的模式(支持重复按键);后者遵循按下后只起一次作用的模式。
InputAction的使用很简单,只要轮询调用InputAction实例的isPressed()方法就可以了,它会根据不同的模式,返回按键是否
按下。以下是InputAction类的代码

public

class
InputAction {

//两种检测模式

public
static
final
int
MODE_NORAML
=0;

public
static
final
int
MODE_INITAL_ONLY
=1;

//三种按键状态

private
static
final
int
STATE_PRESSED
=1;

private
static
final
int
STATE_RELEASED
=2;

private
static
final
int
STATE_HOLDON
=3;

//检测模式变量

private
int
mode;

//按键状态变量

private
int
state;

public
InputAction(){

this
(MODE_NORAML
);

}

public
InputAction(int
mode){

this
.mode=mode;

this
.state=STATE_RELEASED
;

reset();

}

public
synchronized
void
press(){//按下

if
(state!=STATE_HOLDON
)

state=STATE_PRESSED
;

}

public
synchronized
void
release(){//释放

state=STATE_RELEASED
;

}

public
synchronized
void
reset(){//重置

release();

}

public
synchronized
void
tap(){//模拟一次按键

press();

release();

}

public
synchronized
boolean
isPressed(){

int
lastState=state;

if
(state==STATE_PRESSED
&&mode==MODE_INITAL_ONLY
)

state=STATE_HOLDON
;

return
lastState==STATE_PRESSED
;

}

}

InputManager
类负责管理将keyCode映射到哪一个InputAction上。它内部维护了一个Map。以keyCode作为(key),以InputAction
对象实例作为值。通过调用方法mapkeyToInputAction(int keyCode,InputAction
action)来进行按键映射。这种机制允许将多个键盘映射到同一个InputStream上。例如,将方向“↑”和数字键“2”都映射到一个
InputAction上。以下是InputManager类的代码。

import java.util.Enumeration;

import java.util.Hashtable;

public class InputManager {

private Hashtable keyMap=new Hashtable();//保存键盘映射的MAP

public InputManager(){

}

public void mapkeyCodeToInputAction(int keyCode,InputAction action){

keyMap.put(new Integer(keyCode),action );//将一个整型keyCode映射到一个action

}

public void clearAll(){//清空所有的映射

keyMap.clear();

}

public void keyPressed(int keyCode){//回调方法,用于键盘按下

InputAction action=getInputAction(keyCode);

if(action!=null)

action.press();

}

public void keyReleased(int keyCode){//回调方法,用于键盘释放

InputAction action=getInputAction(keyCode);

if(action!=null)

action.release();

}

public void keyRepeated(int keyCode){//回调方法,用于键盘重复,保持空白

}

protected InputAction getInputAction(int keyCode){//取得map中与之对应的action

return (InputAction)keyMap.get(new Integer(keyCode));

}

public void resetAll(){//

for(Enumeration e=keyMap.elements();e.hasMoreElements();)

((InputAction)e.nextElement()).reset();

}

}


面是使用方法。首先实例化一个InputStream对象,并在职Canvas的三个回调函数中分别对InputManager的对应方法进行回调。准备
工作完成后,可以根据需求以不同的检测模式参数来构造InputAction对象。可选的模式有InputAction.MODE_NORMAL和
InputAction.MODE_INITAL_ONLY。调用inputManager.mapkeyCodeToAction()方法,将不同的
KeyCode映射到InputAction对象上。使用时只要在轮询的input()方法中轮询调用InputAction实例的isPressed方
法就可以了。

import
javax.microedition.lcdui.game.GameCanvas;

public
class
GameScreenInputEnable extends
GameCanvas implements
Runnable{

//...

InputManager inputManager=new
InputManager();

InputAction upAction=new
InputAction(InputAction.MODE_INITAL_ONLY
);

InputAction downAction=new
InputAction();

//...

public
GameScreenInputEnable(){

super
(false
);

}

public
void
run(){

//The main part of the Game

}

public
void
init(){

//...

inputManager.mapkeyCodeToInputAction(KEY_NUM2
, upAction); //映射数字键

inputManager.mapkeyCodeToInputAction(KEY_NUM8
, downAction);

inputManager.mapkeyCodeToInputAction(-1, upAction);//映射方向导航键

inputManager.mapkeyCodeToInputAction(-2, downAction);

}

public
void
input(){

if
(upAction.isPressed()){

//TODO

}

if
(downAction.isPressed()){

//TODO

}

}

protected
void
keyPressed(int
keyCode){//

inputManager.keyPressed(keyCode);

}

protected
void
keyReleased(int
keyCode){

inputManager.keyReleased(keyCode);

}

protected
void
keyRepeated(int
keyCode){

inputManager.keyRepeated(keyCode);

}

}

在这个例子中,方向导航键“↑”和数字键“2”被映射到了upAction,并只在按下后起一次作用。对应的方向导航键“↓”和数字键“8”被映射到了downAction,在按下后总是有效。

可以在运行时改变按键的检测模式和按键的映射关系,这为在运行时由用户自行配置按键提供了可能。使用这个输入组件的前提条件是:GameCanvas不能屏蔽键盘事件,即构造函数传入false,不能为true。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: