转载自ufolr的博客 原文链接:http://blog.csdn.net/ufolr/article/details/7624851ios
最近项目中须要一个落叶的效果,原本想用粒子特效来实现,可是几经调试,虽然调出了落叶的效果,可是并非十分理想,最大的不足就是落叶是平面的,没有立体感,虽然把落叶作小以后倒是立体感的感受会有所缓解,但总不能把树叶无限的缩小吧,并且立体感的缺失在粒子特效中确实是一个始终存在的问题。做为一个最求品质的程序猿,最终仍是决定本身设精灵动做来实现。函数
在分析了粒子特效实现的原理并在国内外论坛上爬了半天,最后边实验边修改,终于完成了一个可行的仿真感较强的立体的落叶效果,如今就拿出来跟你们分享一下。测试
原理->树叶飘落动做分析:this
树叶下落过程分解为:下落+摆动+叶片自传。spa
也就是只要将这三个动做实现,并同时执行就能够实现树叶飘落的效果。.net
下面就拿出代码具体解析实现过程:3d
老规矩,先上.h的内容,.h就很少解释了:调试
#ifndef __LEAF_H__ #define __LEAF_H__ #include "cocos2d.h" USING_NS_CC; class Leaf : public cocos2d::CCLayer { public: virtual bool init(); void resetLeafPos(CCNode* sender);//叶片位置重置函数 void playLeafAnim(CCSprite *spriteLeaf);//下落过程实现函数 LAYER_NODE_FUNC(Leaf); }; #endif // __LEAF_H__
接下来是具体的实现,为了咱们能不断的产生天然、随和的落叶,咱们分三步来完成:blog
1:第一次初始化;2:落叶动做的实现;3:下落动做完成从新设定落叶开始。rem
上代码,先看看用到的头文件:
#include <iostream> #include <ctime> #include <cstdlib> #include"Leaf.h" using namespace std; enum {TAG_LEAF1 = 101, TAG_LEAF2};
初始化树叶精灵的设定:
<span style="font-size: 12px;">bool Leaf::init() { CCSprite *spriteLeaf1 = CCSprite::spriteWithFile("img_yezi_1.png"); spriteLeaf1->setRotation(30);//旋转角度 spriteLeaf1->setAnchorPoint(ccp(0.5, 3));//设置精灵锚点 spriteLeaf1->setPosition(ccp(450, 500));//叶子1第一次初始位置 spriteLeaf1->setScale(0.5);//设置叶片大小 this->addChild(spriteLeaf1,100,TAG_LEAF1); this->playLeafAnim(spriteLeaf1);//调用play函数播实现叶动做 CCSprite *spriteLeaf2 = CCSprite::spriteWithFile("img_yezi_2.png"); spriteLeaf2->setRotation(50); spriteLeaf2->setAnchorPoint(ccp(0.5, 3)); spriteLeaf2->setPosition(ccp(200, 540)); spriteLeaf2->setScale(0.5); this->addChild(spriteLeaf2,101,TAG_LEAF2); this->playLeafAnim(spriteLeaf2); return true; }</span>
将精灵的锚点设定在其高度的3倍的位置,加上旋转动做后,叶片会产生单摆的动做效果。再加上下落的动做,就会有树叶飘落的感受了。
<span style="font-size: 12px;">//叶子飘落动做 void Leaf::playLeafAnim(CCSprite *spriteLeaf) { int iTag = spriteLeaf->getTag(); CCLog("playtag%d", iTag); ccTime time, roTime; float fAngle1, fAngle2; if (iTag == TAG_LEAF1) { CCLog("tag1"); time = 10;//叶子下落的时间 roTime = 2.5;//叶子单向摆动一次时间 fAngle1 = -80;//叶子逆时针摆动角度 fAngle2 = 80;//顺时针摆动角度 } else { CCLog("tag2"); time = 14; roTime = 3.2; fAngle1 = -100; fAngle2 = 100; } CCLog("rotime%ffAngle1%ffAngle2%f",roTime, fAngle1,fAngle1); //随机生成叶子横向偏移值 srand((UINT)GetCurrentTime()); int iRandPos = rand() % 250; CCLog("Pianyi%d", iRandPos); //叶子所运动到的位置 CCMoveTo *moveTo = CCMoveTo::actionWithDuration(time, ccp(CCDirector::sharedDirector()->getWinSize().width - iRandPos, 30)); CCCallFuncN *actDone = CCCallFuncN::actionWithTarget(this, callfuncN_selector(Leaf::resetLeafPos)); CCFiniteTimeAction *putdown = CCSequence::actions(moveTo, actDone, NULL); //叶子旋转动做 CCRotateBy *rotaBy1 = CCRotateBy::actionWithDuration(roTime, fAngle1); CCRotateBy *rotaBy2 = CCRotateBy::actionWithDuration(roTime, fAngle2); //叶子翻转动做 spriteLeaf->setVertexZ(60);//设置深度抬高60,避免出现使用CCOrbitCamera实现空间翻转时产生错位和遮挡等问题 //CCDirector::sharedDirector()->setDepthTest(false); //关闭深度测试一样能够避免上述问题,不过,推荐使用深度设置setVertexZ来正确解决,由于有时你可能须要遮挡的效果,关闭深度测试后将形成遮挡效果的缺失 CCOrbitCamera * orbit = CCOrbitCamera::actionWithDuration(8, 1, 0, 0, 360, 45, 0); //让树叶精灵始终执行三维翻转的动做 CCRepeat *fz3d = CCRepeat::actionWithAction(orbit, -1);//无限循环执行叶片翻转的动做 //CCRepeatForever *fz3d = CCRepeatForever::actionWithAction(orbit); //因为下面使用CCSpawn同时执行动做,因此不可使用无限次数类型的动做,而因使用有线次数循环CCRepeat将循环次数设置为-1 //用CCEaseInOut包装落叶摆动的动做,让树叶的进入、出现更天然(淡入淡出效果) CCEaseInOut *ease1 = CCEaseInOut::actionWithAction(rotaBy1, 3); CCEaseInOut *ease2 = CCEaseInOut::actionWithAction(rotaBy2, 3); //摆动动做合成 CCFiniteTimeAction *seq2 = CCSequence::actions(ease1, ease2, NULL);//依次执行顺时针、逆时针摆动 CCRepeat *baidong = CCRepeat::actionWithAction(seq2, -1);//摆动合成 //动做执行->同时执行全部动做 spriteLeaf->runAction(CCSpawn::actions(putdown, baidong, fz3d, NULL)); }</span>
如今叶子飘落的主干就设定完毕了,其实看上去并不复杂,就是三个动做:下落+摆动+翻转,将来使落叶更天然,咱们尽量的在数据可变的范围内使用随机参数,我这里用了系统时间作种子来产生随机数,可是我感受产生的随机数仍是不够理想,若是你有更好的种子,能够告诉我。其实还有不少参数能够在限定范围内使用随机数,因为时间关系我没有逐个去调试,而是直接设定了一个固定值。有时间你能够逐个设定实验,找到最佳的数据范围。
如今为了使咱们的落叶可以源源不断的产生,咱们还须要让落叶的产生和消亡循环起来:
<span style="font-size: 12px;">//重置叶子的位置 void Leaf::resetLeafPos(CCNode* sender) { int iTag = int(sender->getTag());//得到被重置叶片的标签 int iZoder = int(sender->getZOrder());//获取被重置叶片的z轴值 sender->removeFromParentAndCleanup(true);//清除已经落到底点的叶子 char sImg[15] = "img_yezi_1.png"; _snprintf(sImg, sizeof(sImg), "img_yezi_%d.png", iTag % 100); CCPoint pos; float fAngle; //随机生成叶子的起始位置 srand((UINT)GetCurrentTime()); int iRand = (rand() % 200); if (iTag == TAG_LEAF1) { pos = ccp(iRand, 600); fAngle = 30; } else { pos = ccp(iRand, 570); fAngle = 50; } //从新生成新的叶片,在起点处释放 CCSprite *spriteLeaf = CCSprite::spriteWithFile(sImg); spriteLeaf->setScale(0.5); spriteLeaf->setAnchorPoint(ccp(0.5, 3)); spriteLeaf->setRotation(fAngle); spriteLeaf->setPosition(pos); this->addChild(spriteLeaf, iZoder,iTag); this->playLeafAnim(spriteLeaf);//重置后的树叶再次执行飘落动做 }</span>
这样3d仿真的落叶的效果就基本实现了,为了节约时间,这里只写了2片叶子的状况,多片叶子的状况能够触类旁通,多加几片叶子就行。这里须要注意的是在使用CCOrbitCamera来实现三维空间的翻转时,因为openGL绘图的关系,咱们得将精灵的深度设置上浮,以免openGL绘图时精灵的部分被后面的色彩遮挡。
解决遮挡问题能够直接关闭深度测试CCDirector::sharedDirector()->setDepthTest(false);
也能够设置精灵VertexZ上浮spriteLeaf->setVertexZ(60);
若是你的程序不须要深度测试,你大能够直接关了它,可是你不能肯定是的程序是否每一个地方都没有用到深度测试,因此,推荐设置VertexZ值来避免你的精灵被遮挡。VertexZ值的大小为你的精灵被挡住部分的像素值。