雑貨屋でこんな商品を見つけて買ってきた。CABLE BITEというiPhoneの純正ライトニングケーブルに取り付ける装飾アイテム。何がいいかっていうとキャラクターがシマリスということ。うちはシマリスを飼っているしすっかりシマリス派だ…といいたいけど相変わらずカエルももちろん好き。
同じシリーズにカエルもあるらしいが売ってなかった。ネットにはあるけど500円程度のものに送料がかかるのはちょっと…ということで売ってる店舗を探しているところ。
純正のライトニングケーブルに取り付けるとこんな感じ。iPhoneの充電ケーブルは付け根のこの部分がとても貧弱なので、断線防止の意味でもいいかもしれない。ベトナムでiPhone充電ケーブルの断線が原因での火災、死亡事故も報告されている(充電ケーブルが正規か非正規かはわかっていない)ので、断線には気を使っていて損は無いはずだし、それがシマリスならなおよし。
iPhoneにつなげたところ。割れているのはシマリスが噛んだから…だと思う。
どうでもいいけどこのシリーズ結構売れてるのかもしれない。ネットで種類を見るとシリーズが増えていて、雑貨屋に売っていた量の3倍ぐらい種類がある気がする。
ではでは
]]>こんにちは。
8/19~8/28で開催しているりんくうプレミアムアウトレットのバーゲンに行ってきました。
アウトレットのバーゲンは安くていいですよね。
この時期まだ暑いから夏服を買うか、ちょっと我慢したら秋だから秋服を買うか迷いますね。でも秋って意外と短くてすぐに冬になるせいか着もしない服が溜まっていくんですよねぇ。
そんなこんなでバーゲン品を見まわっていたら、いるわいるわポケモンGOをしている人達。
ポケモンGOは配信から約1ヶ月経ちましたが、配信日から1週間ぐらいは家の近くの公園ですらわらわら人が賑わっていたのですが、さすがに最近ではほとんど見なくなったので、かなりユーザーは減ったと思っていました。でもいっぱい居たんです。りんくうプレミアムアウトレットに。
結局みんなポケモンがいっぱい捕まえれるところで起動するようになったのかもしれません。鳥さん虫さんしかいない家の近くで起動するのが面倒で、私も家の近くではほぼ起動しないですしね。バッテリーめっちゃ食うし。
ルアーモジュールも途切れなく使用されていて、もしかしたら親がルアーモジュール使って子供を30分遊ばせつつ服を見てるんじゃないかと思った次第です。
可愛い系のポケモンだらけでした。
ちなみに私はレベル18くらいのライトユーザーです。最近周りのジムが普通にCP2000越えでツライです。
そんなわけで、老若男女誰もがやってる社会現象的な状態からは脱したような気がしますが、まだまだポケモンGOが熱いことがわかりました。
※バーゲンは夏服ばっかり買いました。正直暑すぎて秋服に目がいかなかった。
ではでは
]]>
Cocos2d-x 10円ゲーム講座 第13回、デモ画面を実装します。
これまででゲーム部分は完成しています。デモ画面は蛇足といっても過言ではありません(笑)。
タイトルを作ってシーン遷移、というのがチュートリアル的にはシーン遷移を学べて良いかもしれません。しかし、10円ゲームにタイトルからのシーンチェンジは不要でしょう。コインを入れてサクッとスタート。現実世界でもサクサク感は10円ゲームの醍醐味の1つだと思います。
私の思い出ですが、駄菓子屋に置いてあるゲーム機のデモ画面が好きでした。今でもそうですが、筐体機のデモ画面って非常に楽しそうな感じがしますよね。お菓子にするか、ゲームをするか……どれにお金を使おうか迷ってた子供時代、デモ画面は効果的な宣伝だったように思います。
そんなわけで、10円ゲームにコインを入れるまではデモ画面にしてみます。現実世界の10円ゲーム機のデモプレイはありえませんけどね。
前置きが長くなりましたが、10円ゲームのデモ画面というのを考えてみます。
こんな感じでしょうか。
デモプレイ画面であることは、このDEMO PLAY文字を明滅させることにします。
説明する部分は少ないのでコードを載せていきます。
class OneCoinGame : public cocos2d::Layer { public: // DEMO画面用update void demoUpdate(float delta); private: // デモモード bool _isDemo; cocos2d::Sprite *_demoSprite; };
デモ画面用のスケジュール関数と変数の定義です。
// デモモードかどうか _isDemo = false; // カスタム定期処理の開始(5.0秒毎の呼び出し) this->schedule(schedule_selector(OneCoinGame::demoUpdate), 5.0f);
コインの監視の際に説明したscheduleUpdate()メソッドはフレーム毎にupdate(float delta)を繰り返す呼び出す処理と決まっていましたが、デモ画面用のスケジュール処理は毎フレーム呼び出す必要はありません。そういった場合にschedule()を使用することで任意のメソッドを任意の間隔で呼び出す処理が可能です。今回は5.0秒毎に1回demoUpdate()を呼び出します。
// デモモード時の更新処理 void OneCoinGame::demoUpdate(float delta) { // デモ状態かどうか if (!_isDemo) { return; } // coinの状態を確認 // Enableがfalseなら、コイン投入処理 if (!_coin->getPhysicsBody()->isEnabled()) { this->coinEntry(); } // コインがレバー位置にあるなら、レバー処理 for (int i = 0; i < 4; i++) { // 範囲内ならコインにランダムな力を加える if (leverArea[i].containsPoint(_coin->getPosition())) { auto power = CCRANDOM_0_1(); if (i == 0 || i == 2) { this->applyForceCoin(1.0f, power); _lever[i]->setRotation(power * -90); _lever[i]->runAction(RotateTo::create(0.2, 0.0f)); } else { this->applyForceCoin(-1.0f, power); _lever[i]->setRotation(power * 90); _lever[i]->runAction(RotateTo::create(0.2, 0.0f)); } // 効果音を鳴らす CocosDenshion::SimpleAudioEngine::getInstance()->playEffect("se/lever.wav"); } } }
CCRANDOM_0_1()はCocos2d-xでランダムを扱う場合に便利なマクロです。0~1までのfloat値を返してくれます。その他の説明は特に新しいことをしていないので省きます。
DEMO PLAYの明滅は、フェードイン・アウトの繰り返し処理で実現しています。Blinkを使わない理由としては、Blinkの点滅処理はbVisibleを操作するため、ボタンを押した際のsetVisibile処理と不整合が出るためです。ちなみにFadeIn, FadeOutはopacityを操作します。
デモプレイ。どうでしょうか。ソースコードはこちら
私はこういう勝手に操作しているのを眺めるのは大好きです。ランダムに力を加えているだけですが、見ていると結構楽しいですよね。
]]>Cocos2d-x 10円ゲーム講座 第12回、パーティクルを使ってエフェクトを発生させます。
10円ゲームにパーティクルを使ってエフェクト処理を施してみます。
パーティクルとは粒子のことで、小さな画像を多量に使い、ひとつひとつに動きをつけることでエフェクトを表現するものです。これはもう実際にパーティクルを作成するエディタを触ってもらった方が早いと思います。
有名なパーティクルエディタ。パーティクルエディタはいくつかありますが、こちらはフリーで使えるのでオススメです。
ササッとあれこれ弄ってパーティクルが出来たら、
出力はExport → COCOS2DXをクリックすればparticle_texture.plist(デフォルトファイル名)がダウンロードできます。
今回作成したパーティクルはこちら。コイン投入時に火花を散らすようなシンプルなエフェクトです。
Cocos2d-xではパーティクルのplistをそのまま読み込んで使用することができます。
// パーティクル処理 auto particle = ParticleSystemQuad::create("particle/particle_texture.plist"); particle->setPosition(Vec2(visibleSize.width*0.7, visibleSize.height*0.9)); particle->setAutoRemoveOnFinish(true); // パーティクルが終了したらNodeを削除する this->addChild(particle, 3);
パーティクルを使うにはParticleSystemQuadクラスを利用します。setAutoRemoveOnFinish(true)をしておけば、パーティクルが終了した際に自動で削除してくれるので便利です。
無限ループを設定しているパーティクルは当たり前ですが自動で削除されないので注意しましょう。
ソースコードはこちら
エフェクトの回でも書きましたが、10円ゲームはレトロさがウリでもあるので過度なエフェクトは不要な気もしますね。
次回はデモ画面の実装です。
]]>
Cocos2d-x 10円ゲーム講座 第11回、スプライトアクションを使ってユーザビリティを向上させる方法についです。
ゲームの骨格となる部分は完成しました。1つ問題点を上げるならボタンがボタンと解りづらいことです。つまりユーザビリティが悪いということです。
レバーは見た感じでわかるかもしれませんが、お金の投入口がボタンになっていることは、パッと見ただけではよくわからない可能性もあります。
スマートフォンの台頭で、マウスではなくタッチ操作が主流になってきました。マウスの場合は、マウスオーバーという手法によりボタンがボタンであることを明示できました。
しかし、タッチ操作ではマウスオーバーに相当する操作がありません。
ボタンを明示するよくある方法としてはグロウ処理を行うことです。ボタンを淡く光らせることで、ユーザーに装置であることを明示することができます。今回はボタン投入口とレバーにグロウ処理を施してみます。
用意したグロウ画像です。
グロウ画像をこれまで同様、Spriteクラスを使って表示します。
auto sprite = Sprite::create("coin_entry_glow.png"); sprite->setPosition(Vec2(visibleSize.width*0.7, visibleSize.height*0.9)); this->addChild(sprite, 2);
グロウ処理のためにSpriteにアクションを設定します。
// アクション auto blink = Blink::create(1.5f, 1); // 点滅 1.5秒で1回点滅 auto action = RepeatForever::create(blink); // blinkを永久に繰り返す sprite->runAction(action);
点滅を永久に繰り返すというアクションを設定します。
レバーも同様に設定します。
// レバーのglow処理 auto leverGlow = Sprite::create("lever_glow.png"); leverGlow->setAnchorPoint(Vec2(0.0f, 0.0f)); _lever[i]->addChild(leverGlow, 2); // 1.5秒に1回の点滅を永久に繰り返す leverGlow->runAction(RepeatForever::create(Blink::create(1.5f, 1)));
Cocos2d-xが使えるアクションの詳細についてはReferenceを見ると良いでしょう。
ソースコードはこちら
10円ゲームはレトロさがウリでもあるので、ボタンやレバーのグロウ処理はレトロさを損なう結果になりますが、ボタンとしての理解度は向上したと思います。
リアリティかユーザビリティか。どちらも良いに越したことはありませんが、ゲームとしてユーザビリティを重視する場面は多いと思います。
次回はパーティクルを使用します。
]]>
みなさん、ポケモンGOライフ楽しんでいますか?
私もそこそこレベルが上がって現在レベル16。廃レベルには程遠いですが、ぼちぼち頑張っています。
最近捕まえて嬉しかったポケモンはハクリュー。ミニリューじゃなくてハクリューの状態で捕まえることができました。ちなみにオリックスの応援に京セラドームに行ったところ、京セラドーム南口あたりで見つけました。
バタフリーも捕まえました。可愛いポケモンランキングでは上位に入るんじゃないでしょうか。
そんなポケモンGO満喫中に見つけたニュース
<ポケモンGO>ポケモン捕獲率などで不具合
「ポケモンGO」では、ポケモンを捕まえる際に「モンスターボール」を投げるが、ボールに入ったポケモンが脱走する確率が上がり、ボールの当たり方でもらえる経験値のボーナスが付加されないなどの問題が発生しているものとみられる。
引用:http://news.yahoo.co.jp/pickup/6210096
ポケモンに逃げられやすくなるという不具合。なんか最近ポケモンを捕まえにくいと思ってたのは不具合のせいだったんですね。
というかCP20くらいの鳥や虫、モサモサ(※)したのにも逃げられまくっていたのでなんなんだと思ってはいたのですが、自分のトレーナーレベルが上がったせいで捕まりにくくなったと勝手に納得していました。
※ポッポ、ビートル、コンパン
この件はもうモンスターボールを返してくれという声が各所で聞かれますが、私としては捕まりにくいからモンスターボールを溜めて、バッグを空けるためにキズぐすりを捨てまくったのでそっちを何とかして欲しいと思っている次第です。
ジムバトルをするとキズぐすりがいくらあっても足りない。しかもキズぐすりは現状ショップで買えないですしね。
私の最も強いポケモンはシャワーズのCP1055なんですよね。でも周りのジムのポケモンが強くてなかなか辛いところです。
CP2397とか強いよー。特にカビゴンとかナッシーとか辛いですね。カイリューとかはもう対戦する気が無くなるので対戦しません(笑)。
ジムポケモンのCPが手持ちよりある程度強くてもみんなでボコれば被ダメージが少なくてキズぐすりを節約できます。
ジムのシャワーズ(CP1531)をみんなのシャワーズ3体(平均CP1000くらい)でボコボコにするの図。最近このやり方を知りました。みんなでジムバトルを一斉にすれば一緒に攻撃できるんですね。
このみんなでボコる方法でそこそこCP高いジムも倒せるので、ジムを荒らしまわってポケコインをゲットできました。
でも4つが限界でした。後はもう倒してジムにポケモンを設置してはやられ、設置してはやられのイタチごっこです。そこがこのポケコイン取得の醍醐味でもありますね。倒されずに5匹配置できるか…それともみたいなドキドキ感。
ちなみにジムに弱いポケモンを配置して鍛えまくっていたらレベルが簡単に上がり、どんどんジムの高さが段々高くなっていきますが、レベル8とかで高くなりすぎたのかフィールドが地図のままというバグも確認できました。
まさかポケモンが捕まりにくいのが不具合のせいとは知りませんでしたが、モンスターボールを溜めてキズぐすりを捨てまくったのでジムバトルがあまり出来なくなってしまいました。ジムバトル楽しいので不具合直して…できればキズぐすり返して欲しいですね。
なんか凄かったポケストップ密集場所。大阪にある伝法大橋。橋をわたるだけでポケストップ7,8個とジムに出会えました。
とある人のポケモンGO外出装備。スマートフォンに充電器に電気ラケット。ポケモンGOやりはじめてもう1年分は蚊に刺された気がしますが、何故電気ラケットが必要かはわかっておりません。
ではでは。
]]>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()がコインかどうかであることと、コインが接触した場合どちらがコインかがわかりません。コインに名前を付けたのはこの判断をするためです。
名前を取得してコインなら効果音を鳴らすことで、コイン接触音を適切なタイミングで鳴らすことができるようになります。
※音が小さいかもしれません。
ソースコードはこちら
次回はユーザビリティを向上のためにスプライトアクションを実装します。
]]>
Cocos2d-x 10円ゲーム講座 第9回、はずれ穴とゴールの処理を追加します。
今回ははずれ穴とゴールの処理です。
現実の10円ゲームのはずれ穴は奥行きを持っていて、奥に10円玉が落ちることで10円ゲームの失敗となります。Cocos2d-xの物理エンジンで奥行きをもたせることは出来ないので、穴にコインが入ったかどうかをコインの位置で判定することにします。ゴールも同様です。
コインの位置を監視するには、Cocos2d-xのスケジュール機能を使うとフレーム毎にコインの位置を監視することができます。コインの位置の監視によってはずれ穴とゴール位置に来た事を検知します。
class OneCoinGame : public cocos2d::Layer { public: // スケジュール処理 void update(float delta); };
init()内でthis->scheduleUpdate();を実行します。
// スケジュール処理の開始 this->scheduleUpdate();
これでフレーム毎にupdate(float delta)が呼ばれるようになります。
続いてはupdate(float delta)の中身を実装していきます。
// 穴の矩形領域 Rect coinHole[] = { Rect(245, 430, 30, 30), Rect(67, 335, 30, 30), Rect(213, 335, 30, 30), Rect(260, 258, 30, 30), Rect(182, 136, 30, 30), Rect(61, 67, 30, 30), }; // ゴールの矩形領域 Rect goalHole = { Rect(117, 67, 30, 30), }; // フレーム毎の処理 void OneCoinGame::update(float delta) { ///////////////////////////// // コインがはずれ穴とゴールに入っている場合の処理 // コインの速度の取得 auto velocity = _coin->getPhysicsBody()->getVelocity(); float speed = velocity.getDistance(Vec2::ZERO); // コインの速度が一定速度以下かつコインの物理が有効でかどうか if (speed < 3.0f && _coin->getPhysicsBody()->isEnabled()) { // はずれ穴に入っているかどうかの確認 for (int i = 0; i < 6; i++) { if (coinHole[i].containsPoint(_coin->getPosition())) { // コインがはずれ穴に落ちた時の処理 // コインの物理動作を停止し、非表示にする _coin->getPhysicsBody()->setEnabled(false); _coin->setVisible(false); } } // ゴールに入っているかどうかの確認 if (goalHole.containsPoint(_coin->getPosition())) { // コインがゴールに到達した時の処理 // コインの物理動作を停止し、非表示にする _coin->getPhysicsBody()->setEnabled(false); _coin->setVisible(false); } } }
穴に触れた瞬間にコインが消えると、少し理不尽な感じが出るので、コインの速度を取得しコインの速度が遅い時だけ穴に落ちたことにします。これは実際の10円ゲームでは奥行きがあることを、2Dゲームで擬似的に表現するための処理です。コインの速度が速い時は穴を通り抜ける方が理不尽に感じないためです。
はずれ穴、ゴールどちらに到達してもコインは無くなることになるため、コインの物理動作を停止し非表示にします。
_coin->getPhysicsBody()->setEnabled(false); _coin->setVisible(false);
ゴールの場合はクリア報酬として景品を出します。ついでなので、この景品も剛体にして物理を適用してみます。
gift.png
景品。おもちゃ箱っぽいイメージです。
景品口の物理領域を実装。
// 景品口の物理領域 Vec2 vec[4] = { Vec2(202, 65), // 左上 Vec2(202, 23), // 左下 Vec2(301, 23), // 右下 Vec2(301, 65), // 右上 }; auto wall = Node::create(); wall->setPhysicsBody(PhysicsBody::createEdgeChain(vec, 4, PhysicsMaterial(1.0f, 0.5f, 0.2f))); wall->getPhysicsBody()->setDynamic(false); this->addChild(wall);
筐体と同じように実装します。
続いて景品を出す処理をゴールに到達した際に追加します。
// 景品を出す処理 auto gift = Sprite::create("gift.png"); gift->setPosition(Vec2(250.0f, 74.0f)); this->addChild(gift, 1); // 景品の物理設定 auto giftPhysics = PhysicsBody::createBox(Size(26.0f, 14.0f), PhysicsMaterial(0.6f, 0.3f, 6.0f)); giftPhysics->setMass(1.0f); // 重さ giftPhysics->setMoment(10.0f); // モーメント(大きいほど回転しにくい) gift->setPhysicsBody(giftPhysics);
景品口の物理設定を作り、少し上から景品を出現させて落とします。
これを実行してみると問題があることがわかります。
景品が筐体の衝突領域に接触し、景品口に出てくれません。
ゲームで物理物体を扱っていると、ある物体とは衝突して欲しいけど、こっちの物体とは衝突してほしくない、といったことが出てきます。その解決方法として、Cocos2d-xでは物体にカテゴリーやマスクが設定できます。
今回は景品をコインや筐体と接触しないようにカテゴリ設定を行います。
剛体をカテゴライズするにはsetCategoryBitmask()を使用します。また、setContactTestBitmask()で剛体の衝突するカテゴリを指定できます。
コインとレールはそれぞれ衝突するようにカテゴリを指定します。
coinPhysics->setCategoryBitmask(0x00000001); // 物体のカテゴリ coinPhysics->setCollisionBitmask(0x00000001); // 接触する物体のカテゴリを指定
wall->getPhysicsBody()->setCategoryBitmask(0x00000001); // 物体のカテゴリ wall->getPhysicsBody()->setCollisionBitmask(0x00000001); // 接触する物体のカテゴリを指定
rail->getPhysicsBody()->setCategoryBitmask(0x00000001); // 物体のカテゴリ rail->getPhysicsBody()->setCollisionBitmask(0x00000001); // 接触する物体のカテゴリを指定
コイン、レール、筐体のカテゴリを1(0x00000001)に設定し、接触する物体のカテゴリを同じく1(0x00000001)に設定します。これでコインとレール、筐体はそれぞれ接触することになります。動作としては今まで通りです。
次に景品の設定です。景品と景品口は接触して欲しいが、コインやレールとは接触して欲しくないので、コインやレールとは別のカテゴリである2(0x00000010)を設定します。
wall->getPhysicsBody()->setCategoryBitmask(0x00000010); // 物体のカテゴリ wall->getPhysicsBody()->setCollisionBitmask(0x00000010); // 接触する物体のカテゴリを指定
giftPhysics->setCategoryBitmask(0x00000010); // 物体のカテゴリ giftPhysics->setCollisionBitmask(0x00000010); // 接触する物体のカテゴリを指定
これで物体の衝突を制御できました。
ことが確認できます。
ゴールに導くのが難しくて動画撮れませんでした(笑)。内部ではテスト用のコインを作成して確認しています。
ソースコードはこちら
次回は効果音を鳴らすです。
]]>ポケモンGOやってますかー。
近くの公園はポケストップが3箇所もある大きな公園なのですが、全てにルアーモジュールを
使ったらわらわらと人が集まってきてポケモンGOの反響の凄さをあらためて確認するとともに少し笑えました。
まぁ他の人がルアーモジュール使ってたら自分も向かったりするので、ホイホイ人が釣れてるなぁと思った次第です。でも近くの公園、ポッポとコラッタくらいしかいない…。
ポケモンGOをしてたら散歩の機会が増えます。そんなこんなでたまご孵化、2kmのたまごから歩いていたらピカチュウが!!
現在50匹程捕まえましたが、ピカチュウだけアニメ版みたいにピカチュウって鳴くんですね。
他は初代ゲームボーイで聞いたような電子音全開の鳴き声なんですが。
これはこれで懐かしいというか、当時マリオスタジアム(テレビ番組)で鳴き声を聞いて何のポケモンかを当てるクイズで、答えられる子供がいっぱいいたのを思い出しました。
卵孵化でピカチュウが出たことが嬉しくて、ムゲンふかそうち以外のふかそうちも使って一気に卵を孵化させようとしました。これが後々の悲劇を招くことに…。
この、おや?の画面でフリーズ。
フリーズしてどうしようも無いので、仕方なくアプリを再起動。するとたまごが無くなり、ふかしたポケモンもいない…。
しかも、5kmのたまごをふかそうちを使って一気にふかさせようとしたので、3つのたまごが全て無くなりました。
「おや?」じゃねーよハゲw
せっかく歩いたのが無くなるのは寂しいので、一応問い合わせメールを送ってみました。
不具合のご報告を受付けいたしました。 調査するにあたり追加情報が必要になった場合は、別途メールでご連絡させていただきます。 すでにこちら側で認識している問題については、Pokémon GOの公式ページ上の【既知の問題】で報告されている場合がございます。あわせてご確認いただけますと幸いです。 現在発生している症状によってPokémon GOが正常にプレイできない場合、以下の手順にてアプリの再インストールをお試しください。 一般的に、アプリの再インストールにより多くの問題が解決できる場合がございます。 【Andorid】 -ホーム画面より設定>アプリケーションマネージャー>Pokémon GO>強制終了>データの消去 【iOS】 -ホーム画面より設定>プライバシー>位置情報>Pokémon GO をOFFに設定 -アプリのアンインストール→インストール -再起動 なお、ゲームのデータはオンライン上で保存されており、上記のトラブルシューティングによるゲームデータの影響はございません。 また、本件についてさらなる調査が必要な場合を除いて個別に返信することはしておりませんので、何卒ご了承ください。 私たちは、すべてのユーザーの報告に目を通しており、あなたの報告は今後のPokémon GOの改善に活かさせれます。今回はご協力いただきありがとうございます。 -NianticOps
返ってきたのは自動返信メール。
ふかそうちに関しては有料アイテムでもあるので、バグで孵化しない問題に対応してくれないとなると購入し辛いアイテムですよね。
今回の件で5kmのたまごを3つ同時にふかそうちに入れてしまい、同時に3つ消失してしまいました。これは耐障害性の観点からダメだと痛感したところです。ふかそうちを使う場合は、それぞれ100メートル以上はずらし、同時にふかしないようにしましょう。
これなら消失も最悪1つで防げるはずです。
ところで、ウィロー博士が人気でイラストやら大喜利やら色々やられてるみたいですね。
自分もこんなのを。
ではでは。
]]>Cocos2d-x 10円ゲーム講座 第8回、レバー操作とコインをはじく処理を追加します。
今回は10円ゲームのキモ、レバー操作の実装です。
レバーを引いて離すと、引いた分に応じて力強くコインをはじきます。
ドラッグ処理でレバーのはじく強さを実現します。
レバーをタッチすることでレバー操作を開始、ドラッグすることでレバーの力強さを決定します。タッチを離した時にコインをはじく処理を行います。ドラッグ中はレバーをアニメーションさせます。
使用する画像はこちら。
lever.png
lever_background.png
レバーは、スティック部分とレバー背景の穴の画像を使います。
画像が用意できたら実装していきます。レバーははじく際にアニメーションさせるので、メンバ変数にします。その他タッチした際に使用する変数も定義しておきます。
class OneCoinGame : public cocos2d::Layer { private: cocos2d::Sprite *_lever[4]; // レバー // レバータッチ処理用 bool _isTouchLever; // レバーのタッチ状態 int _touchLeverNumber; // レバーのナンバー cocos2d::Vec2 _touchlocation; // タッチしたポイント };
// レバー関連初期処理 _isTouchLever = false; _touchLeverNumber = 0; _touchlocation = Vec2(0.0f, 0.0f);
特に変わったことはしていません。
レバーの表示処理
// レバー位置設定 Vec2 vec[4] = { Vec2(45, 400), Vec2(315, 321), Vec2(45, 255), Vec2(315, 160), }; for (int i = 0; i < 4; i++) { // レバーの背景 auto leverBackground = Sprite::create("lever_background.png"); leverBackground->setPosition(vec[i]); leverBackground->setAnchorPoint(Vec2(0.5f, 0.28f)); this->addChild(leverBackground, 2); // レバー部分 _lever[i] = Sprite::create("lever.png"); _lever[i]->setPosition(vec[i]); _lever[i]->setAnchorPoint(Vec2(0.5f, 0.28f)); this->addChild(_lever[i], 2); }
いつも通りSpriteクラスを使って画像を描画しているだけですが、1点だけポイントがあります。それがsetAnchorPoint()です。setAnchorPoint()は画像の基準となる点で、回転させる際に軸となる点にもなります。
AnchorPointを軸に回転する
レバーの棒はレバー穴を軸に回転して欲しいので、Vec2(0.5f, 0.28f)を設定しています。
レバーにタッチ処理を使うので、タッチイベントを受け取れるようにします。
タッチイベントには、onTouchBegan()、onTouchMoved()、onTouchEnded()があり、それぞれタッチ開始時、移動時、終了時に呼ばれます。
実際の操作と対応しているので、イメージしやすく直感的に理解できると思います。
シーンでタッチイベントを受け取るには、タッチイベントのクラスメソッドを定義します。
class OneCoinGame : public cocos2d::Layer { public: // タッチ処理 bool onTouchBegan(cocos2d::Touch* touch, cocos2d::Event* event); void onTouchMoved(cocos2d::Touch* touch, cocos2d::Event* event); void onTouchEnded(cocos2d::Touch* touch, cocos2d::Event* event); };
タッチイベントを受け取るためのリスナーイベントを登録します。
// タッチイベントを使用するための初期処理 // タッチに関するイベントリスナーを生成 auto listener = EventListenerTouchOneByOne::create(); listener->onTouchBegan = CC_CALLBACK_2(OneCoinGame::onTouchBegan, this); listener->onTouchMoved = CC_CALLBACK_2(OneCoinGame::onTouchMoved, this); listener->onTouchEnded = CC_CALLBACK_2(OneCoinGame::onTouchEnded, this); // タッチイベントをリスナーに登録 auto dip = Director::getInstance()->getEventDispatcher(); dip->addEventListenerWithSceneGraphPriority(listener, this);
これでこのシーンでタッチイベントを受け取れるようになりました。
レバーのタッチ処理を実装します。
// タッチ開始時の処理 bool OneCoinGame::onTouchBegan(cocos2d::Touch* touch, cocos2d::Event* event) { auto location = touch->getLocation(); bool isTouchLever = false; // タッチしたレバーの調査 for (int i = 0; i < 4; i++) { Rect rect = _lever[i]->getBoundingBox(); // タッチした点がレバー領域内かどうか if (rect.containsPoint(location)){ isTouchLever = true; // タッチしたレバーを記憶 _touchLeverNumber = i; // タッチした位置を記憶 _touchlocation = location; } } // レバーをタッチした場合、レバーのタッチ状態を設定する if (isTouchLever == true) { _isTouchLever = true; } else { _isTouchLever = false; } // return true; } // タッチ移動時の処理 void OneCoinGame::onTouchMoved(cocos2d::Touch* touch, cocos2d::Event* event) { auto location = touch->getLocation(); if (_isTouchLever) { float d = _touchlocation.getDistance(location); if (d >= 100.0f) { d = 100.0f; } // レバースプライトの更新 if (_touchLeverNumber == 0 || _touchLeverNumber == 2) { _lever[_touchLeverNumber]->setRotation(-90.0f * (d / 100.0f)); // 0 - 90 max } else { _lever[_touchLeverNumber]->setRotation(90.0f * (d / 100.0f)); // 0 - 90 max } } } // タッチ終了時の処理 void OneCoinGame::onTouchEnded(cocos2d::Touch* touch, cocos2d::Event* event) { auto location = touch->getLocation(); if (_isTouchLever) { float d = _touchlocation.getDistance(location); if (d >= 100.0f) { d = 100.0f; } // レバースプライトを初期位置に戻す _lever[_touchLeverNumber]->setRotation(0); // レバーのタッチ状態を解除 _isTouchLever = false; } }
レバーをタッチしたかどうかを判定するには、レバーの矩形とタッチした点の衝突判定の結果を調査します。
onTouchBegan()がタッチ開始時に呼ばれるイベントのため、onTouchBegan()でレバーがタッチされたかどうかを判定しています。
touch->getLocation()でタッチした点を取得できます。レバー画像側はSpriteクラスのgetBoundingBox()で画像の矩形領域が取得できるため、この矩形領域とタッチした点で衝突判定を行います。矩形領域と点の衝突判定には、矩形領域のクラスであるRectクラスのメソッドcontainsPoint()で判定することができます。
onTouchMoved()はドラッグ動作に対してのイベントです。onTouchMoved()でレバーのアニメーションを行い、レバーの引く処理を視覚的に表示しています。タッチを開始した点から、距離が離れるほどレバーを倒す(回転)する処理を入れます。
画像の回転処理は、SpriteクラスのメソッドsetRotation()で回転できます。
onTouchEnded()はタッチ終了時のイベントです。ここではonTouchMoved()で回転させたレバーを元に戻しています。
静止画では解りづらいですが、レバーがドラッグ処理で傾いています。
タッチイベントを利用してレバー操作が実現できました。次はタッチを離した際にコインをはじく処理を実装します。
class OneCoinGame : public cocos2d::Layer { public: // コインに力を加える void applyForceCoin(float vecx, float power); };
// コインに力学的な力を加える void OneCoinGame::applyForceCoin(float vecx, float power) { _coin->getPhysicsBody()->applyImpulse(Vec2(400.0f * power * vecx, 0.0f)); }
appliImpulse(Vec2 Impulse)で剛体(コイン)に対して瞬間的に力を加えることができます。
タッチを離した際に呼ばれるoneTouchEnded()にコインに力を加える処理を追加します。
// レバーの接触部分設定 Rect leverArea[4] = { Rect(45, 410, 30, 30), Rect(285, 330, 30, 30), Rect(45, 270, 30, 30), Rect(285, 170, 30, 30), }; // タッチ終了時の処理 void OneCoinGame::onTouchEnded(cocos2d::Touch* touch, cocos2d::Event* event) { auto location = touch->getLocation(); if (_isTouchLever) { float d = _touchlocation.getDistance(location); if (d >= 100.0f) { d = 100.0f; } // レバースプライトを初期位置に戻す _lever[_touchLeverNumber]->setRotation(0); // コインがレバー範囲にあるか確認 if (leverArea[_touchLeverNumber].containsPoint(_coin->getPosition())) { // 範囲内ならコインに力を加える if (_touchLeverNumber == 0 || _touchLeverNumber == 2) { this->applyForceCoin(1.0f, (d / 100.0f)); } else { this->applyForceCoin(-1.0f, (d / 100.0f)); } } // レバーのタッチ状態を解除 _isTouchLever = false; } }
レバーを動作させた際に、コインに逆方向への力を加えて動かします。
実際の10円ゲームは、レバーとハンマー機構によりコインを叩くわけですが、今回は擬似的に実装しています。ハンマー機構を作っても面白いですが、物理で動く部分が増えるとコインをはじく結果への不安定さが増えることになり、ゲームとして面白くなるか、運ゲーになるか微妙なところなので擬似的な方を採用しました。
レバーの領域を表示しています。この範囲にコインがあれば、レバー処理でコインに力を加える処理が働きます。
ここまでのソースコードはこちら
次回ははずれ穴とゴールの処理の追加です。
]]>