cocos2d-x下开发鞭炮效果
由于现在项目需要在春节进行更新,主要方面包括烟花效果以及鞭炮效果等
烟花效果主要由粒子完成,如果需要更好的效果,可以使用序列帧的方式,每一张图片的尺寸可以一定程度的缩小,保证内存的消耗不是过大。
当然今天主要讨论的问题不是烟花效果,是如果做一个比较效果不错的鞭炮。如下图所示:
这里主要谈两部分的内容,第一部分如何开发较简单的直鞭炮;第二部分我们将讨论下如果做其他形状鞭炮方法。
简单鞭炮制作
这里主要用到的相关技术: DB(DragonBones)、ClippingNode、粒子;主要使用Lua作为脚本语言来进行开发。
首先我们需要确定鞭炮有多少个个数,个数不一样那么最后鞭炮的长度也是不同的。
local bianpao_type = { 25, 18, 13 }
上图我们确定了长度分别为25、18、13几种长度的鞭炮
最后得到的图如上图,我们这里数量不是真实的数量,我们生成的时候数量是4x个
local function genWhipItem(p_node, index, num) local pos = { cc.p(rand(90, 93), num * 100), cc.p(-rand(90, 93), num * 100), cc.p(rand(90, 93), num * 100 + 25), cc.p(-rand(90, 93), num * 100 + 25), } local rotation = { rand(6, 8), -rand(6, 8), rand(6, 8), -rand(6, 8) } local order = { 3, 3, 1, 1 } for i = 1, 4 do local item_pao = create() p_node:addChild(item_pao.node, order[i]) item_pao.node:setPosition(pos[i]) item_pao.node:setRotation(rotation[i]) pianpao_arr[index][#pianpao_arr[index] + 1] = item_pao.node end end
上述函数是生成每一组鞭炮的函数,设定了每个鞭炮的位置有一点随机,Order的设定为了燃放的时候先放上层,整体的燃放方式就是先上后下,先左后右。
然后说下引线的制作方法
首先有一条鞭炮捏的图片,然后将使用ClippingNode就可以完成一个遮罩动画,感觉引线越来越短,当然这里ClippingNode只限于规则的图形,下一块我们讨论非规则引线的实现方案。
ClippingNode这里简单说下,具体可以参考上面的链接
一个ClippingNode对象可以设置底版和模板(Stencil),这里弄清楚两者的区别就可以非常容易的使用了。
模板就是遮罩层,不会对其绘制,将通过它对底版进行裁剪。
底版就是显示层,ClippingNode* node = new ClippingNode();...(一定设置了模板),node->addChild(child_node); 这里addChild的所有的Node就是底版。
引线这里可以显示了越来越短了,但是似乎还是少了点什么,对了火星子,这里我们使用一个粒子动画,来模拟就可以。有一个细节就是在执行move动画的时候,一定运动同步,最好开始定一个速度,然后引线缩短和粒子移动是一致的。
引线制作完成了,当到达了鞭炮位置,鞭炮需要燃放,鞭炮本身是一个骨骼动画,播放爆炸的动画,然后释放掉就可以了。一定要小心节奏的控制,否则会感觉很假。
好啦,很简单,下面看看非规则图形怎么办,比如下图
这个图我们想沿着“S"图形的一端沿着轨迹走完,走过的地方被清除。首先我们发现使用ClippingNode的遮罩怎么都无法完成这个工作。
我们需要变通,更换思路,可以参考RenderTexture绘制的方法,然后设置一个橡皮去擦除,橡皮需要依靠路径斜率来变化角度。
RenderTexture的实现方法如下:
创建
ear_can_use = cc.Sprite:create("ear.png") ear_can_use:setRotation(-60) local array = { cc.p(893, 580), cc.p(1040, 580), cc.p(1120, 675), cc.p(1002, 795), cc.p(928, 910), cc.p(1000, 950), cc.p(1130, 950) } local action = cc.CardinalSplineBy:create(3, array, 0) ear_can_use:runAction(action) drawNode1 = cc.Sprite:create("ear.png") drawNode1:runAction(cc.Sequence:create({ cc.RotateBy:create(0.8, -90), cc.RotateBy:create(0.4, -75), cc.RotateBy:create(0.16, -40), cc.RotateBy:create(0.25, 40), cc.RotateBy:create(0.5, 70), cc.RotateBy:create(0.8, 90), })) ear_can_use:runAction(cc.Sequence:create({ cc.RotateBy:create(0.1, 0), cc.RotateBy:create(10, -60), })) ear_can_use:setVisible(false) drawNode1:setRotation(-60) drawNode1:setPosition(200, 1536 / 2) drawNode1:setVisible(false) pRTex = cc.RenderTexture:create(2048, 1536) pRTex:setPosition(cc.p(1024, 1536 / 2)) current_page_objects['Page']:addChild(pRTex, 10) local image = cc.Sprite:create("s_mask_image.png") image:setPosition(cc.p(1024, 1536 / 2)) pRTex:begin() image:visit() pRTex:endToLua()
update中
local posi = cc.p(ear_can_use:getPositionX(), ear_can_use:getPositionY()) drawNode1:setPosition(cc.p(posi.x, posi.y )); drawNode1:setBlendFunc(cc.blendFunc(gl.ZERO , gl.ONE_MINUS_SRC_COLOR)) pRTex:begin() drawNode1:setVisible(true) drawNode1:visit() drawNode1:setVisible(false) pRTex:endToLua()
这里相当于在每一帧设置了橡皮的位置与游动图片一致,那么就会按照路径来进行删除了。
角度的设置根据图形的不同而设定不同,一般可以放到update中,一段时间执行一次计算,其实和微积分的思路差不多。
欢迎大家多来提问!