AndEngine學習(二):AndEngine引擎運行原理

通過對於一般遊戲的邏輯原理的分析,以及對AndEngine源碼的閱讀,總算是對遊戲的一般架構以及AndEngine是如何實現這一架構的原理有瞭一定的瞭解,總結一下備忘!

一般的遊戲主邏輯:(在遊戲的主線程中)

[java] <span style="font-size:16px;">while (true) 

    用戶交互監聽(用戶輸入) 
    邏輯運算 
    繪制屏幕 
}</span> 
<span style="font-size:16px;">while (true)
{
 用戶交互監聽(用戶輸入)
 邏輯運算
 繪制屏幕
}</span>
如果簡單的寫成這樣會有一個很嚴重的問題,就是在不同配置的機器上遊戲運行的效果不一樣,因為線程是在一直不停的運行的,而不同的CPU會影響到邏輯的運算,而不同的GPU又會影響其繪制效率,所以就是,在不同的配置的機器上在一定的時間內,循環運行的次數是不一樣的,假設一個精靈會在一幀中走10px,不同機器上每秒跑10幀和跑100幀,那麼精靈就會出現在不同的機器上在相同的時間內移動的距離是不同的!這是很嚴重的!所以,主邏輯有瞭下面的改進,

[java] <span style="font-size:16px;">while (true) 

        startTime = currentTime; 
        用戶輸入監聽 
        邏輯運算 
        endTime = currentTime; 
             
        deltaTime = endTime – startTime; 
        if (deltaTime < FRAME_LENGTH_PER_SECOND) 
        { 
            sleep(deltaTime); 
        } 
             
        繪制屏幕 
}</span> 
<span style="font-size:16px;">while (true)
{
  startTime = currentTime;
  用戶輸入監聽
  邏輯運算
  endTime = currentTime;
   
  deltaTime = endTime – startTime;
  if (deltaTime < FRAME_LENGTH_PER_SECOND)
  {
   sleep(deltaTime);
  }
   
  繪制屏幕
}</span>
註意currentTime是獲取的系統當前時間,deltaTime即用於用戶輸入監聽,邏輯運算的時間,而FRAME_LENGTH_PER_SECOND這個值很重要,這是我們希望每幀用的時間(當然是毫秒級的,即 每秒 / 我們希望遊戲在不同的機器上每秒都運行的幀數上限制,比如我們希望遊戲在所有的機器上都保持在30幀以內,則FRAME_LENGTH_PER_SECOND=1000 / 30 = 33ms(毫秒)),當然我們是認為繪制屏幕過程很快的,如果使用雙緩沖的話,在邏輯運算部分就已經將要繪制的內容繪制到緩沖區瞭,而繪制屏幕的過程則相當於緩沖區到屏幕的一個拷貝,過程會非常快!

帥帥的分割線 www.aiwalls.com ===============================================================================================

下面就要來介紹一下AndEngine是來如何實現這個主線程死循環的!

在Engine類中有一個內部類UpdateThread類,

[java] <span style="font-size:16px;">    private class UpdateThread extends Thread 
    { 
        // ===========================================================  
        // Constants  
        // ===========================================================  
 
        // ===========================================================  
        // Fields  
        // ===========================================================  
 
        // ===========================================================  
        // Constructors  
        // ===========================================================  
 
        public UpdateThread() 
        { 
            super(UpdateThread.class.getSimpleName()); 
        } 
 
        // ===========================================================  
        // Getter & Setter  
        // ===========================================================  
 
        // ===========================================================  
        // Methods for/from SuperClass/Interfaces  
        // ===========================================================  
 
        @Override 
        public void run() 
        { 
            android.os.Process.setThreadPriority(Engine.this.mEngineOptions.getUpdateThreadPriority()); 
            try 
            { 
                while (true) 
                { 
                    Engine.this.onTickUpdate(); 
                } 
            } 
            catch (final InterruptedException e) 
            { 
                Debug.d(this.getClass().getSimpleName() + " interrupted. Don't worry – this " 
                        + e.getClass().getSimpleName() + " is most likely expected!", e); 
                this.interrupt(); 
            } 
        } 
 
        // ===========================================================  
        // Methods  
        // ===========================================================  
 
        // ===========================================================  
        // Inner and Anonymous Classes  
        // ===========================================================  
    }</span> 
