7でSpriteKitという新しいゲーム作成用のフレームワークがでました。コレを使ってゲームを作って一攫千金を狙いたいと思います。勢いだけで書いてしまったので、大分雑な感じになっていますが悪しからず。細かい部分はそのうち直していきます。2Dとかなり似てます。2Dと比較した場合のSpriteKitのメリットとしては、Apple純正だから多分今後のOS対応も安心?インストールの必要がないので手軽UIKitの延長的な感覚で使える物理エンジン使った時のコードもObj-Cで書けるので読みやすいといったような所でしょうか。10.8以前では動かない」というデメリットもありますが、まあなにはともあれ、使ってみましょう。2D使ってる人がSpriteKitに乗り換えるメリットはどれくらいあるかわかりませんがなにをつくろう?2012年のパズドラの大ヒット、そして2013年現在は艦これが話題です。いうわけで、この二つのゲームの良い部分を組み合わせたゲームシステム、はえたたきを作ろう。Gameテンプレートを使ってプロジェクトを作るのが簡単です。プロジェクト名等は適当でOK。そのままビルドしてみましょう。World!という文字列だけが表示されていて、画面をタップするごとに回転する飛行機が増えていきます。テンプレートを読むでは、このテンプレートの中身を読んでいきましょう。プロジェクト内にはいくつかファイルがありますが、今回SpriteKitの使い方を理解する上では(Prefix)MySceneクラスだけを読めばOKです。Spriteの2つについて知っておく必要があります。古くはFlash等でも登場している一般的な概念なので、既にそれらのツールを使ったことがある人であればお馴染みだと思います。Sceneはその名の通り、ゲーム内の場面毎に一つ用意する要素で、「オープニング用にOpeningSceneクラス、バトル画面用にBattleSceneクラス」といったような作り方をします。Spriteは「主人公」「敵キャラA」「敵キャラB」「背景画像」...といった各Sceneに登場する「絵」の単位です。テンプレートのゲームには、(Prefix)MySceneというシーンがあり、画面をタッチする毎に飛行機のSpriteが追加されます。画面がタッチされた時にスプライトを追加している処理を見てみましょう。touchesBegan:の中でタッチされた位置にスプライトを追加している部分だけを抽出してコメントします。addChild:sprite];スプライトの画像と場所を指定し、シーンに追加しているだけですね。アクションはくるくる回るアニメーションの設定なのですが、一旦ここでは存在を無視しましょう。location.y);なお、locationの値をログに出してみるとわかりますが、座標系は左下が原点(0,0)で右に行くほどx値が、上に行くほどy値が大きくなる独特な座標系になっています。UIKitの座標系(画面左上が原点で、下に行くほどy値が大きくなる)ではないので、注意しましょう。※y軸が逆転しているのは、三角関数を使った際に余計な計算をしなくてよいためだと思われます。*/}シーンクラスには、update:メソッドが定義されています。Gameテンプレートを使ったプロジェクトでは、最大60FPS(1秒間に60フレーム)で画面の更新が行われます。画面更新の直前にはupdate:メソッドが呼ばれるので、必要に応じてこの中でゲームロジックの処理やアニメーションの処理を行います。テンプレート内では特になにもしていないので空になっています。スプライトとupdate:に慣れるテンプレートの内容を改造して、スプライトとupdate:の扱いに慣れましょう。画面がタッチされる度にスプライトを追加する処理をやめて、代わりにシーンが初期化されるタイミングで一つスプライトを追加するようにします。100)の位置に出してみましょう。initWithSize:メソッド内には画面上にテキストを描画するコードが既にありますが、そこは消してしまいます。背景色を指定している部分は残しておきます。yScaleプロパティ、回転を反時計回りのラジアン値で指定するzRotationプロパティがあります。試しに、サイズを1/10・向きを右向きにしてみましょう。addChild:sprite];では、update:に慣れるために1秒間に10pxずつこのスプライトを動かせるようにしていきましょう。update:メソッド内でスプライトのpositionを操作できるように、スプライトをクラスのインスタンス変数mySpriteとして保持するようにしておきます。self;}そして、update:メソッド内でmySpriteのpositionを変化させていきます。ここに最初の難関、というか不便な点があります。上述した通り、このプロジェクトは最大60FPSで画面の更新が行われますが、動作させてみて画面下部のFPSを見ればわかる通り、実際にはFPSは処理の内容・デバイスの性能によって変動します。moveAmount);}1秒間に60FPS呼ばれる前提でこのようなコードを書いてしまうと、遅い端末で30FPSしかでないような場合には半分の5pxしか動かない状態になってしまいます。経過時間分だけ移動する、というのが正しい実装になります。いうわけかSpriteKitには前回画面更新時からの経過時間を取得するメソッドが用意されていないようです。(同じAppleが出しているGLKit、あるいはサードパーティのCocos2dにはそういったメソッドが用意されているのに)従って、前回更新時からの経過時間を自分で計算しなければなりません。update:メソッドには「システム時刻」が引数で渡されるようになっています。(なぜ経過時刻ではなくシステム時刻にしたのか、実に謎ですが)。面倒ですが、前回更新時のシステム時刻を保存しておき、現在のシステム時刻を比較して経過時刻を取得するようにします。moveAmount);}正直、ここはイケてないですが、実行してみてとりあえずスプライトが一定スピードで上に上がっていればOK。等速で回転させてみる(アクションを使わずに)zRotationプロパティを変化させて1秒に反時計回りに1回転するように実装してみましょう。angularAmount;}スプライトを進行方向に動かす現在の実装では、スプライトは回転しながら上方向に進みますが、これを進行方向に向かって移動ようにします。sinを使って求めることができます。1.57)を追加した値にしなければいけません。移動・回転のスピードを調整して、スプライトが円を描きながら進行方向に動くようにします。moveAmountY);さてこの実装、実はFPSが安定しないと角度が変わるタイミングがずれるので円の中心がずれてきれいな円を描けないという問題があるのですが、厳密にやろうとすると面倒な上、今回のゲーム上はさほど問題ではないので放置します。スプライトのタッチを検出スプライトおよびupdate:の使い方がわかったところで、次はタッチイベントの処理です。標的(はえ)が叩かれたら点数を加えて、その標的を消す、そういう処理です。生のOpenGLで自前で画面を描画してるような場合、ビューでタッチされた座標と画面上に描画した要素の座標を比較してタッチされたかどうかを判定する必要がありますが、SpriteKitではスプライトがタッチされたかどうかはフレームワーク側で勝手に判断して処理してくれます。従って、UIViewにタッチイベントを実装するのと同じ要領でスプライトにtouchesBegan:...等を実装するだけで、スプライトのタッチを検出できます。標的のサブクラス化やり方は完全にUIViewにタッチイベントを実装するのと同じ方法です。SKSpriteNodeクラスを継承したHTHaeクラスを作り、中にtouchesBegan:メソッドを定義します。NSLog(@"はえたたかれた");}@endシーン側の初期化コードでもHTHaeクラスのインスタンスを作るように変えます。self;}最後に、追加したspriteがタッチイベントに反応することを示すため、userInteractionEnabledをYESにします。YES;実行してみて、スプライトをタップしたらログが表示されるか確認してください。シーンへの通知シーン側で点数を数えるには、標的がタップされたことをシーン側で検知する(標的側からコールバックしてやる)必要があります。一般的なデリゲートの仕組みを使って通知しましょう。delegate;@endさらに、実装側のコードも変更して、touchesBegan:が呼ばれたらデリゲートのhaeTouched:を呼んでやるようにします。}}@endシーンがHTHaeDelegateに準拠するようにして、標的のインスタンス生成時にdelegateをシーンにしてやります。シーン");}実行してログを確認。タッチされる毎に点数をカウントしていく点数をシーン側で管理し、標的がタッチされる毎に点数を増やしていきましょう。point);}叩かれたら場所を移動する叩かれたら、標的を他の場所に移動させます。インスタンスの場所を画面内のランダムな地点に飛ばすようにします。本来は画面の外側に飛ばした方が自然ですが、動作確認がわかりにくいので一旦画面内に飛ばします。self.size.height);}シーンの画面サイズはself.sizeで取得できるので、その範囲のランダムな箇所に飛ばします。乱数生成は初期化が面倒なのでarc4random()を使ってますが、rand()でもかまいません。標的の数を増やすリファクタリングここで一度コードを整理しておきます。update:内で標的を動かすコードは、HTHaeクラスにmove:メソッドとして定義しましょう。その際引数で前回更新からの経過時間を渡してやります。moveAmountY);}@endシーン側からはmove:メソッドを呼び出します。move:timeSinceLastUpdate];}@endシーンが少し読みやすくなりました。標的の数を増やす標的をNSArrayで管理して、複数表示できるようにします。インスタンスをNSArrayで管理する変更だけで、特にSpriteKit固有の話はないので、結果だけ表示。}}@end標的の動きをランダムにする円運動を繰り返すだけではなんの面白みもないので、適当なタイミングで回転の方向が変わるようにしてみましょう。インスタンス毎の回転速度(angularSpeed)を定数ではなくプロパティにします。...}@endシーン側でインスタンスを生成する際に、インスタンス毎に値がランダムになるようにしましょう。3.14f;...標的がランダムな方向に回るようになりましたが、相変わらず円運動なので、さらにupdate:メソッド内で一定の割合で方向転換を行うように実装します。}}画面外に行ったときの処理さて、このままプログラムを動かし続けると、標的はやがて画面の外側に消えていき最終的には二度と戻ってこなくなります。それは少し困るので、update:メソッドの中で標的が画面の中にいるかチェックして、画面の外にいったら画面の反対側にワープさせます。}画面の端にいった瞬間にすぐにワープさせてしまうと違和感があるので、適当にマージンを与えてやった方が自然になります。初期位置の最適化ゲーム開始時に標的がでてくる位置をランダムにしましょう。標的を叩いた後に移動する先を画面の外側にする画面内のランダムな場所に突然出現するのは違和感があるので、画面の外側に移動するようにしましょう。ポイントを表示するポイントを画面上に表示させてみます。OpenGLを使ってゲームを作った経験がある方はおわかりだと思いますが、文字の描画はOpenGL自体に機能が提供されておらず、自前で実装するのが面倒くさい実装の一つですが、幸いSpriteKitには簡単に画面上に文字描画を行うための機能が用意されています。それが、プロジェクトの最初のタイミングでテンプレートから消してしまったSKLabelNodeというやつです。名前から予想がつくかもしれませんが、SKSpriteNodeと同じ「ノード」というやつで、SKSpriteNodeと同じ感覚で使えます。SKSpriteNodeがイメージを表示するためのノードであるのに対して、SKLabelNodeはUILabelのように、文字を表示するためのノードです。self;}SKLabelNodeはシーンへの追加さえしてしまえば、後はUILabelと同じ感覚で使えます。point];}制限時間の実装もはやここらへんはSpriteKitが関係なくなってくるので、SpriteKitが関係ない部分は説明を端折っていきます。残り時間の表示update:内でゲーム開始からの経過時間を記録していき、残り時間をログに表示してみます。...}動作が問題なさそうであれば、ポイントラベル同様の手順でSKLabelNodeを追加し、そこに残り時間を表示しましょう。over!と表示しましょう。...}@end制限時間すぎたら叩けないようにするそろそろ疲れてきた。俺の集中力の制限時間も近い。...}リトライボタンを作る最後です。制限時間が終わったら、画面上にリトライボタンを表示し、クリックされたらゲーム経過時間・ポイントをクリアして、ゲームをリスタートできるようにしましょう。リトライボタンが必要です。タッチされたらシーンに「リトライ」を通知する必要があるので、標的のHTHaeクラスと同様にノードのサブクラスを作ります。「リトライ」の画像を用意すれば、HTHaeクラスと同様にSKSpriteNodeのサブクラスでも良いですが、面倒であればSKLabelNodeのサブクラスで実装してもOK。delegate;@endデリゲート、通知センター、コールバックブロックどの形でも良いのですが、今回もデリゲートで。}}@endリトライボタンは制限時間をすぎてゲームオーバーになっている時のみ表示したいので、initWithSize:内ではインスタンス化はするけどシーンには追加しないでおきます。ここではaddしない。self;}@endゲームオーバーになったタイミングでシーンに追加しましょう。ゲームオーバーになったタイミングで一度だけ処理するのは少し面倒ですが、前回更新時にゲームオーバーだったかどうか(時間が残っていたかどうか)を覚えておくことで実現できます。...}...@endリトライボタンがおされたら、制限時間とポイントをリセットします。リトライボタン自身はremoveFromParentメソッドを呼ぶことで、シーンから削除できます。removeFromParent];}おわりどうでしょうか、この文章が読まれている頃、僕は億万長者になっているでしょうか。アクションだったり物理エンジンだったり、まだまだ学ぶべきことはたくさんありますが、なんだか口内炎が痛いので今回はここら辺で終わりにしておこうと思います。気が向いたら続き書きます。コメント失礼いたします。素人ですがiPhoneアプリでRPG、ボードゲーム等を作りたく、cocos2dをはじめようと思ったのですが、spritekitの存在を知りました。PMすごく分りやすかったです。ありがとうございます。次に、具体的にどうやったかを書きます。今回はBluetoothタグを三つ用意し、それぞれからRSSIを取得して距離を推定、その距離からスマートフォンのいる方向の推定を試みました。それを表示するために、Spritekitを利用し、こちらを参考にしました。画面はSetUp画面とMap画面の二とし、SetUp画面では、それぞれのタグとの接続・切断からアラートとバッテリの確認をします。Map画面はSpritekitでスマートフォンの位置と方向を表示することを目的とします。BLEタグを用います。値段が安かったのと、3色あったのでわかりやすかったという理由で選びました。本来はiBeacon端末で試したかったのですが、まだちょっと値段が高いので今回は保留しました。gitでクローンしてきたコードが理解出来るようになれば、楽に0から作れるようになると思います。コメントをキャンセルメールアドレスが公開されることはありません。*ウェブサイトご意見、ご感想をお願いします。その2ARを学ぶ10連休。折り返し地点までの記録。