2004-11-17 
唐良 
       游戏引擎的结构很多,不过基本上都是在一个游戏主循环内实现。程序里面的主循环包含了程序框架的最主要的结构体。J2me的程序一般都包含两个class文件,一个是MIDlet,一个是Displayable。一般我都是把游戏的主要代码放在Displayable这个类里面。这个类是基于事件驱动的程序,有三个主要相应函数void paint(Graphics g),void keyPressed(int keyCode),void keyReleased(int keyCode)。 
  
1.         使用Runnable和创建线程的主循环 
一般主体的做法就是让Displayable这个类实现Runnable这个接口,然后在其构造函数中创建一个线程,启动其run()函数,而run函数里面就包含了游戏的主循环。下面是我在仙剑里面的片断代码。 
  
public class GameMIDlet extends MIDlet { 
  static GameMIDlet instance; 
  Display display; 
  GameDisplayable displayable = null; 
  
  public GameMIDlet() { 
    instance = this; 
    display =  Display.getDisplay(this); 
    displayable = new GameDisplayable(); 
  } 
  
  public void startApp() { 
    display.setCurrent(displayable); 
  } 
  
  public void pauseApp() { 
  } 
  
  public void destroyApp(boolean unconditional) { 
    displayable.running = false; 
  } 
  
  public static void quitApp() { 
    instance.destroyApp(true); 
    instance.notifyDestroyed(); 
    instance = null; 
  } 
} 
  
public class GameDisplayable extends FullCanvas implements Runnable { 
  /** 主控制线程 */ 
  Thread MainThread = null; 
  /** 游戏时钟间隔 毫秒为单位 */ 
  public static long timeinterval = 20; 
  public static boolean Isstable = true; 
  
  /* 用于游戏时钟的变量 */ 
  public static long timeold = 0; 
  public static long timenow = 0; 
  public long interval = 0; 
  public static long frames_per_second = 0; 
  int count = 0; 
  long second = 0; 
  public static boolean running = true; 
public GameDisplayable() { 
      // 开始主线程 
    Thread MainThread = new Thread(this); 
    MainThread.start(); 
  } 
public void run() { 
    while (running) { 
      timenow = System.currentTimeMillis(); 
      interval = timenow - timeold; 
  
      if (interval >= timeinterval) { 
        timeold = timenow; 
        Game_Process(); 
        if (second != (System.currentTimeMillis() / 1000)) { 
          second = System.currentTimeMillis() / 1000; 
          frames_per_second = count; 
          count = 1; 
        } 
        else 
          count++; 
      } 
      lib.sleep(30); 
    } 
  
 } 
  
其中关于控制主循环速度的代码可以不要,但是lib.sleep(30)必须保留,因为在Nokia 60的手机上,如果去除了sleep(30),那么游戏将无法切换回来。同时,在游戏中任何一个内部循环中,也必须加入sleep(30)这个等待,才能让游戏可以切换回来,至于为什么这样做,我暂时还不清楚。30ms是我测试过没有问题的数值,可能比30ms还小的值也是没有问题的。 
  
同时,在MOTO的手机上,必须将游戏的主循环放在一个线程中,游戏才能切换回来,不过可以不加上面说的sleep(30)延时。 
  
2.         不使用线程的主循环办法 
这个办法只能在Nokia的平台上实现,而我只建议在Nokia 40的平台上做,这样不需要线程,道理上来说节约了一些内存,如果不是内存很紧张的机型,那么最好还是使用上一种办法。 
  
游戏的主循环放在MIDlet的class里面,具体做法如下: 
public class GameMIDlet extends MIDlet { 
  GameDisplayable displayable = null; 
  
  /** 游戏时钟间隔 毫秒为单位 */ 
  public static long  timeinterval = 0; 
  //用于游戏时钟的变量 
  public static long timeold = 0; 
  public static long timenow = 0; 
  public long interval = 0; 
  public static long frames_per_second=0; 
  int count=0; 
  long second =0; 
  public static boolean running = false; 
  static boolean exitApp =false; 
  
  public GameMIDlet() { 
    displayable = new GameDisplayable(); 
    running =true; 
  } 
  
  public void startApp() { 
    running =true; 
    Display.getDisplay(this).setCurrent(displayable); 
    while(running) { 
      timenow = System.currentTimeMillis(); 
      interval = timenow - timeold; 
      if (interval >= timeinterval) { 
        timeold = timenow; 
        displayable.Game_Process(); 
        if(second != (System.currentTimeMillis() /1000)){ 
            second = System.currentTimeMillis()/1000; 
            frames_per_second = count; 
            count = 1; 
          }else 
            count ++; 
      } 
    } 
    if(exitApp) { 
      destroyApp(true); 
      notifyDestroyed(); 
    } 
  
  } 
  
  public void pauseApp() { 
    running =false; 
  } 
  
  public void destroyApp(boolean unconditional) { 
    running = false; 
  } 
  
  public static void quitApp() { 
    running =false; 
    exitApp =true; 
  } 
  
} 
   
 
  |