J2ME游戏优化秘密
本文章描述了代码优化在为移动设备写运行起来速度快的游戏中扮演的角色。我会用例子说明如何、什么时候和为什么要优化你的代
码,来榨干兼容MIDP的手机的每一滴性能。我们将要讨论为什么优化是必要的和为什么有时候最好不要优化。我将解释高级优化和低
级优化的差别,然后我们会知道如何使用J2ME无线开发包(WTK)自带的Profile程序来发现到哪里去优化你的代码。这篇文章最后揭
示了很多让你的MIDlet运行的技术。
为什么优化?
计算机游戏可以分为两大类: 实时的和输入驱动的. 输入驱动的游戏显示游戏的当前运行状态,并在继续之前无限地等待用户的输入.
扑克牌游戏属于这一类,同样,大多数的猜谜游戏、过关游戏和文字冒险游戏都属于这一类。实时游戏,有时候被称为技能或动作游戏
,不等待用户,他们不停地运行直到游戏结束。
技能和动作游戏经常以大量的屏幕上运东为特征(想想Galaga游戏和Robotron游戏)。刷新率必须至少有10fps(每秒的帧数)并且要
有足够的动作来保持玩家的挑战性。它们需要玩家快速的反应和好的手眼配合,所以就强迫S&A(技能和动作)游戏必须对玩家的输入
有很强的响应能力。在快速响应玩家案件的同时提供高帧数的图形动作,这是实时游戏的代码必须运行起来快的原因。在用J2ME开发
的时候,挑战性就更大了。
Java 2 Micro Edition(J2ME)是java的一个分解版本。 适用于有限功能的小型设备,比如手机和PDA。J2ME设备有:
*有限的输入能力(没有键盘!)(译者注:这里键盘特指个人电脑的键盘)
*小的显示尺寸
*有限的内存容量和堆大小
*慢速的CPU
在J2ME平台上写出快的游戏-------写出在比桌面电脑里的慢得多的CPU上运行的代码更是挑战了开发者。
什么时候不优化
如果你不是在写一个技能或者动作游戏,那么可能不需要优化。如果玩家已经为自己的下一步考虑了几秒钟抑或几分钟,她可能不会
介意如果你的游戏响应花掉了几百微秒。这个规则的一个例外是,如果这个游戏在决定下一步如何运行的时候有大量的工作要处理,比
如搜索一百万个可能的象棋片组合。这种情况下,你可能想要优化你的代码,从而在几秒钟内计算出电脑的下一步,而不是几分钟。
就算你正在写这种类型的游戏,优化也可能是危险的。许多这样的技术伴随着一个代价--他们表示着好”的程序设计这个通常概念飞
过来的时候,同时使你的代码更难读懂。有些是一个权衡,需要开发者大大增加程序的大小来得到性能上一点点的改进。J2ME开发者
们对于保持他们的JAR尽可能的小这个挑战再熟悉不过了。这里是一些不优化的理由:
*优化是一个增加bug的好手
*有些技术会降低你的代码的移植性
*你可能要花费大量的努力来得到微小的或者没有改进
*优化是困难的
最后一点需要一些阐述。优化是一个活动目标,在Java平台上更是这样,而且在J2ME上就更加突出,因为其运行环境是那样的多变。
你优化后的代码可能在一个模拟器上运行得更快,但却在实际设备上更慢,或者相反。为一部手机优化可能会降低其在另一部上的性能。
不过还是有希望。有两条路径你可以做优化,高层的和底层的。第一条基本上会在所有的平台上增加执行性能,甚至会改进你代码的
整个质量。第二条是可能会让你头疼的,但是那些底层技术是很容易创造的,而且更加容易消去如果你不想使用它们。最起码,他们看起来很有趣。
我们将用系统的timer在实际设备上剖析你的代码,这可以帮助你测量出那些技术在你所开发的硬件上到底有多有效。
最后一点:
*优化是有趣的
一个反面例子:
让我们来看一看这个包含两个类的简单的应用程序,首先,是Midlet...
import javax.microedition.midlet.*;
import javax.microedition.lcdui.*;
public class OptimizeMe extends MIDlet implements CommandListener {
private static final boolean debug = false;
private Display display;
private OCanvas oCanvas;
private Form form;
private StringItem timeItem = new StringItem( "Time: ", "Unknown" );
private StringItem resultItem =
new StringItem( "Result: ", "No results" );
private Command cmdStart = new Command( "Start", Command.SCREEN, 1 );
private Command cmdExit = new Command( "Exit", Command.EXIT, 2 );
public boolean running = true;
public OptimizeMe() {
display = Display.getDisplay(this);
form = new Form( "Optimize" );
form.append( timeItem );
form.append( resultItem );
form.addCommand( cmdStart );
form.addCommand( cmdExit );
form.setCommandListener( this );
oCanvas = new OCanvas( this );
}
public void startApp() throws MIDletStateChangeException {
running = true;
display.setCurrent( form );
}
public void pauseApp() {
running = false;
}
public void exitCanvas(int status) {
debug( "exitCanvas - status = " + status );
switch(status) {
case OCanvas.USER_EXIT:
timeItem.setText( "Aborted" );
resultItem.setText( "Unknown" );
break;
case OCanvas.EXIT_DONE:
timeItem.setText( oCanvas.elapsed+"ms" );
resultItem.setText( String.valueOf( oCanvas.result ) );
break;
}
display.setCurrent( form );
}
public void destroyApp(boolean unconditional)
throws MIDletStateChangeException {
oCanvas = null;
display.setCurrent ( null );
display = null;
}
public void commandAction(Command c, Displayable d) {
if ( c == cmdExit ) {
oCanvas = null;
display.setCurrent ( null );
display = null;
notifyDestroyed();
}
else {
running = true;
display.setCurrent( oCanvas );
oCanvas.start();
}
}
public static final void debug( String s ) {
if (debug) System.out.println( s );
}
}
Second, the OCanvas class that does most of the work in this example...
import javax.microedition.midlet.*;
import javax.microedition.lcdui.*;
import java.util.Random;
public class OCanvas extends Canvas implements Runnable {
public static final int USER_EXIT = 1;
public static final int EXIT_DONE = 2;
public static final int LOOP_COUNT = 100;
public static final int DRAW_COUNT = 16;
public static final int NUMBER_COUNT = 64;
public static final int DIVISOR_COUNT = 8;
public static final int WAIT_TIME = 50;
public static final int COLOR_BG = 0x00FFFFFF;
public static final int COLOR_FG = 0x00000000;
public long elapsed = 0l;
public int exitStatus;
public int result;
private Thread animationThread;
private OptimizeMe midlet;
private boolean finished;
private long started;
private long frameStarted;
private long frameTime;
private int[] numbers;
private int loopCounter;
private Random random = new Random( System.currentTimeMillis() );
public OCanvas( OptimizeMe _o ) {
midlet = _o;
numbers = new int[ NUMBER_COUNT ];
for ( int i = 0 ; i < numbers.length ; i++ ) {
numbers[i] = i+1;
}
}
public synchronized void start() {
started = frameStarted = System.currentTimeMillis();
loopCounter = result = 0;
finished = false;
exitStatus = EXIT_DONE;
animationThread = new Thread( this );
animationThread.start();
}
public void run() {
Thread currentThread = Thread.currentThread();
try {
while ( animationThread == currentThread && midlet.running
&& !finished ) {
frameTime = System.currentTimeMillis() - frameStarted;
frameStarted = System.currentTimeMillis();
result += work( numbers );
repaint();
synchronized(this) {
wait( WAIT_TIME );
}
loopCounter++;
finished = ( loopCounter > LOOP_COUNT );
}
}
catch ( InterruptedException ie) {
OptimizeMe.debug( "interrupted" );
}
elapsed = System.currentTimeMillis() - started;
midlet.exitCanvas( exitStatus );
}
public void paint(Graphics g) {
g.setColor( COLOR_BG );
g.fillRect( 0, 0, getWidth(), getHeig![]()
- 最新文章
- 使用Eclipse配置doja开发环境[01-11]
- 缩小Doja程序的大小[01-11]
- Doja版的helloword程序[01-11]
- 教您如何使用EclipseME开发J2ME程序(2)[01-11]
- J2EE新手入门篇:“Spring”的名词解释[01-11]
- J2EE基础:使用JSF技术开发Web应用程序[01-11]
- 相关文章
- 教您如何使用EclipseME开发J2ME程序(2)[01-11]
- J2ME游戏开发技巧:用setClip分割图片[01-11]
- J2ME中程序优化的十个小方法[01-11]
- J2ME中有关手机中文传输问题的解决办法[01-11]
- 教您如何使用EclipseME开发J2ME程序(3)[01-11]
- J2ME开发笔记-键盘响应[01-10]
