Cocos2d-x ゲーム制作

ゲーム制作 Cocos2d-x関連 第7回 「ボールとドロップとの衝突検知と跳ね返り」

投稿日:2016年7月3日 更新日:

今回はボールとドロップとの衝突検知を実装してきます。

 

前回はボールと画面端との衝突判定と跳ね返りのプログラムを行いました。

 

画面端であれば上下左右の4通りの検知を行えばよかったのですが、画面上に存在するドロップの数が変わり得る場合、ベタにプログラムを書くことは難しいですね。

 

 

リストを使おう!

リストの用意

そこで便利なのがリストです。リストを使えばプログラムを比較的簡易に記述することができます。以下のプログラムを記述してください。

 

HelloWorldScene.h

std::list<cocos2d::Sprite*> _dropList; // フィールド上のドロップを格納する

 

・std::list

std::listはC++のコンテナです。上記のプログラムはSprite*型の変数を格納できる_dropListを定義した、という意味です。

詳しくはC++プログラミングシリーズで解説予定です。

 

リストへの要素の格納

実際にプログラム中でリストに要素を格納していきましよう

HelloWorldScene.cpp HelloWorld::init

 // ドロップ5種の格納
 _dropList.push_back(dropRed);
 _dropList.push_back(dropYellow);
 _dropList.push_back(dropBlue);
 _dropList.push_back(dropGreen);
 _dropList.push_back(dropPurple);

 

・push_back

リストの最後尾に要素を追加するメソッドです。

 

 

 

それでは続いて衝突判定の繰り返し処理、跳ね返り処理を実装していきましょう。

衝突判定の繰り返し処理、跳ね返り処理

ドロップとボールとの衝突判定の繰り返し処理、跳ね返り処理を行っていきます。

下記のプログラムをHelloWorldScene.cppのupdateメソッド中に追加してください。

 

HelloWorldScene.cpp HelloWorld::update

