Effective J2ME(3)
本文节选于笔者在数年前开发手机游戏时总结的一份文档。一家之言,贻笑大方。2.2 规模
??? 通常J2ME设备对Jar文件的大小进行了限制,例如Nokia6610允许最大的Jar大小为64K。因此在开发J2ME应用时,应该尽量编写精简的代码,使用具有可以接受效果的最小的图片。2.2.1 编写精简的代码
??? 很多人都曾提出过在J2ME开发中减小代码大小的建议,例如减少类和方法的个数、缩短变量和方法名、打破类的继承关系等等。但是我认为,这在做法都是在牺牲了程序的可读性、可扩展性的基础之上的,而且代价高昂。没有必要为了减少代码的大小刻意地这样做,这种代码通常难以调试和修改。与此相反,好的设计同样可以有效地减小代码的大小。代码冗余往往是编写精简代码的最大敌人而不是过长的变量名。唯一例外的是,我认为在J2ME开发中可以使用公共的类成员变量,而不必象在J2SE和J2EE开发中那样,将类的状态封装的那样完美。这样做既可以加快程序的速度,又比提供额外的访问方法(get方法和set方法)的做法生成的代码更小。在我编写的四款游戏中,雷鸟号、BomberMan和荒岛探宝的代码规模都控制在2000-2200行左右。丛林武士的代码在3100行左右。其中用于程序框架部分(这四款游戏重用的基本的框架代码)的代码在800-900左右。2.2.2 合并图片以减小图片的总大小
??? 在J2ME应用中通常使用.png格式的图片。由于.png图片使用彩色查找表(调色板)来存储颜色,所以将在颜色上类似的多个图片合并成一张图片可以有效地减少图片的总大小。例如以下四张图片 总大小为649字节。如果合并为一张图片之后 ,大小为191字节。在使用合并后的图片时,可以利用Graphics类的setClip(int sx ,int sy, int width ,int height)来设置实际的绘图区域。例如要在屏幕上(50,50)这个点上绘出第三把钥匙的代码是:(假设每把钥匙的大小为12×12)
g.setClip(50,50,12,12);
g.drawImage(image,50-12*2,50,20);
?
2.3 可移植性
??? 尽管目前我们的开发都是基于MIDP1。0之上的,但是不同的手机厂商往往提供了特定的开发包。例如Nokia提供了DirectGraphics类、 DeviceControl类等;Motorola提供了Game API等。此外,不同型号手机的屏幕尺寸和处理器速度往往也不相同。因此在开发的过程中,应该充分认识到这些不可移植的因素,尽量增强程序的可移植性和自适应能力。2.3.1 尽量使用标准的类库
??? Motorola 的Game API针对游戏的开发提供了一些底层支持。但是如果我们在程序中使用了这些API,那么就意味着这款游戏将很难移植到Motorola以外的手机上,也无法移植到不支持Game API的Motorola 手机。就我们目前的开发要求而言,这是不明智的。同样,Nokia提供了DirectGraphics类,支持图片的旋转、对图片象素的访问等等。在某些情况下,使用这些API的确可以带来便利。例如如果使用DirectGraphics的图片旋转功能就可以节省图片资源,不必另外在资源中提供旋转后的图片。利用DirectGraphics的对图片象素的访问功能就可以利用象素的α通道计算出图片可见部分的边缘。在这种情况下,就要在可移植性和便利性之间做出取舍。就我而言,应该选择前者。2.3.2 运行时得到屏幕的大小
??? 不要在程序中使用硬编码的屏幕尺寸。比如语句 g.drawString(“载入中…”,64,100,Canvas.HCENTER);这句代码明显是针对128象素宽的屏幕的。不适合移植到具有其它显示大小的设备上。如果可能,应该使用类似于g.drawString(“载入中…”,sCanvasWidth/2,sCanvasHeight- 16,Canvas.HCENTER);的语句。对于变量sCanvasWidth,一种做法是将其定义为静态常量,如public static final int sCanvasWidth=128;这种做法的缺点在于,当程序需要移植到其它设备上时需要修改变量的初值。我个人推荐的做法是将其定义为静态变量,同时在程序运行时为其赋值一次。比如在显示闪屏之前(因为这个操作只需执行一次)利用sCanvasWidth=getWidth();获得屏幕的尺寸。2.3.3 将控制游戏速度的参数保存在Jad文件中
??? 在目前的J2ME游戏中,通常通过调整游戏主线程的休眠时间来控制游戏的速度。对于处理速度较快的机型,可以适当增加休眠的时间;对于处理速度较慢的机型,可以适当减少休眠时间。通过这种方法来消除具有不同处理速度的手机之间的差异。推荐将这个参数保存在Jad文件中,而不是将其硬编码在程序代码中。这样做的优点在于可以在不必重新编译整个工程的情况下,针对目标机的特点调整游戏的速度。2.3.4 使用GameAction来处理按键事件
??? 使用GameAction会增强应用程序的可移植性。MIDP 1.0规范中定义了KeyCode和GameAction之间的转换规则。Canvas类定义了抽象游戏动作集,例如UP、DOWN、LEFT、 RIGHT和FIRE等等。通过Canvas类的public int getGameAction(int keyCode)和public int getKeyCode(int gameAction)两个方法可以实现KeyCode和GameAction之间的转换。需要注意的是,getKeyCode只能返回基于 GameAction的一个KeyCode,即使MIDP 1.0允许超过一个KeyCode被实现到相同的GameAction。另外,GameAction映射对于要求快速键盘响应的游戏来说可能并不适用。不推荐在同一个应用程序中混合使用KeyCode和GameAction