在(一)中曾经简要的分析了Ogre帧监听的方法。这主要是通过_fireFrameStarted()和_fireFrameEnded()两个方法中,生成一个事件,然后分发给每一个注册上去的监听器。
这篇日志着重分析观察者模式在Ogre中的应用,和Ogre引擎中监听器的使用原理。
4.1监听器模式:首先看看“四人帮”写的设计模式上,对监听器模式的描述。
定义:为对象建立一个一对多的关系,这样当一个对象变化时,所有的依赖对象都能被通知到并且自动的更新。他的作用主要有(1)抽象观测者和对象之间的耦合(2)提供广播机制(3)非预期的更新。在什么时候使用观察者模式呢:(1)当抽象分为两层,一层依赖另一层的时候,(2)需要支持广播的时候。(3)当一个对象需要在不知道其他对象的情况下,需要通知那些对象(状态发生改变)的情况下。
Ogre引擎中,或者是任何一款游戏引擎中,为什么需要使用观察者模式呢。游戏主循环往简单了说,无非是两个操作:响应用户操作(处理下一帧的状态:人物是走向,NPC(AI)的动向,NPC的反应,网络游戏中当然还要考虑网络输入流),输出响应结果给用户(渲染一帧,输出音乐,震动等等)
在Ogre中,帧监听器不一定只有一个,Ogre使用两个set(deque?)维护监听器的队列,每次渲染一帧之前,首先会剔除失效的监听器,然后对每个有效的监听器,发送时间消息(主要是上一帧和上一个时间,到这一帧,这一个事件的时间间隔。)也就是动态维护和使用监听器队列。以帧监听器为例,他实际上只是一个接口,通过类继承和实现基类的接口,来实现一个监听器的具体操作内容。比如,响应用户的前后左右移动的操作,改变模型的位置和姿势状态等等。因此,这就需要向每个注册上的有效帧监听器(注册上去的是基类监听器的指针)发送时间,并调用其方法实体。完成某些特定的功能(使用多态实现了~。)
源码剖析:
【1】增加一个Framelistener:
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)
}
这个方法中,首先检查监听器是不是在删除队列里,如果是,那么就取消其删除。(说明其已经存在,不用再添加了),否则,说明他确实是一个新的监听器,那么就直接在监听器set里添加这个新的监听器。
【2】 删除一个监听器,只需要将其加入到删除队列里即可。
void Root::removeFrameListener(FrameListener* oldListener)
{
// Remove, 1 only (set)
mRemovedFrameListeners.insert(oldListener);
}
很简单的操作,但是监听器实际是在什么时候被删除的呢?(一)号日志里已经提到过了:在bool Root::_fireFrameEnded(FrameEvent& evt)这个方法中。
std::set<FrameListener*>::iterator i;
for (i = mRemovedFrameListeners.begin();
i != mRemovedFrameListeners.end(); i++)
{
mFrameListeners.erase(*i);
}
mRemovedFrameListeners.clear();
这些代码负责删除监听器,不过令人费解的是监听器在什么时候被Delete掉没有发现。难道监听器不是放到heap里的吗……根据Ogre官方提供的几个实例,监听器类的使用是很简单的,无非就是2个要点:
【1】继承监听器类,并实现其接口
【2】将监听器添加到root中的mFrameListeners(set型的容器),root会在_fireFrameStarted()和_fireFrameEnded()方法中向这个容器中的所有监听器发送事件消息,并通知每个监听器执行其特定方法。而_fireFrameStarted()和_fireFrameEnded()这两个方法会在每次的渲染操作之前和之后调用。