コレは、・・・アレです。動くゲージをどこで止めるかによって、ボールの移動距離が変わるやつです。
パワーゲージの実装方法検討
パワーゲージの実装方法ですが、
パワーゲージのフレーム
と
パワーゲージ本体
の画像を用意しておいて、タップしている間カウンタを回し、カウンタの数に応じてパワーゲージ本体画像の横方向の表示の範囲を変えていく、というのがオーソドックスな実装だと思います。
パワーゲージ関係の具体的な仕様は以下のとおりです。
・ボールが止まっている時にボールをタップし続けるとパワーゲージが増減する。
・パワーゲージはMAXになると、僅かな時間MAX状態を維持する。
・タップを離した時点のパワーゲージの長さに比例してボールが移動する距離が長くなる
それでは早速行きましょう!!
リソースの追加
まずはパワーゲージの画像をresourcesに追加してください。画像は以下からダウンロードしてください。
画像追加のやり方を忘れてしまった方は第2回 「Cocos2d-xで作ろう!!~画像の表示処理」を参考にしてください。
プログラムの実装
続いて、以下のコードを記述してください。ハイライトされているメソッドは、ハイライト部分を追記または変更してください。
HelloWorldScene.h
// パワーゲージ関連 BaseChara* _powerGaugeFrame; BaseChara* _powerGauge; bool _isPowerGaugeCounting = false; int _powerGaugeCount = 0; // パワーゲージをカウントする int _powerGaugeStopCount = 0; // パワーゲージが100%になった時、わずかに100%を維持するためのカウンタ float _powerGaugeWidth; cocos2d::Vec2 _powerGaugeOrigin; void viewPowerGauge(); // パワーゲージの増減の様子を表示する
HelloWorldScene.cpp HelloWorld::init
// パワーゲージ(外枠) _powerGaugeFrame = BaseChara::create("power_gauge_frame.png"); _powerGaugeFrame->setPosition(Vec2(visibleSize.width / 2, visibleSize.height / 20)); this->addChild(_powerGaugeFrame, 0, 5); // パワーゲージ _powerGauge = BaseChara::create("power_gauge.png"); _powerGauge->setPosition(Vec2(visibleSize.width / 2 - _powerGauge->getContentSize().width / 2 , visibleSize.height / 20 - _powerGauge->getContentSize().height / 2)); _powerGauge->setAnchorPoint(KM_VEC2_ZERO); _powerGaugeOrigin = _powerGauge->getTextureRect().origin; _powerGaugeWidth = _powerGauge->getContentSize().width; this->addChild(_powerGauge, 0, 5);
・setAnchorPoint(KM_VEC2_ZERO)
画像のアンカーポイントを(0,0)地点に設定しています。アンカーポイントとは画像をsetPositionする際の基準点です。cocos2dxでは通常は画像の中央にアンカーポイントが設定されています。
HelloWorldScene.cpp HelloWorld::update
void HelloWorld::update(float dt){ // パワーゲージの増加 if(_isPowerGaugeCounting){ if(_powerGaugeCount >= 100){ _powerGaugeCount = 100; _powerGaugeStopCount++; if(_powerGaugeStopCount >= 6){ _powerGaugeCount = 0; _powerGaugeStopCount = 0; } }else if(35 < _powerGaugeCount && _powerGaugeCount < 100){ _powerGaugeCount += 10; }else{ _powerGaugeCount += 5; } // パワーゲージ増減の表示 viewPowerGauge(); } if(_ball->_isBallMoving){ float x = _ball->getPosition().x - _ball->_speed*cosf(CC_DEGREES_TO_RADIANS(_ball->_degree)); float y = _ball->getPosition().y + _ball->_speed*sinf(CC_DEGREES_TO_RADIANS(_ball->_degree)); _ball->setPosition(Vec2(x, y)); // ドロップに接触した場合の跳ね返りの処理 std::list<Sprite*>::iterator it; for(it = _dropList.begin(); it != _dropList.end(); it++){ if(hitDetectionOval(_ball, (*it))){ // 跳ね返り角度の更新 _ball->_degree = getDegree(_ball->getPosition(), (*it)->getPosition()); } } // 画面端に接触した場合の跳ね返りの処理 Size visibleSize = Director::getInstance()->getVisibleSize(); // 画面下端 if(_ball->getPosition().y - _ball->getContentSize().height / 2 <= 0){ _ball->_degree = _ball->_degree * -1; y = _ball->getContentSize().height / 2; } // 画面上端 if(_ball->getPosition().y + _ball->getContentSize().height / 2 >= visibleSize.height - _enemy->getContentSize().height){ _ball->_degree = _ball->_degree * -1; y = visibleSize.height - _ball->getContentSize().width / 2 - _enemy->getContentSize().height; } // 画面左端 if(_ball->getPosition().x - _ball->getContentSize().width / 2 >= 0){ _ball->_degree = 180 - _ball->_degree; x = _ball->getContentSize().width / 2; } // 画面右端 if(_ball->getPosition().x + _ball->getContentSize().width / 2 >= visibleSize.width){ _ball->_degree = 180 - _ball->_degree; x = visibleSize.width - _ball->getContentSize().width / 2; } _ball->setPosition(Vec2(x, y)); // 減速処理 _ball->_speedDownCount++; // 一定回数をカウントしたら徐々に減速する if(_ball->_speedDownCount > _powerGaugeCount){ // スピードが0になったらムーブフラグをfalseにする。減速したスピードを元に戻し、スピードダウンカウントを0に初期化する // 減速し終えたら攻撃エフェクト if(_ball->_speed <= 0){ // 減速していたスピードを元に戻す _ball->_speed = 50; // ボール移動中フラグを元に戻す _ball->_isBallMoving = false; // カウンタのリセット _ball->_speedDownCount = 0; }else{ // 減速処理 _ball->_speed -= 1; } } } }
HelloWorldScene.cpp HelloWorld::onTouchBegan
bool HelloWorld::onTouchBegan(Touch* pTouch, Event* pEvent){ // ボールが動いている状態でない、かつプレイヤーのターンであればタップ可能 if(!_ball->_isBallMoving){ Vec2 touchPoint = convertTouchToNodeSpace(pTouch); if(_ball->boundingBox().containsPoint(touchPoint)){ // 角度の取得 _ball->_degree = getDegree(_ball->getPosition(), touchPoint); // 矢印オブジェクトの角度を設定 _allow = Sprite::create("allow.png"); _allow->setPosition(Vec2(_ball->getPosition().x, _ball->getPosition().y)); _allow->setRotation(_ball->_degree); this->addChild(_allow, 0, 7); // パワーゲージカウント開始、パワーゲージカウントをリセット _isPowerGaugeCounting = true; _powerGaugeCount = 0; return true; } } return false; }
HelloWorldScene.cpp HelloWorld::onTouchEnded
void HelloWorld::onTouchEnded(Touch* pTouch, Event* pEvent){ if(_allow != nullptr){ _allow->removeFromParent(); _allow = nullptr; } _ball->_isBallMoving = true; // パワーゲージカウントを停止 _isPowerGaugeCounting = false; }
HelloWorldScene.cpp HelloWorld::viewPowerGauge
void HelloWorld::viewPowerGauge(){ _powerGauge->setTextureRect(Rect(_powerGaugeOrigin.x ,_powerGaugeOrigin.y , _powerGaugeWidth * _powerGaugeCount / 100 , _powerGauge->getContentSize().height)); }
・setTextureRect
cocos2dxのメソッドで、スプライト画像の表示範囲を設定できます。コレを知っているかどうかがパワーゲージ表現の鍵になると思います。
「setTextureRect(Rect(X軸方向の基準点, Y軸方向の基準点, X軸方向の長さ, Y軸方向の長さ))」という風に引数を指定して、スプライト画像に矩形を指定します。この時、アンカーポイントは予め(0,0)が指定されていないとパワーゲージの表現として不自然になる点に注意しましょう。
プログラム解説
パワーゲージ増加のイメージ
updateメソッドでカウンタをインクリメントしています。下図のイメージをプログラムに起こしています。
パワーゲージカウントの開始
onTouchBeganメソッドでフラグをtrueにすることでupdate中のパワーゲージカウントが実行されます。
パワーゲージカウントの終了
onTouchEndedメソッドでフラグをfalseにすることでupdate中のパワーゲージカウントが実行されなくなります。
パワーゲージの表示
viewPowerGaugeメソッドのsetTextureRectを毎フレーム行うことでゲージの増減を行っています。setTextureRectに指定する横方向の長さを、「(_powerGaugeCount / 100) × 元画像の長さ」としてやることでゲージ増減を表現しています。
シミュレータの起動
シミュレータを起動してみましょう。図のように画面下部にパワーゲージが表示されていて、ボールをタップしている時間に応じてゲージが増減すると思います。
パワーゲージ関係の仕様である
・ボールが止まっている時にボールをタップし続けるとパワーゲージが増減する。
・パワーゲージはMAXになると、僅かな時間MAX状態を維持する。
・タップを離した時点のパワーゲージの長さに比例してボールが移動する距離が長くなる
の確認もお忘れなく・・・。
だんだんゲームらしくなってきました・・・か?
次回はゲーム内で音楽や効果音を鳴らします。サウンドがあると、よりゲームらしくなります!!
次回
記事、大変勉強になっています。
HelloWorldScene.cpp HelloWorld::initの
4行目・13行目ですが、
addChildの優先順位が0だと、ゲージの表示優先順位が不定?(コードの書かれた位置に依存)になると思います。(実際ゲージが表示されずに悩んだ)
なので、優先順位は、2とかの大きな数字にした方が良いのではないでしょうか?
ご連絡ありがとうございます。
ご見解の通り、addChildの表示オーダーにはenumなどを使用して、
表示順序が不定にならないようにするべきです。
一方で、当記事はなるべく初心者の方向けに最小限の構成を心掛けましたため、
突然表示オーダーが変わるような記載を極力避けたつもりです。
それでもいくつかは散見されますが・・・
Cocos2dxの仕様として、addChildの表示オーダーに同一パラメータを指定した場合、
最後に実行されたaddChildのオブジェクトが画面手前側に表示されるようです。
ですので、あくまで記事通りに実装した場合は問題なく表示されるはずです。
宜しくお願いいたします。