cocos2d-x 仿真樹葉飄落效果的實現


轉自:http://blog.csdn.net/ufolr/article/details/7624851

最近項目中需要一個落葉的效果,本來想用粒子特效來實現,但是幾經調試,雖然調出了落葉的效果,但是並不是十分理想,最大的不足就是落葉是平面的,沒有立體感,雖然把落葉做小之后卻是立體感的感覺會有所緩解,但總不能把樹葉無限的縮小吧,而且立體感的缺失在粒子特效中確實是一個始終存在的問題。作為一個最求品質的程序猿,最終還是決定自己設精靈動作來實現。

   在分析了粒子特效實現的原理並在國內外論壇上爬了半天,最后邊實驗邊修改,終於完成了一個可行的仿真感較強的立體的落葉效果,現在就拿出來跟大家分享一下。

 

原理->樹葉飄落動作分析:

         樹葉下落過程分解為下落+擺動+葉片自傳

         也就是只要將這三個動作實現,並同時執行就可以實現樹葉飄落的效果。

下面就拿出代碼具體解析實現過程:

老規矩,先上.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__

 接下來是具體的實現,為了我們能不斷的產生自然、隨和的落葉,我們分三步來完成:

 

           1:第一次初始化;2:落葉動作的實現;3:下落動作完成重新設定落葉開始。

上代碼,先看看用到的頭文件:

#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>

  現在葉子飄落的主干就設定完畢了,其實看上去並不復雜,就是三個動作:下落+擺動+翻轉,未來使落葉更自然,我們盡可能的在數據可變的范圍內使用隨機參數,我這里用了系統時間做種子來產生隨機數,但是我感覺產生的隨機數還是不夠理想,如果你有更好的種子,可以告訴我。其實還有很多參數可以在限定范圍內使用隨機數,由於時間關系我沒有逐個去調試,而是直接設定了一個固定值。有時間你可以逐個設定實驗,找到最佳的數據范圍。

  現在為了使我們的落葉能夠源源不斷的產生,我們還需要讓落葉的產生和消亡循環起來:

//重置葉子的位置
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);//重置后的樹葉再次執行飄落動作
}

  這樣3d仿真的落葉的效果就基本實現了,為了節約時間,這里只寫了2片葉子的情況,多片葉子的情況可以舉一反三,多加幾片葉子就行。這里需要注意的是在使用CCOrbitCamera來實現三維空間的翻轉時,由於openGL繪圖的關系,我們得將精靈的深度設置上浮,以避免openGL繪圖時精靈的部分被后面的色彩遮擋。

     解決遮擋問題可以直接關閉深度測試CCDirector::sharedDirector()->setDepthTest(false);

     也可以設置精靈VertexZ上浮spriteLeaf->setVertexZ(60);

     如果你的程序不需要深度測試,你大可以直接關了它,但是你不能確定是的程序是否每個地方都沒有用到深度測試,所以,推薦設置VertexZ值來避免你的精靈被遮擋。VertexZ值的大小為你的精靈被擋住部分的像素值。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM