游戏设计模式 - 观察者模式
给出一个例子:
小球砸在地面,达成一个成就“蛋疼”。砸在地面,听起来是需要物理引擎参与吧,但我们总不能在物理引擎计算各种力学公式的时候强行插入一个unlockEggPain()方法吧,观察者模式就出来有话说了。
def undateEntity(entity): entity.accelerate(GRAVITY) entity.update() if entity.IsOnSurface(): notify(entity, EVENT_ON_FACE)
这里,我们实现了某个东西砸在地上了,感兴趣的人自行关注。
成就系统就可以注册自己进去,收到信息后检查是否和自己相关,有则进行运算,否则抛弃。这样成就系统和物理系统没进行耦合,任何一方的存亡也没另一边的关系。
如何实现:
定义观察者
class ObSever: def onNotify(entity, event): pass
继承obsever即可成为观察者,就是那么简单。
class Achievents(Obsever): def onNotify(entity, event): if event == EVENT_ON_FACE: self.Unlock(ACHIEVENT_EGG_PAIN) elif xxx: pass
此外,还得定义被观察的对象,称为客体
class Subject: def __init__(self): self.m_ObList = [] def addObsever(obObj): self.m_ObList.append(obObj) def removeObsever(obObj): pass def notify(entity, event): for obObj in self.m_ObList: obObj.onNotify(entity, event)
通过注册本身进去,就可关注此类事件的发生。
使用列表存储观察者可避免观察者发生竞争,后续还有各种存储的方式,选择符合情况的即可。
物理引擎在合适的时机出发onNotify就可以通知观察者。
注意:
1.观察者们在触发事件后会等待通知方法返回后才会执行其他工作,如果此方法内含有需要耗时很大的内容,容易导致ui卡死,所以通知方法应该尽快返回控制权,如需要进行此类工作,可开线程和工作队列完成
2.如果观察者要获取客体的锁,容易导致游戏死锁,最好使用事件队列来做异步通信
3.客体和观察者进行销毁时,都要向对方发送通知,例如c++如果观察者被注销却没告知客体,客体会向一片莫名的内存发起通告,就原地爆炸了。客体销毁也要通知下观察者。
4.注册观察者最好是注册方法,而不是注册观察者实例本身,最好是使用弱引用,以免引发不必要的回收问题。拥有闭包能力的语言非常适合做此类工作,例如py,lua