cocos2d-x 3.4 lua 触摸 BUG

2015-04-20 更新:该 bug 已经解决,见本文结尾。

查看所有 quick 移植到 cocos2d-x lua 的文章

移植 quick2.2.3 的项目到 cocos2d-x 3.4 lua 中提到,我已经开始使用 cocos2d-x 3.4 lua 来开发目前公司的两个项目。

在这两个项目中,我将 cocos 的 lua 框架、quick 的 lua 框架和我自己开发的 lua 代码,全部整合到了我的 lua 项目 中。

下面提到的 quick 框架,都是指的我整合的这个 lua 项目。

问题描述

使用 cocos2d-x 3.4 lua + quick 框架进行开发的时候,发现对于销毁再重建的按钮来说,有超过 30% 的几率无法响应触摸事件。

为了方便重现问题,我使用 quick 框架提供的 cc.ui.UIPushButton 做了测试。下面这段代码中 testUIButton() 方法会创建一个按钮,每次点击这个按钮时调用 newUIButton() 方法,这个方法销毁前面创建的按钮,然后再新建一个按钮。

这个新建的按钮,相当大的几率会出现无法响应触摸事件的情况。

 1function TestScene:ctor()
 2	self:testUIButton()
 3end
 4
 5function TestScene:testUIButton()
 6	self._bi = 0
 7	cc.ui.UIPushButton.new('normal.jpg')
 8		:setButtonSize(240, 60)
 9		:onButtonClicked(function(evt)
10			self:newUIButton()
11		end)
12		:align(display.LEFT_CENTER, display.cx, display.cy)
13		:addTo(self)
14end
15
16function TestScene:newUIButton()
17	if self._bi > 0 then
18		local name = 'btn'..self._bi
19		self[name]:removeSelf(true)
20		self[name] = nil
21	end
22	self._bi = self._bi + 1
23	name = 'btn'..self._bi
24	local x = math.random(200)
25	local y = math.random(200)
26	self[name] = cc.ui.UIPushButton.new('normal.jpg')
27		:setButtonSize(240, 60)
28		:addTo(self)
29		:align(display.CENTER, 
30			display.cx - x, display.cy - y)
31	self[name]:onButtonClicked(function()
32		print('--------------- test '.. name)
33	end)
34end

问题解决

用一个很简单的方法可以解决这个问题,修改 testUIButton() 方法,将 newUIButton() 进行延迟调用即可:

sheduler 的实现在 quick 框架中可以找到。

 1function TestScene:testUIButton()
 2	self._bi = 0
 3	cc.ui.UIPushButton.new('normal.jpg')
 4		:setButtonSize(240, 60)
 5		:onButtonClicked(function(evt)
 6			scheduler.performWithDelayGlobal(function()
 7				self:newUIButton()
 8			end, 0.1)
 9		end)
10		:align(display.LEFT_CENTER, display.cx, display.cy)
11		:addTo(self)
12end

但这是个恶心的解决方案。若想知道真正的解决方案,就要知道这个 BUG 产生的原因。

原因分析

当然,上面测试代码的实现方式也是很恶心的。在一个按钮响应事件中创建一个按钮,在真实项目中应该不会这样使用。

不过,虽然这个代码有些极端,但能更方便地重现问题。在我们的实际项目中,只要是销毁之后再重建的按钮,都有可能出现这个 BUG 。而且,不仅仅是按钮,只要是继承 Node 的对象,销毁之后再重建,都会出现这个问题。

咨询了 cocos2d-x lua 开发组的 阳光七月 ,才知道原来这还是个历史遗留问题:

阳光七月 16:15:52 销毁的时候那个node标记为已经移除了,如果再创建时指针一样的话会判断为旧的,不会被添加触摸处理 Jacky 16:16:37 但是我已经换了变量名称,这样也会导致创建出相同的指针么? 阳光七月 16:17:18 在C++层创建与变量名称没有相关性啊 阳光七月 16:17:38 内存释放后又重新分配,当然有可能使用同一块内存 Jacky 16:17:54 是的,而且在同一段代码中,这种情况比较容易发生? 阳光七月 16:18:49 还是有不小机率的 阳光七月 16:21:13 估计不改Node是不行了 Jacky 16:21:41 Node 在 quick 里面不是改过么? 阳光七月 16:21:57 之前是想尽量不改-x的代码,在外面挂一层触摸模块 阳光七月 16:22:04 3.3之后没有改了 阳光七月 16:22:47 以现在为基础的话,改动应该也不大 阳光七月 16:23:01 主要是在Node释放时要处理一下 Jacky 16:23:25 quick2确实是没有这个问题的。 Jacky 16:23:42 如果在 C++层面这么写,也会有这个问题么? 阳光七月 16:24:09 肯定没有的,因为之前触摸模块是Node的一部分,不可能有问题 阳光七月 16:24:15 3.3之后才分开的 阳光七月 16:27:29 现在3.3之后是在lua层面收到onCleanup消息再回到C++层面释放触摸模块的 阳光七月 17:16:46 现在就是绕了个弯,在lua端收到onCleanup消息时再调用removeTouchEvent,这样的联系实在是不牢靠,所以问题也多 阳光七月 17:52:40 嗯,或者可以改一下引用机制,在底层延时销毁,上层不需要考虑这个细节。不过这样也要改Node,我后面再评估一下

看来,只需要坐等 cocos2d-x lua 开发组来修改就行啦。

2015-04-24 更新

这个 bug 已经被 阳光七月 解决,合并这个 PR 即可:

https://github.com/dualface/v3quick/pull/422

(全文完)