【cocos2dx】游戏逻辑+cocos2dx+编译器 导致的TouchEvent异常

简单的问题描述:

使用的是quick cocos2dx 3.5,在特定的游戏UI逻辑下在配合cocos2dx的eventListener管理机制和操作系统的堆内存分配机制时便会导致TouchEvent异常。

特定的游戏UI逻辑比如:有接收Touch事件的某节点Node A,在游戏逻辑中在Node A的OnTouch中或者其子节点的OnTouch需要remove掉该节点,在remove之后立马create一个同类型(都为cc.node 或 都为cc.laber等)的节点Node B,对Node B同样也设置可Touch,然而Node B始终不能接收到Touch事件。


问题排查:

首先当然是排查逻辑代码部分…跟了Touch的分发流程,都没察觉任何问题,未果。

然后怀疑是Node A中有什么东西缓存了,在Lua层打印了Node A和Node B(只打印了Lua的映射地址)验证了Lua对象没有被缓存。然后做了些逻辑上的改动尝试,发现将Node A的remove放到Node B的create之后,Node B的Touch便正常;或者提前将Node A remove,即让Node A的remove和Node B的create不在同一帧内执行,Node B的Touch也正常。然后跟踪Node的remove流程,发现在Node析构的时候会去删除evenListener,删除Listener时会判断如果当前正在执行和还未执行的Listener会延后到下一帧移除,当前已执行的Listener直接移除,此流程也没察觉异常应该如此。

然后采用二分法原理,在Node A  remove之前先将Node A retain住然后在removefromparent 这样Node A只是从场景里移除了,其对象还在,相比正常remove只是少了析构,发现Node B的Touch正常。cocos2dx中evenListener采用Node对象地址管理,由此联想到cocos2dx 3.X版本做了内存优化,复用了Node A的对象,于是在cocos2dx C++中做了个测试,发现Node A和Node B C++中分配到的内存地址果然相同,嘴里边骂cocos2dx坑货边想内存分配是操作系统做的,会不会操作系统就给出的就是同一块内存区域,于是在C++下做测试,果然如此,不明白,在同事点拨下恍然大悟,操作系统必须把已释放的内存重新分配(要不然就内存泄露了),此时是有可能会出Node A与Node B地址相同的情况的。弄明白了这些这个问题就明了了,Node A与Node B得到的是同一快内存区域,Node B注册evenListener时其实是更新了Node A的evenListener,然后在下一帧中Node A移除evenListener时实际上是移除了Node B的,Node B没有了evenListener所以无法接收到Touch。


解决方法:

目前采用的方法是将remove evenListener的判断提前至node 的remove中,即发现此Node remove后引用计数为0了并且其Listener当前正在执行或还未执行便将node retain住,到下一帧时再release此node,这样就避开了Node B取到Node A的内存块,此问题需三个条件同时满足才能触发,此解决方案破坏了内存分配这一条件即解决了此问题。


方案分析:

这种解决方案会加重内存碎片无疑,还可以尝试调整evenListener的管理机制(采用非内存地址的Tag或是添加标记类似时间戳来区分同一地址是否为同一对象),或者仍使用那块内存,但是在给Node B设置Listener时(更新Node A的Listener)判断此Listener是否需要移除,不需要时将其重DeleteList中移除即可。



刚毕业那会儿老大就建议我们多写写博客啥的,一直无动于衷,现在开始写时才发现完全不知道如何下笔,也根本说不清问题……


你可能感兴趣的:(游戏开发)