コインエフェクトを作成する

Cocos2d-x 3.14.1でコインエフェクトを作成したいとおもいます。

エフェクトは宝箱を開けたときや、敵を倒したときに「複数枚のコインがジャラジャラと出る」ようなエフェクトにしたいと思います。自分で描いたコインの画像を使います。

f:id:Takachan:20170828210122p:plain

で、作成したものがこちら。

それっぽくジャラジャラしていますよね?

考え方

もう少し細かく動作の仕様を考えたいと思います。

  • コインは放物線を描いて3回バウンドする
    • バウンドすると運動量が半分になる
  • 同じ場所でバウンドせず、X/Y方向に移動しながらバウンドする
  • 動きは等速運動とする
    • 等速運動でも動きが早ければそれなりに見える

で、バウンドはアニメーションで使用するActionのJumpByを使用します。

図にすると以下のようになります。

f:id:Takachan:20170828204649p:plain

では、早速実装していきたいと思います。

実装 - ヘッダー側

特に状態を管理しないため、Spriteを継承したCoinEffectクラスを作成し、runEffectすると自分自身にコインを設定して指定した座標でコインが発生するような動作を行います。

#pragma once
#pragma execution_character_set("utf-8")

#include "cocos2d.h"

// ダメージ表示を表します。
class CoinEffect : public cocos2d::Sprite
{
    // 生成個数
    int minCnt = 8;
    int maxCnt = 20;

    // 実行時間
    float minDulation = 0.2f;
    float maxDulation = 0.35f;

    // 飛び跳ねる高さ
    float minHeight = 12.0f;
    float maxHeight = 22.0f;

    // 広がる幅
    float minX = -18.0f;
    float maxX = 18.0f;

    // 広がる高さ
    float minY = -14.0f;
    float maxY = 0;

    // 最後に自分を削除する処理用
    void updateSelf();

public:

    // いつもの
    CoinEffect() {}

    // いつもの
    ~CoinEffect() {}

    // いつもの
    virtual bool init();
    
    // いつもの
    static CoinEffect* create();

    // 指定した速度でフェクトを開始する
    void runEffect(const std::string img);

    // アニメーション実行を設定する
    void setDulation(const float min, const float max)
    {
        this->minDulation = min;
        this->maxDulation = max;
    }

    // 飛び跳ねる高さ
    void setJumpHeight(const float min, const float max)
    {
        this->minHeight = min;
        this->maxHeight = max;
    }

    // 広がる範囲 - X方向
    void setWidth(const float min, const float max)
    {
        this->minX = min;
        this->maxX = max;
    }

    // 広がる範囲 - Y方向
    void setHeight(const float min, const float max)
    {
        this->minY = min;
        this->maxY = max;
    }
};

実装 - コード

次にコード側を実装します。

#include "CoinEffect.h"

using namespace std;
using namespace cocos2d;

bool CoinEffect::init()
{
    if (!Sprite::init())
    {
        return false;
    }

    return true;
}

CoinEffect * CoinEffect::create()
{
    CoinEffect *pRet = new(std::nothrow) CoinEffect();
    if (pRet && pRet->init())
    {
        pRet->autorelease();
        return pRet;
    }
    else
    {
        delete pRet;
        pRet = nullptr;
        return nullptr;
    }
}

void CoinEffect::runEffect(const string img)
{
    int max = random(this->minCnt, this->maxCnt);

    for (int i = 0; i < max; i++)
    {
        auto coin = Sprite::create(img);

        float baseDulation = random(this->minDulation, this->maxDulation);
        float baseHeight = random(this->minHeight, this->maxHeight);
        float baseVecX = random(this->minX, this->maxX);
        float baseVecY = random(this->minY, this->maxY);

        // 段々バウンドが小さくなるようにjumpを指定
        auto j1 = JumpBy::create(baseDulation, Vec2(baseVecX, baseVecY), baseHeight, 1);
        auto j2 = JumpBy::create(baseDulation * 0.8, Vec2(baseVecX / 2, baseVecY / 2), baseHeight / 2, 1);
        auto j3 = JumpBy::create(baseDulation * 0.6, Vec2(baseVecX / 4, baseVecY / 4), baseHeight / 4, 1);
        auto delay = DelayTime::create(1.0f);
        auto lastFunc = CallFuncN::create([this](Node* node) 
        {
            node->removeFromParentAndCleanup(true); 
            this->updateSelf();
        });
        

        coin->runAction(Sequence::create(j1, j2, j3, delay, lastFunc, nullptr));

        this->addChild(coin);
    }
}

void CoinEffect::updateSelf()
{
    this->total++;

    if (this->max >= this->total)
    {
        this->removeFromParentAndCleanup(true);
    }
}

利用側コード

利用時は、デフォルトだと動画の通りの動きになりますが、カスタマイズしたい場合各アクセッサで自分の希望の値を指定します。以下の例ではタッチした座標にコインエフェクトを発生させます。

bool HelloWorld::init()
{
    // ...省略...

    // コインのエフェクト
    auto listner = EventListenerTouchOneByOne::create();
    listner->onTouchBegan = [this](Touch* touch, Event* event)
    {
        auto effect = CoinEffect::create();
        effect->setPosition(Vec2(touch->getLocation().x + random(-10, 10),
                                 touch->getLocation().y + random(-10, 10)));
        effect->runEffect("coin.png");

        this->addChild(effect);

        return true;
    };

    this->getEventDispatcher()->addEventListenerWithSceneGraphPriority(listner, this);

    return true;
}

以上、でコインエフェクトの実装は完了です。