一般来说,最常用的 游戏 结构是这样的:游戏开始后即进入一个循环,每次循环做三件事情: 1. 获取玩家的输入。 2. 更新游戏状态。 3. 刷新屏幕。 理论" name="description" />
MILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">一般来说,最常用的游戏结构是这样的:游戏开始后即进入一个循环,每次循环做三件事情:
1. 获取玩家的输入。
2. 更新游戏状态。
3. 刷新屏幕。
理论上说,刷屏的频率在24Hz以上,人的眼睛看到的就是动画了。
这个例子游戏的主循环是这样的:
public void run() {
Graphics g = getGraphics();
Thread currentThread = Thread.currentThread();
try {
while (currentThread == gameThread) {
long startTime = System.currentTimeMillis();
if (isShown()) {
if (isPlay) {
tick();
}
render(g);
}
long timeTake = System.currentTimeMillis() - startTime;
if (timeTake < MILLIS_PER_TICK) {
synchronized (this) {
wait(MILLIS_PER_TICK - timeTake);
}
} else {
currentThread.yield();
}
}
} catch (InterruptedException ex) {
// won't be thrown
}
}
说明:
1. 两个thread。currentThread当然就是当前执行的这个线程,那么gameThread当然也就是运行这个run方法的线程(不然while一开始就不成立,游戏就没法运行了^_^)。这里它们的作用是控制游戏的循环,当一开始运行时,currentThread==gameThread,循环下去;当游戏需要退出时,将gameThread(类成员)设置为null,那么循环结束,游戏就退出了。强制结束某个线程是很不好的方法,sun的标准库里早就deprecate这个方法了。
2. tick方法:处理玩家的输入以及游戏状态的变更。
3. render方法:顾名思义,绘图。这里MIDP2.0相对于1.0有很大的改进,后面说。
4. 几个long型的time的用处。代码看起来很直观,就是考虑到tick和render方法执行时间可能不固定,用time加以计算,得到需要sleep的时间。这样的结果就是游戏画面以一个固定的延时刷新,FixedDelay。当然,还有另外一种控制方式,即固定频率的刷新,FixedRate。
a) FixedDelay:固定延时的好处是画面看起来总是很流畅,而且一定不会出现跳帧的现象。
b) FixedRate:在一种情况下和FixedDelay有差别,即tick和render方法消耗的时间比预设的时间(MILLIS_PER_TICK)还长。FixedDelay这时不再wait,直接绘制下一幅图;而FixedRate就不能绘了,否则频率不固定。它的选择是不绘这一幅,而是直接进入下一个循环,更新游戏状态,综合计算时间,绘下一幅图——于是产生了跳帧。
像CS或星际这样的游戏,跳帧是必须的,因为联机游戏必须保证各方的图像显示的同步,不固定刷新频率会导致错误。而像仙剑之类的单机游戏倒是可以考虑用固定延时,因为不跳帧不会产生什么错误,同时也可以增加游戏画面的流畅性。
绘图:
private void render(Graphics g) {
// Set Background color to beige
g.setColor(0xF8DDBE);
g.fillRect(0, 0, width, height);
g.setColor(0x0000ff);
// LayerManager Paint Graphics
layerManager.paint(g, 0, 0);
flushGraphics();
}
在MIDP1.0里面,Canvas的绘图只能写在paint(Graphics g)方法里,因为一来g参数在此方法结束后就没有意义了,保留它的副本没有用处;二来此方法由平台调用,而不是由Canvas这个线程调用。因此在MIDP里绘图总是一件很痛苦的事情,特别是需要保证Canvas线程与调用paint的那个线程同步(J2me的Specification说的是,可以保证你repaint了以后,paint方法在之后的某个时刻被调用,但不能保证马上就被调用,因为paint是个比较耗时的方法,它无法确定其他的paint是否及时完成了)。
在MIDP2.0里,有所改进了。首先是你可以通过GameCanvas的一个方法getGraphics来获取那个g,然后这个g就永不失效了(当然是在这个GameCanvas的生命周期内)。当然,这个g和1.0里的还是有所区别。这个g实际上是画在一块缓冲“画布”上,即内存里。完全画好以后,再用flushGraphics刷到屏幕上。缓冲的好处是可以让屏幕在画时不显得闪烁,很有用的东西。flushGraphics还有另外一个作用,就是强制输出,以达到显示的那个线程与游戏控制线程的同步(这也是为什么做时间协调时,要把显示方法所消耗的时间也计算进来)。
Tick…………暂时先不讨论。