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。
轮询键盘会带来新的问题。首先,一次按键可能带来两次响应的问题。尽管移动设备上的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。
相关文章推荐
- 数据库设计中的一些问题
- 电路系统设计制作过程和需要注意的一些问题
- 自己初学Java给自己设计的排序算法遇到的一些问题
- python中类的设计问题(一些高级问题探讨,函数重载,伪私有,工厂模式,类方法等)
- 电路系统设计制作过程和需要注意的一些问题
- 中国象棋程序的设计与实现(十一)--第2次回答CSDN读者的一些问题
- 【DDD】领域驱动设计实践 —— 一些问题及想法
- 建议平面设计爱好者要学的软件,和一些CDR常见问题
- 开发经验!嵌入式硬件设计需要考虑的一些问题总结
- 课程设计中遇到的一些问题
- 总结网页设计中的一些比较突出的细节问题
- 通达OA 工作计划模块中一些设计问题有待改进(图文)
- sql server 数据库设计表的一些小问题
- 关于iphone界面设计的一些尺寸问题
- 问题点0807------框架设计中的一些调整
- MOSS上发布的SharePoint Designer上设计的工作流的一些问题
- 教学设计中的一些疑难问题
- 中国象棋程序的设计与实现(五)--回答CSDN读者的一些问题
- 响应式设计的一些问题