Prainブログ

ゲーム開発とかIT小話とかその他雑記のブログ

*

ゲーム制作 Cocos2d-x関連 第10回 「パワーゲージによるボール移動距離の強弱付け」

      2016/08/16

コレは、・・・アレです。動くゲージをどこで止めるかによって、ボールの移動距離が変わるやつです。

 

 

スポンサーリンク

 

 

パワーゲージの実装方法検討

パワーゲージの実装方法ですが、

 

パワーゲージのフレーム

power_gauge_frame

パワーゲージ本体

power_gauge

の画像を用意しておいて、タップしている間カウンタを回し、カウンタの数に応じてパワーゲージ本体画像の横方向の表示の範囲を変えていく、というのがオーソドックスな実装だと思います。

 

 

パワーゲージ関係の具体的な仕様は以下のとおりです。

・ボールが止まっている時にボールをタップし続けるとパワーゲージが増減する。

・パワーゲージは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メソッドでカウンタをインクリメントしています。下図のイメージをプログラムに起こしています。

10_1

 

パワーゲージカウントの開始

onTouchBeganメソッドでフラグをtrueにすることでupdate中のパワーゲージカウントが実行されます。

 

パワーゲージカウントの終了

onTouchEndedメソッドでフラグをfalseにすることでupdate中のパワーゲージカウントが実行されなくなります。

 

パワーゲージの表示

viewPowerGaugeメソッドのsetTextureRectを毎フレーム行うことでゲージの増減を行っています。setTextureRectに指定する横方向の長さを、「(_powerGaugeCount / 100) × 元画像の長さ」としてやることでゲージ増減を表現しています。

 

 

シミュレータの起動

シミュレータを起動してみましょう。図のように画面下部にパワーゲージが表示されていて、ボールをタップしている時間に応じてゲージが増減すると思います。

10_sc_simu

パワーゲージ関係の仕様である

・ボールが止まっている時にボールをタップし続けるとパワーゲージが増減する。

・パワーゲージはMAXになると、僅かな時間MAX状態を維持する。

・タップを離した時点のパワーゲージの長さに比例してボールが移動する距離が長くなる

 

の確認もお忘れなく・・・。

 

 

だんだんゲームらしくなってきました・・・か?

次回はゲーム内で音楽や効果音を鳴らします。サウンドがあると、よりゲームらしくなります!!

 

 

次回

第11回 「音楽、効果音を鳴らす」

 

 - ,

        

Comment

  1. さとよし より:

    記事、大変勉強になっています。
    HelloWorldScene.cpp HelloWorld::initの
    4行目・13行目ですが、
    addChildの優先順位が0だと、ゲージの表示優先順位が不定?(コードの書かれた位置に依存)になると思います。(実際ゲージが表示されずに悩んだ)
    なので、優先順位は、2とかの大きな数字にした方が良いのではないでしょうか?

    • aki より:

      ご連絡ありがとうございます。

      ご見解の通り、addChildの表示オーダーにはenumなどを使用して、
      表示順序が不定にならないようにするべきです。

      一方で、当記事はなるべく初心者の方向けに最小限の構成を心掛けましたため、
      突然表示オーダーが変わるような記載を極力避けたつもりです。
      それでもいくつかは散見されますが・・・

      Cocos2dxの仕様として、addChildの表示オーダーに同一パラメータを指定した場合、
      最後に実行されたaddChildのオブジェクトが画面手前側に表示されるようです。

      ですので、あくまで記事通りに実装した場合は問題なく表示されるはずです。
      宜しくお願いいたします。

Message

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

  関連記事

Cocos2d-x iOS アプリ名(アプリのアイコン名)のローカライズ方法

こんにちは。akiです。 この記事ではCocos2d-xでのアプリ名(アプリのア …

cocos2dx
Cocos2d-x Http通信の完了に同期して処理を実行する

  こんにちは、akiです。 この記事ではHttp通信処理の完了に同期 …

シミュレータ
ゲーム制作 Cocos2d-x関連 第4回 「画面タッチに反応して画像を出そう」

画面を眺めててもゲームにはならないよ。画面タッチしたいんだけど!   …

アプリ名のローカライズ
Cocos2d-x アプリのアイコン名変更方法について iOS/Android

こんにちは。akiです。 この記事ではCocos2d-xにおけるアプリのアイコン …

新しいファイル
ゲーム制作 Cocos2d-x関連 第8回 「より保守性のあるプログラムを目指して」

  何だこのタイトルは・・・ゲーム制作はどこに行ったの?   …