cocos2d-x中让根节点的opacity影响孩子节点
项目那边提了个需求,希望能够让对根节点设置的opacity影响到后代节点,就好像scale那样,以实现页面元素的fade in/out效果。想想觉得很简单,就一天的活,没想到前后折腾了三天。。。。。。
scale之所以可以影响后代是因为scale的效果会被反映在矩阵里,然后最后node在visit的时候,是会进行transform,把矩阵累积乘进去的。而opacity是某些节点独有的属性,且只对节点本身起作用。为了实现一支树枝上所有的节点都能被fade in/out,有两个思路:思路1是按照scale类似的方法处理;思路2是在fade in/out代码内部对树枝进行遍历,对枝上的每个节点分别按原来的方法进行设置。思路2不需要动基本的结构,只是加多个遍历代码,但是为了更有通用性,决定按照思路1来做。
首先毫无疑问确认的是,每个节点都只存自己相对的opacity值,最后在用到opacity的时候,再把祖先的opacity效应乘起来,获得最终实际opacity值,进行使用。opacity是用一个0-255之间的整数表示的,所谓的乘起来实际是乘以除以255的比值,为了保证一致性,每个节点初始默认的opacity是255,也就是默认不会影响孩子(cocos里面默认的值都是0,然后init的时候才设成255)。
原本只有CCSprite,CCLayerColor等少数几个类有opacity这个属性,现在需要把这个属性移到CCNode里。一移才发现这个属性是RGBAProtocol里面的,于是很猥琐的改成了让CCNode执行RGBAProtocol,然后color相关的部分写成默认函数体。再把少数那几个类的RGBAProtocol去掉(不然就成了钻石继承)。结果这样一来又发生了CCRibbon这个类里面自己的color相关函数命名冲突,只得给他改了个名字(一个是用3B的color,一个是用4B的color,所以给他名字后面加了4B)。总之牵连的改了很多文件。。。。。。
然后在那几个少数类里面,凡是用到opacity的地方都改了,改成用积累乘出来的opacity。跑起来,没动静。原因是在setOpacity的时候会相应的updateColor,从而把更新的opacity作用进去。但是现在fade-in的时候是针对祖先节点进行设置,压根没动节点本身,所以根本没有进行颜色的更新。先暴力的每次draw都update一下color,就能看到效果了。
但是这样未免太浪费,大部分时间这里都是在白白重算。和同事讨论下来,觉得只需要在setOpacity,以及addChild——改变了祖先的时候,需要对被操作节点的所有后代进行一次updateColor。removeChild操作因为会紧跟着addChild操作,就不需要更新了。
结果跑出来大部分时候是对的,但烦人的是有仨bug。一个是在cocosbuilder里面,blending src设成GL_One而不是GL_Alpha的会不对;一个是labelbmfont下面的sprite们不能正常显示,感觉压根没把祖先值算进去,需要强迫的给所有children sprite设成labelbmfont的累积值;一个是menuitem的那些sprite不对,也是一样的没算进去的效果,需要强迫的给他们设成menuitem的累积值。
这样改感觉很不合理也很不优美。。。。。。然后试来试去发现给那些不对的孩子setOpacity(getOpacity())也可以,其实也就是把自己的opacity重新设一下。然后同事提议我,为啥要遍历的updateColor而不是遍历的重设一下自己的opacity呢。然后改了,很神奇的所有问题都解决了,百思不得其解具体原因。只知道setOpacity的时候,如果是OpacityAffectColor的话,会重设一下color到原始值,不然opacity效应会累积上去,其余具体细节就没想明白了。
中间因为太激动还很囧的写了个相互调用的死递归,然后stackoverflow了。重写又写了一个每个节点遍历自己所有的后代,然后那些下层节点就会被指数级反复重复遍历导致速度很慢。其实大家只用遍历自己直系那一层就好了啊~~果然很久不写手生了。