Ogre設計模式分析-觀察者模式

觀察者模式是遊戲開發中十分常用的模式,Ogre也大量運用了此模式來監聽,比如FrameListener.ResourceListener

這種方式比常見的回調函數更好用,因爲觀察者模式是基於接口的,任何類只要繼承這個接口,註冊後就可以監聽我們需要觀察的對象。不想監聽,取消註冊就行了,

 

具體實現原理,我們以爲FrameListener爲例子,然後再舉一反三在自己的遊戲中使用它,比如場景編輯器,我拖動了一個entity,改變了他的位置,那麼顯示對象屬性的控件應該更新這個對象的位置,我們就可以設計一個監聽類來監聽任何對對象的操作,有變化就通知給顯示面板。

 

Ogre的幀監聽怎麼實現的呢

 

1,先設計了一個基類,作爲接口,啥東西都沒,要自己override

    class _OgreExport FrameListener
    {
    public:

        virtual bool frameStarted(const FrameEvent& evt) { return true; }
        virtual bool frameEnded(const FrameEvent& evt) { return true; }

        virtual ~FrameListener() {}
    };

 

2,然後在renderOneFrame(void)裏面就會每幀調用這個基類的函數

    bool Root::renderOneFrame(void)
    {
        if(!_fireFrameStarted())   // 調用幀開始
            return false;

        _updateAllRenderTargets();

        return _fireFrameEnded(); // 調用幀結束
    }

 

3, 看看具體實現

    bool Root::_fireFrameStarted(FrameEvent& evt)
    {
        // Increment frame number
        ++mCurrentFrame;

        // Remove all marked listeners
        std::set<FrameListener*>::iterator i;
        for (i = mRemovedFrameListeners.begin();
            i != mRemovedFrameListeners.end(); i++)
        {
            mFrameListeners.erase(*i);
        }
        mRemovedFrameListeners.clear();

        // Tell all listeners

       // 通知所有註冊了的幀監聽類
        for (i= mFrameListeners.begin(); i != mFrameListeners.end(); ++i)
        {
            if (!(*i)->frameStarted(evt))
                return false;
        }

        return true;

    }

看了源碼,就明白,root裏面保存有一個容器mFrameListeners,存儲了所有註冊了的幀監聽

只要加入到這個容器,就可以被遍歷.然後通過C++的動態多態,實現frameStarted的調用

 

4, 怎麼加入這個容器呢?就是addFrameListener函數

    void Root::addFrameListener(FrameListener* newListener)
    {
  // Check if the specified listener is scheduled for removal
  std::set<FrameListener *>::iterator i = mRemovedFrameListeners.find(newListener);

  // If yes, cancel the removal. Otherwise add it to other listeners.
  if (i != mRemovedFrameListeners.end())
   mRemovedFrameListeners.erase(*i);
  else
   mFrameListeners.insert(newListener); // Insert, unique only (set)
    }

 

5, 所有要使用一個幀監聽,實例一個,再addFrameListener就OK,否則void Root::removeFrameListener(FrameListener* oldListener)

,而且我們可以創建N個幀監加入容器,就可以在任何類裏面調用到frameStarted函數.

 

--------------------------------------------------------------------------------------------------------------------------

其作用,其實和用函數指針實現的回調函數差不多,但是明顯比常見的回調的方式更好

 

由於是基於接口的,那麼擴展性非常好,比如Ogre的代碼是封裝成一個dll的,如果用回調,可能要寫明回調到那個函數,可視Ogre並不知道用戶寫的函數,所以Ogre用接口,只操作接口,而用戶繼承這個接口,用C++動態多態,就可以實現對應函數的調用.

這種方式很適合庫和庫之間,比如我Ogre的渲染模塊用了一個庫,客戶端可以用我,場景編輯器也可以用我,Ogre的渲染模塊就應該設計很多接口

 

比如場景編輯器,我拖動了一個entity,改變了他的位置,那麼顯示對象屬性的控件應該更新這個對象的位置,我們就可以設計一個監聽類來監聽任何對對象的操作,有變化就通知給顯示面板。

 

依葫蘆畫瓢

1,先設計一個SceneListener基類

2, 設計一個管理者SceneListenerManager來管理這些類,Ogre裏面就是用root管理的

 

那麼只要我們在更改對象屬性的地方調用此函數通知所有監聽者

所有監聽者都會被通知,那麼只要我們註冊成一個監聽者就可以收到這個消息

 

假設顯示面板要收到這個消息

發佈了67 篇原創文章 · 獲贊 68 · 訪問量 35萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章