觀察者模式是遊戲開發中十分常用的模式,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管理的
那麼只要我們在更改對象屬性的地方調用此函數通知所有監聽者
所有監聽者都會被通知,那麼只要我們註冊成一個監聽者就可以收到這個消息
假設顯示面板要收到這個消息