armytg 2010-05-14
声明:在网上看到的,网址忘了,但不是本人的原创
canvas中的keyEvent具有局限性,它对大小写字符不敏感,对连续按键的无法处理,使得它无法处理字符的输入,所以对于需要字符输入功能要用 textfield或是textbox才行。因此如果想在canvas中自己编码来实现文本输入的功能是不现实的,还是使用高级界面类textbox等来 进行文本输入的实现比较方便。
ITU-Tstandard telephone keypad 定义了12个通用的手机按钮,为了让写出来的游戏尽可能的容易移植和通用,游戏设计时要尽量的用这些键:
KEY_NUM0,KEY_NUM1, KEY_NUM2, KEY_NUM3, KEY_NUM4, KEY_NUM5, KEY_NUM6, KEY_NUM7, KEY_NUM8,KEY_NUM9, KEY_STAR, and KEY_POUND
MIDPdefines the following game actions: UP, DOWN, LEFT, RIGHT, FIRE, GAME_A,GAME_B, GAME_C, and GAME_D.
因为上面提到的这些按键是标准,每个手机厂家都需要将自己手机的功能键与它们进行映射,这个工作是有函数getGameAction()来完成的,这个函 数由不同的手机厂商根据自己手机的不同情况写好,放于手机中。提供给系统调用。不同的手机的功能键并不一样,因此每个厂家给出的 getGameAction()是不一样的,在编写相关程序的时候要小心处理可能会出现的情况。
手机分为有方向键和没有方向键的手机,对于有方向键的手机,要注意下面的情况:
有些手机将数字键也进行了映射,比如:
getGameAction(Canvas.KEY_NUM8); //当按下8键时,它返回的是Canvas.DOWNgetGameAction(-2); //当按下功能键” 下”,它返回的也是Canvas.DOWN
对于这种情况,那么反过来: 用getKeycode(Canvas.DOWN),系统会在-2和KEY_NUM8中选择一个返回,返回结果就具有不确定性了,这里容易出现问题。 因此为了程序的通用性,getKeycode这个函数应该尽量避免使用。
从编程的角度,一个游戏需要对方向键和2,4,6,8键作为方向键这两种情况进行相同的处理,可以按下面的做法:
将上下左右映射到2,4,6,8;然后程序中只对2,4,6,8键进行处理,
Public keyPressed(int keyCode){
Intkey=mergeKey(getGameAction(keyCode),keyCode);
switch(key){
caseKEY_NUM2:
…………….
CaseKEY_NUM4:
………………..
CaseKEY_NUM6:
……………….
CaseKEY_NUM8:
………………
}特别注意mergeKey函数的用处是将两种按键映射到2,4,6,8键。比如:
MergeKey(UP, -1) à KEY_NUM2; MergeKey(0, KEY_NUM2) à KEY_NUM2;
mergeKey中需要注意一种特殊情况,就是当用户按下:KEY_NUM2,而getGameAction(KEY_NUM2)得到的是UP,那么就出现了 mergeKey(UP, KEY_NUM2)的现象。函数中要处理这种特殊的情况
上面keyPress函数的写法只是为了方便说明,它不好的地方在于将按钮从处理工作switch写在了keyPressed的函数中,因为 keyPressed()的处理时间应该尽可能短,不然用户连续的按键的时候就会出现按键丢失的现象,因此可以用一个全局的变量gkey来暂时存放经过 merge的key,然后由canvas的画面线程由run函数中的while中处理。这个全局变量gkey需要注意一个“用完即0”的概念,避免出现按 键丢失的问题。这个思路同时也防止了相关操作多次调用.比如:
Process(gkey); Gkey = 0;
这种写法当process运行的时间太长的话,那么会出现短时间内用户再按键,他的按键会因为=0 那一句而发生丢失。所以要用下面方法写会好一点:
Gkey_temp=gkey;
Gkey=0;
Process(gkey_temp);关于连续按键的实现是通过在keyPressed函数中用一个变量keyPressing 来保存gkey并记录下按下的时间, 在keyReleased函数中将keyPressing清0。在按键处理的地方对按键按下的时间进行判断,如果时间大于500毫秒的话那么就用 switch(keyPressing)的做法, 因为keyPressing在用户松开按键后才会清0,而run又是一直调用按键处理,所以会形成多次调用,从而形成连续按键的效果.
连续按键的速度调整问题,上面的500毫秒只是决定了其开始的快慢而已. 而一秒调用多少次按键处理是由run的一秒循环多少次决定的. ZhanGuoMap设置为50毫秒,就是一秒run20次.以一次移动一个方格计算,按住后等500毫秒启动,一秒会移动20个格。
这里要注意可能出现的一个问题:
· 1.keyPressed()比如按下KEY_NUM0,打开一个form,这是keyPressing被设置为KEY_NUM0
· 2.对KEY_NUM0处理会调用display.setcurrent()函数将当前的屏幕设置为form
· 3.在屏幕切换后并不会调用keyReleased函数,造成了keyPressing变量并不会清0。
· 4.在form中如果对keyPressing进行处理会出现问题,因为它并没有被清0,而是还保证着之前的那个KEY_NUM0。
· 5.按照我们的理解,按下KEY_NUM0后按键有放开啊,为什么系统不调用keyReleased函数?
原因:(wtk2.2/doc中关于canvas的介绍)
Thekey-related, pointer-related, and paint() methods will only be called while theCanvas is actually visible on the output device. These methods will thereforeonly be called on this Canvas object only after a call to showNotify() andbefore a call to hideNotify().
上面的第2步已经将当前的屏幕的显示设置为form,canvas不再是当前显示类,它的keyReleased函数自然不会被调用,造成keypressing不会被清0。
解决方法:
在display.setcurrent()调用之前先了sleep一段时间,让系统有时间先调用keyReleased函数,然后再换屏幕显示。在模拟器上测试过sleep100毫秒可以解决问题,50毫秒就不够,但是到手机上100毫秒是否足够?没有试过。
【结论】不要认为keyPressing变量在按键松开一定会被清0,在屏幕切换的特殊情况下它是有可能不会被清0的!!!
关于keyPressed函数的系统调用
keyPressed()是在线程sleep的时候引发的,也就是说当Canvas这个线程在空闲状态时,KVM才有机会向激活的Display传递消息 说有人按了某键,keyPressed()是在Canvas线程sleep的时候被引发的。这也是按键不响应或延时的根本原因:Canvas线程过于繁忙,没有sleep 或很少sleep。解决办法:
· 一:优化程序,减轻Canvas线程负担,使sleep时间增加。
· 二:在优化后情况仍然存在,可以在需要按键响应的地方强行使线程sleep(20),从而引发keyPressed()。如果项目中需要频繁创建图片,致 使线程过度繁忙,即便在S700这样的机型上也出现了按键响应不及时的问题,我就是这么处理的,对帧速率影响并不大。
(具体见参考文章1)
战国中组合键(*3)的做法, 在keyPressed函数中用一个变量gAltKey存放* 或是# 下一次按需要判断是否上一次按了拿两个键,如果是对两次的按键进行移位和或操作,形成一个新的组合键赋值给gkey, 在keyHandler那里还是只要对gkey进行判断处理就可以了。 注意gAltKey在keyPressed函数中一赋值给gkey就马上清0, 而在run中需要对gAltKey存在时间进行判断,多于1秒那么就没有效了,清0.
· 做法较简单
· 这种做法以* 和#开头的两个的组合按键,快捷键组合类型为: *1, #2 ….等等,仍然不够灵活,只能处理两个按键的组合。对于类似“拳王”之类需要很多组合键来发绝招的游戏并不适合。