Java游戲學習

          Posted on 2006-11-06 18:45 luosheng 閱讀(380) 評論(0)  編輯  收藏

          這幾天一直在看 <<Killer Game Programming in Java>>,非常經典的好書,現在對游戲有了一個基本的認識,過幾天就寫個貪吃蛇出來.
          因為看得有點快,真正準備寫代碼的時候又發現自己對一些基本知識點還是比較模糊,又返回去看前面.邊看邊做筆記,感覺確實理解得要更加清楚了.

          *FPS 和 按時間準確的Sleeping*
          ============================

          FPS
          --------
          ? 一個測量animation速度的常用指標就是楨速(每秒顯示楨的數量:frames per second),簡稱FPS.在下面的
          代碼中,做一次gameUpdate和gameRender的循環就對應一個楨.
          ? 比如100FPS表示run()中的每次迭代應該用1000/100 == 10ms.這個迭代時間存在period變量中.

          //code example:

          public void run() {
          ? long beforeTime, timeDiff, sleepTime;
          ? beforeTime = System.currentTimeMillis();
          ? running = true;
          ? while(running) {
          ??? gameUpdate();//計算game中的model
          ??? gameRender();//畫一個image, "double buffer"
          ??? paintScreen();//在screen上顯示image
          ?? ?
          ??? timeDiff = System.currentTimeMillis() - beforeTime;
          ??? sleepTime = period - timeDiff;//計算需要sleep的時間

          ??? if (sleepTime <= 0)
          ????? sleepTime = 5;

          ??? try {
          ????? Thread.sleep(sleepTime);
          ??? }catch(InterruptedException e);

          ??? beforeTime = System.currentTimeMillis();
          ? }
          }

          --------
          Timer Resolution
          ? 連續調用兩次timer中間必需的最小時間.這樣才能保證每次調用返回不同的時間.
          ? 比如:
          ?? ?????? long t1 = System.currentTimeMillis( );
          ?? ??? ?long t2 = System.currentTimeMillis( );
          ?? ??? ?long diff = t2 - t1;? // 實際輸出是0,單位是ms

          ? 在win95和win98上,resolution值是55ms,說明只有在每隔55ms后調用timer才會返回不同的值.
          ? 在animation loop中,resolution的會導致animation比期望的要慢而且減小了FPS.因為如果gameUpdate
          和gameRender的時間小于55ms,那么timeDiff變量就會設為0,那么sleepTime就會比實際需要的時間要大.
          ? 為了防止這個問題,每個循環周期時間必須大于55ms,表示最高限制是大約18FPS.這個frame rate被廣泛接
          受,因為屏幕刷新過慢會表現得象閃屏(excessive flicker)一樣.
          ?
          ? 在Windows2000, NT和XP上, currentTimeMillis()的resolution是10到15ms,這樣就可以獲得67-100FPS.
          這個值對游戲來說是可以接受的.在Mac OS X和Linux上的resolution是1ms,相當好了.

          --------
          改進過的J2SE Timers

          ? J2SE 1.4.2有一個沒有被寫入到文檔的精確到微秒的timer class: sum.misc.Perf.
          ? Pref計算diff的方法:

          ??? ? Pref perf = Perf.getPerf();
          ?? ?long countFrep = perf.highResFrequency();
          ?? ?
          ?? ?long count1 = perf.highResCounter();
          ?? ?long count2 = perf.highResCounter();
          ?? ?long diff = (count2 - count1) * 1000000000L / countFreq;
          ?? ??? ??? ??? ??? ?//轉換成納秒nanoseconds
          ? nanoseconds:十憶分之1秒

          ? J2SE 5.0中解決了這個timer的問題,System.nanoTime(),可以象Pref timer一樣來計算時間.
          ?? ?? long count1 = System.nanoTime();
          ?? ?long count2 = System.nanoTime();
          ?? ?long diff = (count1 - count2);//單位是納秒

          --------
          Non-J2SE Timers

          ? Java 3D timer的計算方法:
          ?? ?? long t1 = J3DTimer.getValue();
          ?? ?long t2 = j3DTimer.getValue();
          ?? ?long diff = t2 - t1;//單位是納秒


          *更好的Sleeping*
          ================
          ? animation循環依賴一個好的timer和精確的sleep方法調用.現在在前面的基礎上改進代碼,以保證需要
          的楨速.
          //code example:

          private static final int NO_DELAYS_PER_YIELD = 16;

          public void run() {
          ? long beforeTime, afterTime, timeDiff, sleepTime;
          ? long overSleepTime = 0L;
          ? int noDelays = 0;

          ? beforeTime = J3DTimer.getValue();
          ?
          ? running = true;
          ? while(running) {
          ??? gameUpdate();
          ??? gameRender();
          ??? paintScreen();

          ??? afterTime = J3DTimer.getValue();
          ??? timeDiff = afterTime - beforeTime;
          ??? sleepTime = (period - timeDiff) - overSleepTime;

          ??? if (sleepTime > 0) {
          ????? try{
          ??????? Thread.sleep(sleepTime/1000000L); //nano -> ms
          ????? }catch(InterrruptedException ex){}
          ????? overSleepTime = (J3DTimer.getValue() - afterTime) - sleepTime;
          ??? }else {?? ?//sleepTime <= 0; 楨的時間大于期望的period,
          ?? ??? ?//不sleep直到sleepTime > 0 或 連續運行了NO_DELAYS_PER_YIELD次
          ????? overSleepTime = 0L;
          ??? ?
          ????? if (++noDelays >= NO_DELAYS_PER_YIELD) {
          ??????? Thread.yield();
          ?? ? noDelays = 0;
          ????? }
          ??? }
          ?? ?
          ??? beforeTime = J3DTimer.getValue();

          ? }
          }

          ? 如果sleep()設置成sleep 10 ms,但是確用了12 ms,那么overSleepTime會被設置成2 ms,下次就會少
          sleep 2ms.
          ? 如果楨的時間大于期望的period,那么就不浪費時間sleep,而是一直循環,一定次數后調用Thread.yield(),
          這樣來節省時間而又保證其它線程有機會運行.

          *FPS和UPS*
          ==========

          ? 除了FPS,還有一個有用的測量animation速度的指標:UPS. 在現在的animation循環中每次迭代擁有一次
          update和render.但是這個對應不是必需的.在循環中,可以每一次render前做兩次updates.
          ?
          //code example:
          ?
          public void run() {
          ? ...
          ? running = true;
          ? while(running) {
          ??? gameUpdate();//update 游戲狀態
          ??? gameUpdate();//再一次update 游戲狀態
          ?
          ??? gameRender();
          ??? paintScreen();

          ??? //sleep
          ? }
          ? System.exit(0);
          }
          ? 在上面的代碼中,如果游戲提供了50FPS,那么就每秒就會做100次updates.

          從Rendering中分離Updates
          --------

          ? 對于高FPS速率的一個限制是update和render所需要的時間.假設period = 5ms(1000/5 == 200FPS),如果
          update和render需要的時間大于5ms,那么200FPS就不可能達到.而它們所需要的時間中大部分是被render所消耗的.
          ? 在這種情況下,增加游戲速度的方法是增加UPS的速率.在編程中,也就是在每次迭代中增加gameUpdate的次數.
          但是注意,如果增加gameUpdate的次數過多的話會造成游戲不連續,因為有許多游戲狀態沒有顯示出來.

          新的代碼:

          //code example:

          private static int MAX_FRAME_SKIPS = 5;

          public void run() {
          ? long beforeTime, afterTime, timeDiff, sleepTime;
          ? long overSleepTime = 0L;
          ? int noDelays = 0;
          ? long excess = 0L;

          ? beforeTime = J3DTimer.getValue();
          ?
          ? running = true;
          ? while(running) {
          ??? gameUpdate();
          ??? gameRender();
          ??? paintScreen();

          ??? afterTime = J3DTimer.getValue();
          ??? timeDiff = afterTime - beforeTime;
          ??? sleepTime = (period - timeDiff) - overSleepTime;

          ??? if (sleepTime > 0) {
          ????? try{
          ??????? Thread.sleep(sleepTime/1000000L); //nano -> ms
          ????? }catch(InterrruptedException ex){}
          ????? overSleepTime = (J3DTimer.getValue() - afterTime) - sleepTime;
          ??? }else {?? ?
          ????? excess -= sleepTime;
          ????? overSleepTime = 0L;
          ??? ?
          ????? if (++noDelays >= NO_DELAYS_PER_YIELD) {
          ??????? Thread.yield();
          ?? ? noDelays = 0;
          ????? }
          ??? }
          ?? ?
          ??? beforeTime = J3DTimer.getValue();

          ??? int skips = 0;
          ??? while((excess > period) && (skips < MAX_FRAME_SKIPS)) {
          ????? excess -= period;
          ????? gameUpdate();
          ????? skips++;
          ??? }

          ? }
          }

          ? 如果update/render實際需要12ms,但是需要的period是10ms,那么sleepTime會是-2ms(由于引入overSleepTime,
          所以可能會更小一點).額外的執行時間被加到excess變量中.
          ? 當excess達到period大小時,那么相當于丟失了一個楨,在while循環中,為每次丟失執行gameUpdate.但是限制在
          MAX_FRAME_SKIPS里.
          ? 這樣做的優點是,如果一個游戲的update/render速度不能滿足期望的FPS時,那么就會另外執行gameUpdate.
          這樣改變了游戲的狀態但是沒有馬上顯示出來,最后用戶會看見游戲移動更"快"了,雖然每秒鐘顯示的楨數并沒有
          改變.

          from:http://blog.csdn.net/starshus/archive/2006/11/03/1364979.aspx

          ?

          posts - 4, comments - 0, trackbacks - 0, articles - 0

          Copyright © luosheng

          welcome to my blog ! !
          主站蜘蛛池模板: 虎林市| 鄂托克前旗| 广水市| 甘孜| 开封市| 融水| 原平市| 行唐县| 锡林郭勒盟| 长兴县| 根河市| 海原县| 和平县| 建瓯市| 林西县| 尼木县| 海林市| 巫溪县| 永定县| 合作市| 永州市| 嘉义市| 星子县| 双峰县| 积石山| 鄂尔多斯市| 阳东县| 姚安县| 清苑县| 吉林市| 开江县| 思南县| 神木县| 尼木县| 察雅县| 湘潭市| 新郑市| 青浦区| 彩票| 南充市| 盱眙县|