<span style="font-size:16px;"> private class UpdateThread extends Thread
 {
  // ===========================================================
  // Constants
  // ===========================================================

  // ===========================================================
  // Fields
  // ===========================================================

  // ===========================================================
  // Constructors
  // ===========================================================

  public UpdateThread()
  {
   super(UpdateThread.class.getSimpleName());
  }

  // ===========================================================
  // Getter & Setter
  // ===========================================================

  // ===========================================================
  // Methods for/from SuperClass/Interfaces
  // ===========================================================

  @Override
  public void run()
  {
   android.os.Process.setThreadPriority(Engine.this.mEngineOptions.getUpdateThreadPriority());
   try
   {
    while (true)
    {
     Engine.this.onTickUpdate();
    }
   }
   catch (final InterruptedException e)
   {
    Debug.d(this.getClass().getSimpleName() + " interrupted. Don't worry – this "
      + e.getClass().getSimpleName() + " is most likely expected!", e);
    this.interrupt();
   }
  }

  // ===========================================================
  // Methods
  // ===========================================================

  // ===========================================================
  // Inner and Anonymous Classes
  // ===========================================================
 }</span>看到瞭吧,在其run()方法中,就是這個遊戲的主循環,而這個主線程是在Engine的構造方法中啟動的(可以看代碼)

問題來瞭,這隻是實現的我們一般遊戲的引擎,是有不同配置機器運行效率不同的問題的,當然我們可以靠TimerHandler來大概控制,可是卻不是可靠的,那麼我們想要的控制幀數的引擎該怎麼做呢?其實AndEngine已經為我們實現瞭,即 LimitedFPSEngine類,從名字上看,這個類實現的功能就是和我們想要的控制幀數是一樣的!下面是LimitedFPSEngine類中的onUpdate方法,源碼已經很認真的解釋瞭控制幀數的原理,

[java] <span style="font-size:16px;">@Override 
    public void onUpdate(final long pNanosecondsElapsed) throws InterruptedException 
    { 
        final long preferredFrameLengthNanoseconds = this.mPreferredFrameLengthNanoseconds; 
        final long deltaFrameLengthNanoseconds = preferredFrameLengthNanoseconds – pNanosecondsElapsed; 
 
        if (deltaFrameLengthNanoseconds <= 0) 
        { 
            super.onUpdate(pNanosecondsElapsed); 
        } 
        else 
        { 
            final int sleepTimeMilliseconds = (int) (deltaFrameLengthNanoseconds / NANOSECONDS_PER_MILLISECOND); 
 
            Thread.sleep(sleepTimeMilliseconds); 
            super.onUpdate(pNanosecondsElapsed + deltaFrameLengthNanoseconds); 
        } 
    }</span> 
<span style="font-size:16px;">@Override
 public void onUpdate(final long pNanosecondsElapsed) throws InterruptedException
 {
  final long preferredFrameLengthNanoseconds = this.mPreferredFrameLengthNanoseconds;
  final long deltaFrameLengthNanoseconds = preferredFrameLengthNanoseconds – pNanosecondsElapsed;

  if (deltaFrameLengthNanoseconds <= 0)
  {
   super.onUpdate(pNanosecondsElapsed);
  }
  else
  {
   final int sleepTimeMilliseconds = (int) (deltaFrameLengthNanoseconds / NANOSECONDS_PER_MILLISECOND);

   Thread.sleep(sleepTimeMilliseconds);
   super.onUpdate(pNanosecondsElapsed + deltaFrameLengthNanoseconds);
  }
 }</span>LimitedFPSEngine是Engine的子類,在其onUpdate方法中,也會調用父類(Engine)的onUpdate方法,而Engine的onupdate方法就做瞭我們每幀需要的一些運算(Scene的更新是遞歸的噢)

 

這篇內容主要記錄一下AndEngine的運行原理,下一篇將會記錄一下AndEngine與我們的Android遊戲的一些粘合點

不是大神,難免有疏漏,各位如果認為我在哪總結有不妥之處,敬請拍磚!謝謝!

 摘自 oneRain88的專欄

發佈留言