0.背景
在游戏的UI中,往往会出现这样的情况:
在某个战斗副本中获得了某个道具A,那么当进入主界面的时候,你会看到你的背包UI上有个小红点(意思是有新道具),点击进入背包后,发现新增了道具A,显示个数为1,并且在下个界面中有个使用的按钮由灰色不可使用变成橙色的可使用状态
图1. 事件触发说明图
其中这里是由道具获得这个事件,触发了上述的三个行为。如果使用显示调用行为,会使得代码难扩展,易出错,逻辑混乱等问题,如果使用Event回调机制,就会变得十分方便。
其实Event回调机制就是观察者模式,如下图:
图2. 观察者模式
在C#中存在(delegate & event)的语义来实现Event回调机制:具体使用如下:
public delegate void NewToolGotEvent();public class ToolBag{ event NewToolGotEvent newToolGotHandler; void Start() { newToolGotHandler += renderRedPoint; newToolGotHandler += renderNewTool; newToolGotHandler += renderAvaliableUseBtn; } void renderRedPoint() { //TODO } void renderNewTool() { //TODO } void renderAvaliableUseBtn() { //TODO } void EventHappened() { newToolGotHandler(); // usage, fill args if necessary }}
如果在Python,可以在注册事件的回调时,带入一个参数callback,在注册函数实体内,存在一个list将callback添加进去,形如:
def register_callback(self, cb): self.callbacks.append(cb)
但是这样是一个最为普遍的做法,既然是Python,这里我们有更Pythonic的做法,而且相比于上述的观察者模式,它的做法更加简洁,使用更加方便,接下来我们来解析一下Python实现Event callback的步骤。
1. UML类图
上述案例中,是针对游戏客户端UI的案例。所以我们呈现出的UML图也是与UI相关。如图3所示,它显示了Python中实现Event回调的机制。
图3. UML关系图
如上图所示,此机制主要由三个类及他们的实例(instance)组成:UIBase, UIScene, UIDataEvent。
1 . UIBase: 所有UIScene的基类,其实例有scene_id变量,包含两个必要的方法, __init__ 是初始化方法,init_data_listeners方法是将实例中的某些方法, 例如ui_updata_func中包含的UIDataEvent实例(所有的UIDataEvent实例都是单例)遍历,并把ui_update_func注册在每一个UIDataEvent实例中。
2 . UIScene: 场景类,管理某个场景的UI渲染。在其实例中,存在某些方法,例如ui_update_func需要在某些UIDataEvent实例触发时候,也被同时触发调用。ui_update_func在Python中一个bound method object, 它会拥有一个特殊的属性events,即所有需要触发此方法的UIDataEvent实例集合。这个通过装饰器(decorator)来实现,即图中的:
新闻热点
疑难解答