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() 方法,这个方法销毁前面创建的按钮,然后再新建一个按钮。

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
function TestScene:ctor()
self:testUIButton()
end

function TestScene:testUIButton()
self._bi = 0
cc.ui.UIPushButton.new('normal.jpg')
:setButtonSize(240, 60)
:onButtonClicked(function(evt)
self:newUIButton()
end)
:align(display.LEFT_CENTER, display.cx, display.cy)
:addTo(self)
end

function TestScene:newUIButton()
if self._bi > 0 then
local name = 'btn'..self._bi
self[name]:removeSelf(true)
self[name] = nil
end
self._bi = self._bi + 1
name = 'btn'..self._bi
local x = math.random(200)
local y = math.random(200)
self[name] = cc.ui.UIPushButton.new('normal.jpg')
:setButtonSize(240, 60)
:addTo(self)
:align(display.CENTER,
display.cx - x, display.cy - y)
self[name]:onButtonClicked(function()
print('--------------- test '.. name)
end)
end

问题解决

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

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

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

但这是个恶心的解决方案。若想知道真正的解决方案,就要知道这个 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

(全文完)

留言