void HelloWorld::update(float dt){

    if(_isBallMoving){
        float x = _ball->getPosition().x - _speed*cosf(CC_DEGREES_TO_RADIANS(_degree));
        float y = _ball->getPosition().y + _speed*sinf(CC_DEGREES_TO_RADIANS(_degree));
        _ball->setPosition(Vec2(x, y));


        // ドロップに接触した場合の跳ね返りの処理
        std::list<Sprite*>::iterator it; 
        for(it = _dropList.begin(); it != _dropList.end(); it++){
            if(hitDetectionOval(_ball, (*it))){
                // 跳ね返り角度の更新 
                _degree = getDegree(_ball->getPosition(), (*it)->getPosition()); 
            } 
        }

        // 画面端に接触した場合の跳ね返りの処理
        Size visibleSize = Director::getInstance()->getVisibleSize();

        // 画面下端
        if(_ball->getPosition().y - _ball->getContentSize().height / 2 <= 0){
            _degree = _degree * -1;
            y = _ball->getContentSize().height / 2;
        }

        // 画面上端
        if(_ball->getPosition().y + _ball->getContentSize().height / 2 >= visibleSize.height - _enemy->getContentSize().height){
            _degree = _degree * -1;
            y = visibleSize.height - _ball->getContentSize().width / 2 - _enemy->getContentSize().height;
        }

        // 画面左端
        if(_ball->getPosition().x - _ball->getContentSize().width / 2 <= 0){
            _degree = 180 - _degree;
            x = _ball->getContentSize().width / 2;
        }

        // 画面右端
        if(_ball->getPosition().x + _ball->getContentSize().width / 2 >= visibleSize.width){
            _degree = 180 - _degree;
            x = visibleSize.width - _ball->getContentSize().width / 2;
        }

        _ball->setPosition(Vec2(x, y));
}
※ハイライト部分を追記してください。

 

std::list<Sprite*>::iterator it;

for(it = _dropList.begin(); it != _dropList.end(); it++){}

先ほどのリストに格納したドロップをを先頭から最後尾まで取得していくのがこの2行です。この書き方はお決まりなのでリストを使うときは覚えておくと良いと思います。

 

・hitDetectionOval

自作のメソッドです。2つのオブジェクトの円形衝突判定を行います。説明は後述します。

 

・getDegree

前回も使用した自作のメソッドです。2つのオブジェクトの座標から角度を取得します。ドロップがボールにぶつかった際は、このメソッドで新しい角度を取得し、ボールが移動を続けることになります。

 

 

衝突判定の繰り返し処理、跳ね返り処理のプログラム解説

ボールとドロップとの衝突判定の繰り返し処理

リストの先頭からドロップを順番に取り出し、ボールとの円形衝突判定を行っています。衝突している場合には衝突したドロップとボールとの角度を取得して跳ね返りとしています。

 

複数ドロップとの同時衝突の考慮

同時に2つ以上のドロップと衝突した場合は、リストの末尾に格納されているドロップが優先されます。イメージは下図になります。

7

 

この衝突処理は、ゲームとしてどうあるべきかによって実装は変わってきます。例えば以下のようにです。

・リスト末尾に近い要素との衝突を優先する(今回)

・リスト先頭に近い要素との衝突を優先する(衝突を検知した段階でbreak処理を入れるなどして、途中で処理を打ち切る)

・複数の角度を取得し、中間の角度を取得するようにする

 

どの処理が適切であるかは、ゲームの内容によって変わってくるかと思います。

 

 

次に、円形の物体の衝突判定について解説します。

円形の物体の衝突判定を行う

実際に衝突判定を行うメソッドであるhitDetectionOvalメソッドを追加します。

円形での衝突処理のイメージはこんな感じでしょうか。

 

【衝突の直前】

ball_drop1

【衝突の直後】

ball_drop2

 

 

それでは以下のプログラムを追記してください。

HelloWorldScene.h

bool hitDetectionOval(cocos2d::Sprite*, cocos2d::Sprite*); // 円形の接触判定を行う

 

 

HelloWorldScene.cpp HelloWorld::hitDetectionOval

/*
 円形の接触判定を行う。接触していればtrue,接触していなければfalseを返却する
 オブジェクトから算出する円形の縮小率は0.9とする
 
 */
bool HelloWorld::hitDetectionOval(Sprite* o, Sprite* t){
 
    if(((o->getPosition().x - t->getPosition().x) * (o->getPosition().x - t->getPosition().x)
        + (o->getPosition().y - t->getPosition().y) * (o->getPosition().y - t->getPosition().y))
        <= ((o->getContentSize().width / 2 * 0.9 + t->getContentSize().width / 2 * 0.9 ))
        * ((o->getContentSize().width / 2 * 0.9 + t->getContentSize().width / 2 * 0.9))){
        return true;
 
    }else{
        return false;
    }
}

 

 

円形衝突判定のプログラム解説

円形衝突判定のアルゴリズム

3平方の定理より、「2点間の(X方向の距離)^2 +(Y方向の距離)^2」が「(それぞれの円の半径の加算)^2」よりも小さければ接触していることになります。

三平方の定理 プレイン Prain

 

 

円形の衝突判定はググると詳しく解説しているサイトが出てきますのでそちらをどうぞ。

 

当たり感の調整

係数として0.9を乗じることで、見せかけの当たり判定よりも小さくして当たり感を自然にしています。

 

 

シミュレータの起動

さてシミュレータを起動してみましょう

 

どうでしょうか、ボールとドロップが接触すると跳ね返るようになったと思います。

 

ただ、ボールはずっと動き続けていますよね。次はボールの減速処理を追加したいと思います。

 

 

次回

第8回 「より保守性のあるプログラムを目指して 」

 

んん・・・おお・・・??

 

PrainGoogleAdSense

PrainGoogleAdSense

-Cocos2d-x, ゲーム制作
-,

執筆者:


  1. うめなつ より:

    衝突判定、自分で実装するとなるとなかなか難しいですよね・・・
    ボールに重力を与えて、ただバウンドするだけのゲームを作ってた頃は「なんだ物理演算って簡単じゃん」とか考えてました。
    それで実際なにか作ろうとしたら、質量、摩擦、反発係数を考えなきゃいけないし、四角いものがぶつかったら回転しなきゃいけない。そんなこんなで挫折した経験があります。
    思えば、当時はボール同士の衝突について解説しているサイトはほとんど無かったように思います。(そのせいで物理の教科書を広げるハメになりました)
    物理とプログラムの両方を理解する必要があるので内容はやや難しいですが、きっとこの講座は役に立つと思います!

    • aki より:

      コメントありがとうございます。

      これから活動の内容を増やしていく予定ですので、お手すきの際、たまにチェックしていただければ幸いです!

comment

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

関連記事

cocos2dx

Cocos2d-x サーバ側の処理とデータ送信(Httpレスポンス)

この記事は「Cocos2d-x セーブデータをサーバに送信して保存する」の続きになります。 ・第1回 : サーバにデータを保存したい理由と全体の流れ ・第2回 : クライアント側の処理とデータ送信(H …

ゲーム制作 Cocos2d-x関連 最終回 「勝利演出、敗北演出の追加」

勝利演出、敗北演出を実装していきます。今回で最終回です!     仕様の検討 1.バトル中、プレイヤーのHPが0になった場合 → 勝利演出 → 勝利演出中に画面をタップするとスター …

ボールの進行方向

ゲーム制作 Cocos2d-x関連 第6回 「画面端でのボールの跳ね返り」

今回は画面端でのボールの跳ね返りを製作していきましょう。   前回ではボールを弾くと、二度と触れられないところまで去って行ってしまいましたね。それでは早速やっていきましょう。   …

アプリ名のローカライズ

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

こんにちは。akiです。 この記事ではCocos2d-xにおけるアプリのアイコン名の変更方法を、iOSとAndroidでそれぞれ紹介します。それでは早速やっていきましょう。     …

cocos2dx

Cocos2d-x クライアント側のHttpレスポンス処理

この記事は「Cocos2d-x セーブデータをサーバに送信して保存する」の続きになります。 ・第1回 : サーバにデータを保存したい理由と全体の流れ ・第2回 : クライアント側の処理とデータ送信(H …