注册 登录  
 加关注
   显示下一条  |  关闭
温馨提示!由于新浪微博认证机制调整,您的新浪微博帐号绑定已过期,请重新绑定!立即重新绑定新浪微博》  |  关闭

Silence的博客

大师只有一个

 
 
 

日志

 
 

转:Ogre设计模式分析-观察者模式  

2010-09-12 13:24:31|  分类: 技术 |  标签: |举报 |字号 订阅

  下载LOFTER 我的照片书  |

观察者模式是游戏开发中十分常用的模式,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管理的

view plaincopy to clipboardprint?
// 用boost库的智能指针管理的场景对象而已  
class Object;  
typedef boost::shared_ptr<Object> ObjectPtr;  
typedef boost::weak_ptr<Object> ObjectWeakPtr;  
 
// 场景监听基类  
class SceneListener  
{  
public:  
    SceneListener(void) {}  
    virtual ~SceneListener(void) {}  
 
    virtual void onObjectPropertyChanged(const ObjectPtr& object, const String& name) {};  
};  
 
// 场景监听管理器  
class SceneListenerManager  
{  
protected:  
    typedef std::list<SceneListener*> Listeners;  
    Listeners mListeners;  
 
public:  
    SceneListenerManager(void);  
    ~SceneListenerManager(void);  
 
    void addSceneListener(SceneListener* listener)  
    {  
        mListeners.push_back(listener);  
    }  
 
    void removeSceneListener(SceneListener* listener)  
    {  
        mListeners.remove(listener);  
    }  
 
    // SceneListener* exclude 表示不用通知自己,因为自己也可能是一个SceneListener  
    void _fireObjectPropertyChanged(const ObjectPtr& object, const String& name, SceneListener* exclude = 0)  
    {  
        for (Listeners::const_iterator it = mListeners.begin(); it != mListeners.end(); ++ it)  
        {  
            SceneListener* listener = *it;  
            if (listener != exclude)  
            {  
                listener->onObjectPropertyChanged(object, name);  
            }     
        }  
    }  
}; 
// 用boost库的智能指针管理的场景对象而已
class Object;
typedef boost::shared_ptr<Object> ObjectPtr;
typedef boost::weak_ptr<Object> ObjectWeakPtr;

// 场景监听基类
class SceneListener
{
public:
 SceneListener(void) {}
 virtual ~SceneListener(void) {}

 virtual void onObjectPropertyChanged(const ObjectPtr& object, const String& name) {};
};

// 场景监听管理器
class SceneListenerManager
{
protected:
 typedef std::list<SceneListener*> Listeners;
 Listeners mListeners;

public:
 SceneListenerManager(void);
 ~SceneListenerManager(void);

 void addSceneListener(SceneListener* listener)
 {
  mListeners.push_back(listener);
 }

 void removeSceneListener(SceneListener* listener)
 {
  mListeners.remove(listener);
 }

 // SceneListener* exclude 表示不用通知自己,因为自己也可能是一个SceneListener
 void _fireObjectPropertyChanged(const ObjectPtr& object, const String& name, SceneListener* exclude = 0)
 {
  for (Listeners::const_iterator it = mListeners.begin(); it != mListeners.end(); ++ it)
  {
   SceneListener* listener = *it;
   if (listener != exclude)
   {
    listener->onObjectPropertyChanged(object, name);
   } 
  }
 }
};
 

那么只要我们在更改对象属性的地方调用此函数通知所有监听者

view plaincopy to clipboardprint?
object->setProperty("position", position);  
getSceneListenerManager()->_fireObjectPropertyChanged(object, "position", this); 
object->setProperty("position", position);
getSceneListenerManager()->_fireObjectPropertyChanged(object, "position", this);

所有监听者都会被通知,那么只要我们注册成一个监听者就可以收到这个消息

假设显示面板要收到这个消息

view plaincopy to clipboardprint?
// 编辑器的属性面板  
class PropertyPlane : public SceneListener  
{  
public:  
 
    // 初始化的时候加入监听  
    PropertyPlane(void)  
    {  
        getSceneListenerManager()->_addSceneListener(this);  
    }  
 
    // 重写  
    void onObjectPropertyChanged(const ObjectPtr& object, const String& name)  
    {  
        // 显示这个对象的新的属性,对象指针和属性名字已经传过来了  
    }  
}; 

 

本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/qq18052887/archive/2010/03/31/5438864.aspx

  评论这张
 
阅读(1257)| 评论(0)
推荐 转载

历史上的今天

评论

<#--最新日志,群博日志--> <#--推荐日志--> <#--引用记录--> <#--博主推荐--> <#--随机阅读--> <#--首页推荐--> <#--历史上的今天--> <#--被推荐日志--> <#--上一篇,下一篇--> <#-- 热度 --> <#-- 网易新闻广告 --> <#--右边模块结构--> <#--评论模块结构--> <#--引用模块结构--> <#--博主发起的投票-->
 
 
 
 
 
 
 
 
 
 
 
 
 
 

页脚

网易公司版权所有 ©1997-2017