Dorothy新版消息机制

  Dorothy的Lua接口中的消息处理,从最初的函数接口:

	-- 注册
	layer:registerTouchHandler(function(eventType,touch)
		if eventType == CCTouch.Began then
			return true
		elseif eventType == CCTouch.Moved then
		elseif eventType == CCTouch.Ended then
		elseif eventType == CCTouch.Cancelled then
		end
	end)
	-- 取消注册
	layer:unregisterTouchHandler()

  后来简化为仿Lua语言的函数回调形式:

  -- 设置事件回调
	node.nodeHandler = function(eventType,touch)
		if eventType == CCTouch.Began then
			return true
		elseif eventType == CCTouch.Moved then
		elseif eventType == CCTouch.Ended then
		elseif eventType == CCTouch.Cancelled then
		end
	end
	-- 清除事件回调
	node.nodeHandler = nil

  开发过程中发现因为不停有新的事件接口需要导出到Lua中,对于cocos2d 2.0的事件导出相对比较简单,只需要完成引用一个Lua中的函数,并在事件触发时调用这个函数就行。但是Dorothy中的事件就比较麻烦,因为C++中提供的接口使用的是一种模拟C#委托的机制,支持multi cast,比如物理感应器的回调的用法:

	// 添加委托
	sensor->bodyEnter += [](oSensor* sensor, oBody* body){ };
	sensor->bodyEnter += std::make_pair(this, &MyScene::onBodySensed);

	// 删除单个委托
	sensor->bodyEnter -= std::make_pair(this, &MyScene::onBodySensed);

	// 清除所有委托
	sensor->bodyEnter.Clear();

  把这个事件接口绑定到Lua,开始的方法是每个事件使用三个函数,addHandler,removeHandler和clearHandler,随着要导出事件接口的增加,Lua的绑定代码重复得越来越多。而且cocos2d 2.0原版的只能设置一个回调函数的事件的接口其实挺不好用,会导致用户把大量的逻辑代码都放在这一个函数中来获取触发的事件,不方便组织代码。
  所以在Dorothy引进了全局的消息系统以后。在消息系统的启发下现在又增加了基于node的事件系统,想一次解决事件导出到Lua时的multi cast功能,减少反复添加的事件接口的绑定代码,并同时引入一套可以由用户扩展的局部作用域的事件机制。所以现在有了Dorothy的signal slot系统。示例代码如下:

  -- 添加对系统内置消息的监听
	node:slot("Entered"):add(function() end)
	layer:slot("TouchBegan"):add(function(touch) return true end)
	layer:slot("TouchMoved"):add(function(touch) end)
	sensor:slot("BodyEnter"):add(function(body) end)
	model:slot("AnimationEnd"):add(function(name) end)

	-- 删除对消息的监听
	local f = function() end
	node:slot("Entered"):add(f)
	node:slot("Entered"):remove(f)

	-- 清空对消息的监听
	node:slot("Entered"):clear()

  还有语法糖的写法:

	-- 添加对系统内置消息的监听
	node:slot("Entered", function() end)
	layer:slot("TouchBegan", function(touch) return true end)

	-- 清空对消息的监听
	node:slot("Entered", nil)

	-- 委托的feel
	sensor.bodyEnter = sensor:slot("BodyEnter")

	sensor.bodyEnter:add(function(body)
		print("entered body: ", body.tag)
	end)

  总之这个接口是以怎么写着爽就怎么设计的啦。此外还可以用于自定义的消息,比如:

	-- 定义一个MyEvent
	-- 监听消息
	node:slot("MyEvent",function(num,str)
		print("Number is",num,"String is",str)
	end)

	-- 发送消息
	node:emit("MyEvent",998,"PIG") -- multi arguments support

更新的全局消息系统接口

  更新前的全局消息接口很不好用,使用时像这样:

  -- 监听GlobalEventA和GlobalEventB两种消息
	local listenerA = oListener("GlobalEventA",function(args)
		print("handle A",unpack(args))
	end)
	listenerA.enabled = false

	local listenerB = oListener("GlobalEventB",function(args)
		print("handle B",args.text)
	end)

	-- 将监听器挂在C++引用系统中,否则会被销毁
	scene.data = CCDictionary()
	scene.data.listenerA = listenerA
	scene.data.listenerB = listenerB

	scene.data.listenerA.enabled = true

	-- 发送消息
	oEvent:send("GlobalEventA",{998,"PIG"})
	oEvent:send("GlobalEventA",{text="dog"})

  监听器的生命周期需要手动管理,并带来巨大的内存泄露隐患,怎么内存泄露就不说了,作者已多次中招。所以更新版把全局消息系统改成这样:

	-- 监听GlobalEventA和GlobalEventB两种消息
	node.slotA = node:gslot("GlobalEventA",function(args)
		print("handle A",unpack(args))
	end)
	node.slotA.enabled = false

	node.slotB = node:gslot("GlobalEventB",function(args)
		print("handle B",args.text)
	end)

	node.slotA.enabled = true

	-- 发送消息
	emit("GlobalEventA",{998,"PIG"}) -- 注意全局消息只支持传递单个参数所以这里用table来包裹多参数,和局部消息系统略有差别
	emit("GlobalEventA",{text="dog"})

  创建的监听器的生命周期交由创建它的node管理,当node触发cleanup事件时监听器会自动失效。同一个事件可以创建多个监听器:

	-- 创建多个监听器
	node:gslot("GlobalEvent",f)
	node:gslot("GlobalEvent",f1)
	node:gslot("GlobalEvent",f2)
	
	local slots = node:gslot("GlobalEvent") -- 获取所有的监听器
	for _,slot in ipairs(slots) do
		slot.enabled = false
	end
	
	-- 删除某一个监听器
	node:gslot(slot, nil)

	-- 清除某事件下的全部监听器
	node:gslot("GlobalEvent", nil)
标题目录