Cocos2d-x 10円ゲーム講座 第10回、効果音を鳴らします。
前回までの実装により、コインは転がり穴には落ちる。ゴールすれば景品も出る。しかし何か物足りませんね。何かというか、音です。
コインがぶつかる音があるだけで、リアリティがまるで変わります。そんなわけで、ゲームの大切な要素、効果音を鳴らしてみましょう。
今回は効果音を作成することにしました。ネット上にフリー素材が山ほどありますが、それは使用に関してフリーなだけで大体は再配布禁止です。githubでの公開には問題があるため、自分で作ることにしました。
コインの音は簡単に録音できます。一人ではっちゃけて歌いたくなったのでこんなマイクを持っていますが、別にヘッドセットのマイクや、スマートフォンなどでも十分録音できます。
コイン音は3つ録音しました。
ゴールした時の電子音などはBeam2002を使って作りました。Beam2002はアナログシンセ風の効果音作成ソフトウェアです。ADSRを操作するいわゆるエンベロープ・ジェネレーターのようなことができるソフトです。
レバー操作時の音と、ゴール時の音を作成しました。
次はこれらを10円ゲームに組み込み、再生します。
効果音が用意できたので鳴らしてみることにします。Cocos2d-xで効果音の再生はCocosDenshionクラスを使用します。Denshion、でんしおん、つまりは電子音です。
init()で使用する効果音をプリロードしておきます。
#include <SimpleAudioEngine.h> bool OneCoinGame::init() { /*--- 略 ---*/ // サウンドのプリロード CocosDenshion::SimpleAudioEngine::getInstance()->preloadEffect("se/coin_entry.wav"); CocosDenshion::SimpleAudioEngine::getInstance()->preloadEffect("se/coin_contact.wav"); CocosDenshion::SimpleAudioEngine::getInstance()->preloadEffect("se/coin_hole.wav"); CocosDenshion::SimpleAudioEngine::getInstance()->preloadEffect("se/goal.wav"); CocosDenshion::SimpleAudioEngine::getInstance()->preloadEffect("se/lever.wav"); }
CocosDenshionは拡張クラスなので、SimpleAudioEngine.hをインポートします。
後は効果音を鳴らしたい箇所でplayEffect()を実行するだけで、効果音を鳴らすことが出来ます。
// コイン投入ボタン void OneCoinGame::insertCoinCallback(cocos2d::Ref* pSender) { /*--- 略 ---*/ // 効果音を鳴らす CocosDenshion::SimpleAudioEngine::getInstance()->playEffect("se/coin_entry.wav"); }
これでコイン投入口をタッチした時に、お金を投入する効果音が鳴ります。
同じように、コインが穴に落ちた時の音、ゴール時、レバー操作時も該当箇所にコードを実装するだけです。
「コインがレールなどに接触した音」を作成しましたが、この音を鳴らすには、コインの接触を検知する必要があります。
コインの接触を検知するには、剛体が接触した時のイベントを受け取るイベントリスナーを使います。
接触イベントを受け取るメソッドを定義
class OneCoinGame : public cocos2d::Layer { public: // 接触イベントの検知 bool onContactBegin(cocos2d::PhysicsContact& contact); };
タッチイベントと同様に、接触イベント用のリスナーを生成して定義したonContactBeginメソッドを設定、イベントリスナーへ登録することで準備完了です。
// 衝突イベントの検知 auto contactListener = EventListenerPhysicsContact::create(); contactListener->onContactBegin = CC_CALLBACK_1(OneCoinGame::onContactBegin, this); this->getEventDispatcher()->addEventListenerWithSceneGraphPriority(contactListener, this);
これで剛体同士が接触する時のイベントを受け取ることができるようになりますが、接触イベントはまだ発生する設定になっていません。
接触イベントを発生させるには、各剛体に対してsetContactTestBitmask(int bitmask)でマスクの設定を行う必要があります。この値はデフォルトで0のため、0以外に設定しないと接触イベントが発生せず、イベントを受け取ることができません。
デフォルトが0なのは剛体の接触イベントは大量に検知してしまうため、必要なものだけ検知してパフォーマンスを維持するためです。
筐体内部
wall->getPhysicsBody()->setContactTestBitmask(0x00000001); // 接触イベントの検知マスク
レール
rail->getPhysicsBody()->setContactTestBitmask(0x00000001); // 接触イベントの検知マスク
コイン
coinPhysics->setContactTestBitmask(0x00000001); // 接触イベントの検知マスク coinPhysics->setName("coin");
接触イベントのビットマスク設定をそれぞれ設定しました。コインは接触時に識別したいのでsetName()で名前を付けて置きます。
ビットマスクの設定ができたら、onContactBegin(PhysicsContact& contact)で衝突を検知できるので、効果音を鳴らすコードを実装します。
// 衝突が発生した際に呼ばれるイベント bool OneCoinGame::onContactBegin(PhysicsContact& contact) { // 衝突した2つの物体について取得 auto bodyA = contact.getShapeA()->getBody(); auto bodyB = contact.getShapeB()->getBody(); // 衝突した物体がコインかどうかを調べる if ("coin" == bodyA->getName() || "coin" == bodyB->getName()) { // コインなら効果音を鳴らす CocosDenshion::SimpleAudioEngine::getInstance()->playEffect("se/coin_contact.wav"); } return true; }
onContactBegin(PhysicsContact& contact)は、2つの物体が衝突した際に呼ばれます。
引数contactにより、衝突した2つの物体を取得することができますが、getShapeA()かgetShapeB()がコインかどうかであることと、コインが接触した場合どちらがコインかがわかりません。コインに名前を付けたのはこの判断をするためです。
名前を取得してコインなら効果音を鳴らすことで、コイン接触音を適切なタイミングで鳴らすことができるようになります。
※音が小さいかもしれません。
ソースコードはこちら
次回はユーザビリティを向上のためにスプライトアクションを実装します。
]]>