j2me上的键盘响应估计是继画图后出现Bug最多的地方了,尤其像手机游戏这种键盘操作较多的J2ME程序。在工作的过程中CoCoMo曾不止一次的被问及有关键盘响应的问题,pigham前两天还在为他的游戏在7210上按键不能及时响应而发愁,就在刚才我还在努力的解决着S700上不支持getGameAction()的问题。虽然CoCoMo在这个行业已经工作了一年多了,但是键盘响应的bug仍然时常蹦出来刺激我的神经,所以千万不要小瞧了这个不起眼的keyPressed()
keyPressed()响应的位置:
弄清楚keyPressed()响应的位置对最终解决按键响应不及时很有帮助。理论上keyPressed()是由KVM负责的,当Canvas的子类被Display.setCurrent()之后,只要按下任何按钮就会引发keyPressed()。但这只是某些人一厢情愿的美好愿望,仅限于理论研究的范畴,理论和实际往往相差甚远不是吗。实际上keyPressed()的响应是有位置的,CoCoMo可以用如下程序做一个实验:
while(b_running)
{
updateState();
repaint();
serviceRepaints();
long d = System.currentTimeMillis() - s_curFrameTime;
if (d < 80) Thread.sleep(80 - d);
s_curFrameTime = System.currentTimeMillis();
}
这是一个传统意义上的游戏循环,CoCoMo分别在updateState(),paint()和keyPressed()中加入调试语句如下:
void updateState() {
System.out.println("UpdateState");
}
protected void paint(Graphics g) {
System.out.println("Paint");
}
protected void keyPressed(int keyCode) {
System.out.println("keyPressed");
}
protected void keyReleased(int keyCode) {
System.out.println("keyReleased");
}
然后启动MIDlet,一阵狂按之后,得出如下结果:
UpdateState
Paint
keyReleased
UpdateState
Paint
keyPressed
UpdateState
Paint
keyReleased
keyPressed
你会惊奇的发现keyPressed()和keyReleased()总是在paint()之后,UpdateState()之前。实际上keyPressed()是在线程sleep的时候引发的,也就是说当Canvas这个线程在空闲状态时,KVM才有机会向激活的Display传递消息说有人按了某键,或者KVM总能向Display传递消息,但Display需要在空闲状态时才能调用keyPressed(),至于是哪种方式那是底层实现的事情,我们并不得而知,但有一点是可以肯定的:keyPressed()是在Canvas线程sleep的时候被引发的。这也是按键不响应或延时的根本原因:Canvas线程过于繁忙,没有sleep或很少sleep。解决办法:
一:优化程序,减轻Canvas线程负担,使sleep时间增加。
二:在优化后情况仍然存在,可以在需要按键响应的地方强行使线程sleep(20),从而引发keyPressed()。在我的一个项目中需要频繁创建图片,致使线程过度繁忙,即便在S700这样的机型上也出现了按键响应不及时的问题,我就是这么处理的,对帧速率影响并不大。
J2ME按键处理方法:
一:按键逻辑直接写在keyPressed()里
优点:测试时经常使用,对于短小程序编写速度快。
缺点:需要在keyPressed()里再写个switch(gameState)状态机,这样的缺点估计地球人都知道了。而且将逻辑运算写进keyPressed()里影响keyPressed()的响应。
二:将keyPressed()里的键值提取,在需要的地方做判断。
基本上现在手机游戏编写都使用这种方法了,实现方式也千变万化,最简单的就是定义一个curKey变量,在keyPressed()里将其赋值,在keyReleased()里将其清空,在updateState()里判断该变量的值。这里需要一个keyCode表来对应curKey是什么键,类似这样:
public static final int PADDLE_UP = 1;
public static final int PADDLE_DOWN = 6;
public static final int PADDLE_LEFT = 2;
public static final int PADDLE_RIGHT = 5;
public static final int PADDLE_FIRE = 20;
public static final int PADDLE_SOFT1 = 21;
public static final int PADDLE_SOFT2 = 22;
public static final int PADDLE_SOFT3 = 23;
演化而来的在波斯王子里由于需要判断连续按键而引入了按键状态的概念:
public static final byte Up_Instant = -1; //瞬间抬起,中间过度状态
public static final byte Up_Continuous = 0; //持续抬起,无按键
public static final byte Dn_Instant = 1; //瞬间按下,走
public static final byte Dn_Continuous = 2; //持续按下,跑
public static final byte Dn_Continuous2 = 3; //连击两下,滚
这种处理方式需要每帧里都更新按键状态,键被按下在第一帧为Dn_Instant瞬间按下,在第二帧变为Dn_Continuous持续按下。这种按键处理方式在CoCoMo的引擎里使用了很长一段时间,估计SF的兄弟们现在还在使用着这种方式。他最大的缺点是在Dn_Instant转Dn_Continuous时容易出错。
到了彩虹六号和Medieval Combat时因为在短时间内需要判断连续不同按键来发大招,所以按键处理引入了虚拟按键的概念:演化而来的在波斯王子里由于需要判断连续按键而引入了按键状态的概念:
public static final int gk_UP = 1;
public static final int gk_DOWN = 1<<1;
public static final int gk_LEFT = 1<<2;
public static final int gk_RIGHT = 1<<3;
public static final int gk_NUM0 = 1<<4;
public static final int gk_NUM1 = 1<<5;
public static final int gk_NUM2 = 1<<6;
public static final int gk_NUM3 = 1<<7;
public static final int gk_NUM4 = 1<<8;
public static final int gk_NUM5 = 1<<9;
public static final int gk_NUM6 = 1<<10;
public static final int gk_NUM7 = 1<<11;
public static final int gk_NUM8 = 1<<12;
public static final int gk_NUM9 = 1<<13;
public static final int gk_STAR = 1<<14;
public static final int gk_POUND = 1<<15;
public static final int gk_LSOFT = 1<<16;
public static final int gk_RSOFT = 1<<17;
public static final int gk_MSOFT = 1<<18;
在一定时间内用mask对curKey做掩码就可以判断是否按下了一组特定键,时间过了就清空curKey。
CoCoMo因为厌烦每个机型上都需要一个不同的keyCode表,所以CoCoMo用getGameAction()和Canvas自带的keyCode表,只需要一个keyConvert()将按键转换到CoCoMo自定义的keyCode表即可,这个自定义的keyCode表在每个机型上都是一样的:
public static final byte KEY_NONE = -1;
public static final byte KEY_0 = 0;
public static final byte KEY_UL = 1;
public static final byte KEY_U = 2;
public static final byte KEY_UR = 3;
public static final byte KEY_L = 4;
public static final byte KEY_ATTACK = 5;
public static final byte KEY_R = 6;
public static final byte KEY_DL = 7;
public static final byte KEY_D = 8;
public static final byte KEY_DR = 9;
public static final byte KEY_STAR = 10;
public static final byte KEY_POUND = 11;
public static final byte KEY_SOFT1 = 12;
public static final byte KEY_SOFT2 = 13;
这样可以免去移植之苦,对于某些机型例如S700不支持getGameAction()的问题,CoCoMo用此种方法来解决:
try { //解决getGameAction不被支持的情况
keyCode = s_game.getGameAction(code);
}
catch(Exception e) {
keyCode = CRes.KEY_NONE;
}
不支持的时候会抛出一个异常,让keyCode不做转换即可。哈哈。
我只想插一句关于键盘映射的题外话话:
0-9,*,#是不需要做键盘映射的。只剩下几个软键及方向键,确定键需要获取键值,相信大家能很轻松的解决。
分享到:
相关推荐
3. 事件处理:j2ME使用Event Dispatcher模型,开发者需注册事件监听器来处理用户输入,如键盘和触摸屏事件。 五、图形与动画 j2ME使用Graphics类进行绘图操作,支持基本的绘图函数如drawRect()、drawString()等。...
4. **输入处理**:引擎需要解析用户输入,如键盘、触摸屏或游戏控制器的事件,以便游戏响应玩家操作。 5. **资源管理**:内存和存储在移动设备上是宝贵的,游戏引擎会包含资源管理系统,优化图片、音频和数据的加载...
**J2ME拼图游戏详解** Java 2 Micro Edition(J2ME)是Java平台的一个子集,专门设计用于移动设备、嵌入式系统和智能电器等资源受限的环境。在J2ME中开发游戏,如拼图游戏,是一项挑战性但有趣的工作,因为它需要在...
**J2ME手机游戏开发详解** Java 2 Micro Edition(J2ME)是Java平台的一个子集,专门设计用于资源有限的嵌入式设备,包括移动电话和掌上设备。在2000年代初,J2ME是手机游戏开发的主流技术,尤其在诺基亚、摩托罗拉...
4. **事件处理**:通过监听和处理键盘、触摸屏等设备输入事件,游戏可以响应用户的操作。J2ME中的KeyListener和PointerListener接口是实现这一功能的关键。 5. **游戏逻辑**:游戏的核心在于其逻辑,这可能涉及到...
《J2ME飞机游戏开发详解》 J2ME(Java 2 Micro Edition)是Java平台的一个子集,专为移动设备、嵌入式系统等资源有限的环境设计。本篇文章将深入探讨如何利用J2ME技术开发一款简单的飞机游戏,以此帮助初学者理解...
3. **事件处理**:键盘输入、触摸屏操作等事件在游戏开发中至关重要。J2ME的KeyListener和CommandListener接口可用于捕获和响应用户输入,调整游戏状态。 4. **游戏逻辑**:游戏的核心在于逻辑,这通常涉及到角色...
J2ME游戏需要处理用户的键盘、触摸或方向键输入。源代码中可能包含对按键监听和响应的实现,以控制游戏对象的移动和交互。 6. **音频与多媒体**: 音频效果对于增强游戏体验至关重要。J2ME支持MIDI和PCM音频,源...
通过监听键盘、触摸屏或游戏键的事件,开发者可以实现玩家的输入响应。 6. **网络通信**:J2ME支持HTTP和TCP/IP协议,允许游戏实现在线对战、排行榜更新或下载游戏资源等功能。 7. **文件系统和数据存储**:在J2ME...
**J2ME赛车手机游戏详解** Java 2 Micro Edition(J2ME)是Java平台的一个子集,专门设计用于资源有限的设备,如移动电话、智能手表和家用电器等。在J2ME中开发游戏,尤其是像赛车游戏这样的动态、交互性强的应用,...
【J2ME贪吃蛇源码详解】 J2ME,全称Java 2 Micro Edition,是Java平台的一个子集,主要用于开发移动设备、嵌入式系统等小型设备上的应用程序。这个"j2me贪吃蛇源码"是针对J2ME平台设计的一款经典游戏——贪吃蛇的...
3. 用户输入处理:响应键盘或触摸屏事件。 4. 图形绘制:使用Canvas绘制游戏场景。 5. 资源管理:加载和存储图片、音频等资源。 6. 网络通信:如有必要,与服务器进行数据交换。 八、优化与调试 由于移动设备资源...
3. **事件处理**:通过监听键盘或触摸屏事件,游戏可以响应用户的操作。J2ME使用`KeyListener`接口来捕获键盘事件,但对移动设备,通常会用到`PointerListener`来处理触摸事件。 4. **数据结构**:蛇的身体通常由一...
5. **事件处理**:学习如何监听并响应用户的键盘、触摸或摇杆输入,以及如何处理定时器事件,以实现游戏逻辑。 6. **资源管理**:在有限的内存和处理能力下,有效地加载、存储和管理音频、图像和其他资源是J2ME游戏...
5. **事件处理**:J2ME支持触摸屏、键盘和摇杆等多种输入设备的事件处理,开发者需要编写事件监听器来响应用户的操作。 6. **网络通信**:MIDP的Connection API允许游戏进行数据交换,例如下载新关卡、排行榜同步或...
5. `KeyListener`:处理用户输入,响应按键事件。 为了便于学习,每个步骤的源代码都应具有清晰的注释,解释代码的功能和实现细节。此外,你可能还需要一个简单的主程序来启动游戏并加载`GameCanvas`。 在实践中,...
**用户输入处理**:J2ME支持键盘、触摸屏等多种输入方式。游戏源码会包含监听和解析用户输入的代码,例如,按键事件处理,滑动操作识别等。这些输入会被转换为游戏内的动作,比如角色移动或执行特定技能。 **游戏...
**J2ME游戏开发教程详解** Java 2 Micro Edition(J2ME)是Java平台的一个子集,专门设计用于资源有限的设备,如移动电话、智能手表和家用电器等。在2000年代初,J2ME是移动游戏开发的重要平台,许多经典手机游戏都...
2. 输入处理:J2ME支持键盘和触摸屏事件处理,开发者需要监听键盘或触摸屏的输入,通过覆盖keyPressed(), keyReleased()等方法来响应玩家的操作。 3. 游戏循环:为了实现流畅的游戏体验,开发者通常会创建一个无限...
在J2ME中,可以使用键盘监听器(KeyListener)或GameAction事件来处理用户输入。 5. **数据存储** 由于手机内存有限,J2ME游戏通常使用RecordStore来存储非持久化数据,如用户分数、游戏进度等。RecordStore提供了...