原文地址:  实现J2ME游戏的按键(键盘)缓冲


1. 什么是键盘缓冲?

J2ME当中键盘响应是用Canvas下的keyPressed()和keyReleased()方法实现的,所以就有可能出现这种情况: 把所有的键盘响应放在keyPressed()中实现

这样出现三种问题:
  1. 首先就是无法响应持续按键信息,如RPG游戏有的时候需要选择数量,让玩家选择99,莫非让玩家点99次键盘吗?如果使用MIDP2.0,可以使用getKeyStates()函数,但是又出问题了,在选择菜单的时候,菜单会来回蹦,造成选不中所要选择的菜单,必然还要使用keyPressed()方法,况且2.0也有其他的种种问题....
  2. 所有键盘响应放在keyPressed()中,当逻辑逐渐变大时,函数执行时间必然会变大,当又有一个keyPressed()事件发生时,后果无法预料....(因为模拟器,真机,响应方式都不相同,还存在支持不支持多按键操作的问题,所以我也没测...)
  3. 这样,必然会造成相关的逻辑分离,从代码的美观和整体的逻辑上都大打折扣,阅读代码必然会造成困难
有这么多问题,当然就要解决,解决办法就是缓冲,就是找个地方把它存起来,用的时候再取

2. 什么时候发生键盘响应?

我认为,理论上说是即时响应的,在我测试看来,不存在 Thread.sleep(100) 后系统才调用的事情,所以只要有时间片,虚拟机就会自动调用keyPressed方法

所驳文章地址: http://www.j2megame.cn/bbs/viewthread.php?tid=4246&page=1
相关我的回复: http://www.j2megame.cn/bbs/viewthread.php?tid=4246&page=1#pid37075

见最下方

3. 实现方法

今天用了一天的时间研究了键盘缓冲,看起来就区区几行代码,这一天下来,可是收获不少

下面先来看实现,现在发现两种比较好的实现

a实现
代码:
public void keyPressed(int keyCode){            
        int value = getKeyValue(keyCode) ; //自己定义的转换键值函数
        keyPressedBuffer |= value ;
}
public void keyReleased(int keyCode){            
        int value = getKeyValue(keyCode) ;
        keyReleasedBuffer |= value ;
}
private static void acquireKey(){ //主循环中调用
        keyPressed = keyPressedBuffer;
        keyReleased = keyReleasedBuffer;

        keyCurrent |= keyPressed;
        keyCurrent &= ~keyReleased;

        keyPressedBuffer = 0;
        keyReleasedBuffer = 0;
}
public static boolean keyHold(int gameKey){ //按住
        return ((keyCurrent & gameKey)!=0) ;
}
public static boolean keyDown(int gameKey){ //按下
        return ((keyPressed & gameKey)!=0) ;
}
public static boolean keyUp(int gameKey){ //抬起
        return ((keyReleased & gameKey)!=0) ;
}

b实现
代码:
public void keyPressed(int keyCode){            
        int value = getKeyValue(keyCode) ; //自己定义的转换键值函数
        key |= value ;
        pressedKey |= value ;
}
public void keyReleased(int keyCode){            
        int value = getKeyValue(keyCode) ;
        key ^= value ;
        releasedKey |= value ;
}
private static void acquireKey(){ //主循环中调用
        frameKey = key ;
        framePressedKey = pressedKey ;
        frameReleasedKey = releasedKey ;
        pressedKey = 0 ;
        releasedKey = 0 ;
}
public static boolean keyHold(int gameKey){ //按住
        return ((frameKey & gameKey)!=0) ;
}
public static boolean keyDown(int gameKey){ //按下
        return ((framePressedKey & gameKey)!=0) ;
}
public static boolean keyUp(int gameKey){ //抬起
        return ((frameReleasedKey & gameKey)!=0) ;
}
使用的时候直接调用keyHold(),keyDown(),keyUp()就可以了

两种实现都不错,但是我更倾向于第一种,从代码清晰度和健壮程度来说,都很好

比如有的手机键盘不支持多键输入,当按下两个键以后,只会响应一个keyPressed(),但传说有的手机会响应两个keyReleased().......也就是没有按下,直接弹起按键了......这样第二种方法就会出现问题了,就是因为第二种方法使用的时异或操作符

测试:
1000 & ~1000 = 0000     1000 ^ 1000 = 0000 //按下1000,弹起1000
1000 & ~1001 = 0000     1000 ^ 1001 = 0001  出问题...
同时弹起两个按键?..... 呵呵..不管怎么样,可能会出问题

关于getKeyValue(keyCode)说明:
就是一个自己的键值转换的函数,比如定义  YOUR_LEFT = 1;   如果keyCode是左方向键返回   YOUR_LEFT就可以了,也是各种品牌手机真实键值不同惹得祸...

今天研究一天的成果,如果叙述有误,请提出,共同探讨

虽然这个是对J2ME键盘缓冲的研究,但是我感觉在其他平台上的游戏同样存在着类似的问题,思路是一样的







-----------------------
关于《sleep后响应keyPressed()》我的回复

原文: http://www.j2megame.cn/bbs/viewthread.php?tid=4246&page=1#pid37075

在做项目的时候发现,我从来没有sleep过,但是仍然响应keyPressed()的
使用的是nokia的S60模拟器,使用手机顽童模拟器照样响应,使用fullcanvas

作者也说了是使用nokia的~什么问题...?

莫非WTK是这样的吗?

而且如果有sleep的话,也不是在sleep的时候触发的,看代码

代码:
while(true){
    System.out.println("\nBegin");

    updateGame();

    repaint(0, 0, 172, 208);

    serviceRepaints(); //调用System.out.println("paint");

    System.out.println("End\n");

    Thread.sleep(80);
}

Begin 和 End 之间也是可以响应的

我的输出 aaa************    为  keyPressed()  输出

1. 正常按键

Begin
paint
End

aaa*****************

Begin
paint
End


Begin
paint
End

aaa*****************

Begin
paint
End

2. 快速按键

aaa*****************

Begin
aaa*****************
paint
aaa*****************
End


Begin
aaa*****************
paint
End

aaa*****************