Cocos2d-x(3.x系)で、ロマンシングサ・ガ3風のダメージ表示のエフェクトを再現してみました。
制作環境は以下の通りです。
- Cocos2d-x 3.14.1
- Windows10上でWin32プロジェクトにて確認
参考にした動画
実装した結果
微妙に画像がつぶれているので見づらい場合は拡大してください。
説明
当方観察力が無いので、オリジナルの動画から仕様が読み取れているか謎ですが、以下のような感じだと思います。
- 各桁は放物線を描くように放り投げられるように表示される
- 敵のダメージ表示は最終桁 → 先頭桁の順に数字が大きく放り投げられて着地する
- 味方の場合先頭桁 → 最終桁の順に数字が大きく放り投げられて着地する
- 着地した瞬間ほんの少しだけバウンドする
また、実装時は考慮していませんが
- 数字の初期位置が各桁で少し縦にずれている
- 最終桁だけ放物線が大きい?
などの効果が入っているかもしれません。
で、実装の方針ですが
- Nodeを継承した自作クラスを作成する
- 複数のLabelをNodeでまとめて管理する
で行きたいと思います。
ヘッダー側
Nodeを継承した表示用のクラスをこんな感じで用意します。
#pragma once #pragma execution_character_set("utf-8") #include "cocos2d.h" // ダメージ表示を表します。 class DmgEffect : public cocos2d::Layer { // 左向きに数値を表示するかどうかのフラグ(デフォは右) bool _isLeft = false; DmgEffect(); // 各桁を表すラベルを作成する void createLabels(std::string dmg); // 最後に自殺する動作を設定する void setupCleanupAction(std::string dmg, float dulation); // 生成したラベルを取り出す std::vector<Node*> getLabels(std::string dmg); public: // リソースを解放してオブジェクトを破棄する ~DmgEffect(); // いつもの virtual bool init() override; // いつもの static DmgEffect* create(); // 指定した数値でダメージエフェクトを開始する void showEffect(int dmg); // 全てのActionが終了した時の処理 void remove(); // 右か左かの設定または取得 void isLeft(bool value) { this->_isLeft = value; } bool isLeft() { return this->_isLeft; } };
実装側(抜粋)
上記ヘッダーに肉付けしていきます。表示状態は管理しないので、全部終了したら自分で自分を消去するように仕込んでおきます。(そのためにNodeを継承しています)
#include "DmgEffect.h" using namespace std; using namespace cocos2d; DmgEffect::DmgEffect() { // nop } void DmgEffect::createLabels(string dmg) { for (int i = 0; i < dmg.length(); i++) { // BABARAGEOさんからお借りしたBABARAGEOフォント // http://babarageo.com/cgi/wp/ja/notes/babarageo-font.html auto _label = Label::create(std::string{ dmg[i] }, "fonts/babarageo3.ttf", 10); _label->setTag(i); this->addChild(_label); } } void DmgEffect::setupCleanupAction(string dmg, float dulation) { // エフェクトが全部終わったら自殺する設定 Node* last = this->getChildByTag(dmg.length()-1); last->runAction(Sequence::create(DelayTime::create(dulation + 0.3f), CallFunc::create(CC_CALLBACK_0(DmgEffect::remove, this)), nullptr)); } std::vector<Node*> DmgEffect::getLabels(string dmg) { vector<Node*> labelList; for (int i = 0; i < dmg.length(); i++) { labelList.push_back(this->getChildByTag(i)); } if (this->_isLeft) { std::reverse(std::begin(labelList), std::end(labelList)); } return labelList; } DmgEffect::~DmgEffect() { this->remove(); } bool DmgEffect::init() { /* 省略 */ } DmgEffect* DmgEffect::create() { /* 省略 */ } void DmgEffect::showEffect(int dmg) { string dmgStr = to_string(dmg); this->createLabels(dmgStr); // 数字がジャンプする動作の設定 int i = 0.1; float dulation = 0.0f; for (Node* _label : this->getLabels(dmgStr)) { dulation = i / 18.0f; Vec2 newPos(14 * i * (this->_isLeft ? -1 : 1), 0); auto jumpAction1 = JumpBy::create(dulation, newPos, 7 * i, 1); auto jumpAction2 = JumpBy::create(0.07f, Vec2(0, 0), 5, 1); // バウンド _label->runAction(Sequence::create(jumpAction1, jumpAction2, nullptr)); i++; } this->setupCleanupAction(dmgStr, dulation); } void DmgEffect::remove() { this->removeAllChildrenWithCleanup(true); this->removeFromParentAndCleanup(true); }
利用側コード
使用する際にエフェクト表示前に右側・左側のプロパティを指定して、タッチ座標にエフェクトを表示しています。
bool HelloWorld::init() { // ...省略... srand((unsigned int)time(nullptr)); auto listner = EventListenerTouchOneByOne::create(); //Sprite* sp = this->animSp; listner->onTouchBegan = [this](Touch* touch, Event* event) { DmgEffect* effect = DmgEffect::create(); effect->isLeft((this->cnt++ %2 == 0 ? true : false)); // 左右に振り分け effect->setPosition(touch->getLocation()); effect->showEffect(rand() % 9999); this->addChild(effect); return true; }; this->getEventDispatcher()->addEventListenerWithSceneGraphPriority(listner, this); return true; }
処理手順ですが
- ダメージを数字で取る
- 各桁に対応する文字をラベルで生成する
- ラベルにジャンプアクション(JunbBy)を設定する
で、
- 偶数回タップ時は左(敵のダメージ表現)、奇数回タップ時は右(味方ダメージ表現)
としています。
数字が放物線を描いて着地 → 少しバウンド、の動作はJumpアクションをSequenceで順番に実行しています。
Jumpアクションを使うと放物線の表現は簡単にできます。但し全て等速運動なのでご注意ください。
最後に - 汎用化の考察
「複数のラベルを作成して画面に追加」して「終了後に全部まとめて消す」というパターンは結構よくありそうです。そこで、showEffect関数内の「// 数字がジャンプする動作の設定」の個所をデリゲーションして外から具体的な動作を注入したり、派生クラスで動きをつけたり拡張できそうなので、そういったエフェクトならばダメージ以外以外にも適用できそうです。
関連記事
Cocos2d-xでロマンシングサ・ガ3風のダメージ表示を再現する(この記事) Cocos2d-xでFF5風のダメージを再現する