一般来说,最常用的 游戏 结构是这样的:游戏开始后即进入一个循环,每次循环做三件事情: 1. 获取玩家的输入。 2. 更新游戏状态。 3. 刷新屏幕。 理论" name="description" />

简单的游戏结构

发表于:2007-04-28来源:作者:点击数: 标签:结构简单游戏
MI LY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'"> 一般来说,最常用的 游戏 结构是这样的:游戏开始后即进入一个循环,每次循环做三件事情: 1. 获取玩家的输入。 2. 更新游戏状态。 3. 刷新屏幕。 理论

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.       两个threadcurrentThread当然就是当前执行的这个线程,那么gameThread当然也就是运行这个run方法的线程(不然while一开始就不成立,游戏就没法运行了^_^)。这里它们的作用是控制游戏的循环,当一开始运行时,currentThread==gameThread,循环下去;当游戏需要退出时,将gameThread(类成员)设置为null,那么循环结束,游戏就退出了。强制结束某个线程是很不好的方法,sun的标准库里早就deprecate这个方法了。

2.       tick方法:处理玩家的输入以及游戏状态的变更。

3.       render方法:顾名思义,绘图。这里MIDP2.0相对于1.0有很大的改进,后面说。

4.       几个long型的time的用处。代码看起来很直观,就是考虑到tickrender方法执行时间可能不固定,用time加以计算,得到需要sleep的时间。这样的结果就是游戏画面以一个固定的延时刷新,FixedDelay。当然,还有另外一种控制方式,即固定频率的刷新,FixedRate

a)         FixedDelay:固定延时的好处是画面看起来总是很流畅,而且一定不会出现跳帧的现象。

b)        FixedRate:在一种情况下和FixedDelay有差别,即tickrender方法消耗的时间比预设的时间(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的那个线程同步(J2meSpecification说的是,可以保证你repaint了以后,paint方法在之后的某个时刻被调用,但不能保证马上就被调用,因为paint是个比较耗时的方法,它无法确定其他的paint是否及时完成了)。

MIDP2.0里,有所改进了。首先是你可以通过GameCanvas的一个方法getGraphics来获取那个g,然后这个g就永不失效了(当然是在这个GameCanvas的生命周期内)。当然,这个g1.0里的还是有所区别。这个g实际上是画在一块缓冲“画布”上,即内存里。完全画好以后,再用flushGraphics刷到屏幕上。缓冲的好处是可以让屏幕在画时不显得闪烁,很有用的东西。flushGraphics还有另外一个作用,就是强制输出,以达到显示的那个线程与游戏控制线程的同步(这也是为什么做时间协调时,要把显示方法所消耗的时间也计算进来)。

Tick…………暂时先不讨论。



原文转自:http://www.ltesting.net