Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.
ゲームを作ってみた
2014.4.19
Cocoa勉強会 関西
大森智史
@oogon / satoshi.oomori
あんた、誰?
• と、いうわけで自己紹介。
• 大森智史といいます。
• 某印刷会社勤務。
• Cocoa勉強会関西は第0回からいます。
ゲーム
• ガッポガッポでウハウハらしい。
• 当然モテモテらしい。
• 作るしかない!
…でも作ったことない
• どうやって作るんだろうか
• Cocos-2Dとかあるけど
• Sprite Kitでしょ
参考資料
• Sprite Kit iPhone 2Dゲームプログラミング
• http://www.amazon.co.jp/dp/479804055X/
参考資料
• Sprite Kitもろもろ
• http://spritekit.jp
さて今日は
• 試行錯誤で作ってみましたので間違い・  
もっといい方法があるかもしれません。
はてなポイント
• スライドにこのマークが付いているところは
よくわかってないところです。
• 随時ツッコミをお願いします。
• 某社サービスとは無関係です。
?
どんなのを作るか
• シューティングゲーム
• 単純明快なもの
どんなのを作るか
• 宇宙船
• 空中戦
• 地上攻撃
• きれいなグラフィック
どんなのを作るか
• 中ボス登場
• 大ボス登場
• アンドアジェネシス!
参考資料
• ゼビウス
• http://ja.wikipedia.org/wiki/ゼビウス
• ゼビウス軍事パレード
• http://www22.tok2.com/home/takaakit/xevi/
parade.html
まずはサンプル
• Appleのサンプルを動かしてみる。
• とりあえずSKSpriteNode=キャラクタのよう
なのでそれを動かしてみれば良いみたいだ。
2014.2.15
• プロジェクト始動!
いろいろさわってみる
• サンプルをもとにスプライト画像をさしかえ
たり、アクションを変更したりしてみる。
重大な問題
• 絵が描けない。
• 描く道具がない。
• 困った。
Nintendo 3DS
• 「うごめも」で描いてみる。
• パラパラ漫画が描ける。
• 着色はPixelMetorで(Photoshopより安い)
?
参考資料
• Nintendo3DS
• http://www.nintendo.co.jp/3ds/
参考資料
• Nintendo3DS
• http://www.nintendo.co.jp/3ds/
参考資料
• うごくメモ帳3D
• http://www.nintendo.co.jp/3ds/eshop/jkzj/
index.html
とりあえず描いてみた
• うごメモからGIF出力
• 着色はPixelMetorで
• PNGファイルにする
次は敵キャラ
• 敵キャラも作る。
• まずは簡単な動きのキャラクタから。
• でも、鉄板くるくる回したい!
アトラス
• アトラス(SKTextureAtlas)っていうのでパ
ラパラ漫画にするといいようだ。
• パラパラ漫画なら「うごめも」!
?
アトラス
!
• テクスチャだけ
SKSpriteNode *obj = [SKSpriteNode spriteNodeWithImageNamed:@“Spaceship"];
• テクスチャアトラス
SKTextureAtlas *pla...
ちょっとしたTips
• 連番画像を使うとき@“%03d”とすると
001,002,003…とできます。
for (int i=0; i < numImages; i++) {
NSString *textureName =
[NSString...
アトラス
• テクスチャアトラスはRetina用と非Retina用
がいるんだそうだ。Retina用は”@2x”を追加
• ビルド設定
SPRITEKIT_TEXTURE_ATLAS_OUTPUT = YES
にしておかないといけない
• とり...
参考文献
• テクスチャアトラス
• http://www.raywenderlich.com/ja/51748/
sprite-kit-チュートリアル-アニメーションと
テクスチャ
衝突
• ぶつかったらやっぱり爆発するよね
• エミッターからパーティクルが飛んでいく
• パーティクルクラスっていうのはないので注
意が必要
エミッター
//エミッターの作成
NSString *path = [[NSBundle mainBundle]
          pathForResource:@"FireParticle" ofType:@"sks"];
static ...
パーティクル
• エミッターから飛んで行くもの
• パーティクルクラスはないよ!
• エミッタークラスを探せ!
?
とりあえず作って
動かしてみた
整理
• シーンにごにょごにょ書いていたので、見苦
しい。
• オブジェクト志向っぽく書こう
• SKSpriteNodeを使っていたけれど、キャラ
クタごとにサブクラス作成。自分のことは自
分でするように。 ?
• クラス分け
自機クラス
敵キャラクラス
シーンクラス
シーンクラス
!
!
!
!
!
!
!
!
!
自機の動き
敵キャラの動
き
タッチ処理等
タッチ処理等
枠の外
• キャラクタが枠の外に出たら消さないと。
• -(void)didSimulatePhysics ?
枠の外
• いや、ここはやはり時間が取れる     
-(void)update:(CFTimeInterval)currentTime 
を使うべきかと
• このメソッドが呼ばれた時、前回呼ばれた時
間との間隔を測り、一定時間が経っていたら
...
枠の外
• 「後日談」
• (今回は0.3秒間隔で)としていたが…
• →やはり0.015秒間隔で(1/60=0.0166..)
敵キャラの出現
• ランダムだと、パターン攻略本出せないよね。
• 一定の法則で敵キャラを出すには? ?
敵キャラ
• タイマー?
• さっき、一定時間ごとにチェックするように
したんだから、それを使えばいいやん
?
敵キャラ
• 出現時間をリストにして、時間が来たらキャ
ラクタを出すようにした。
• NSDictionaryをNSArrayで
• 将来的には別ファイルにして「面」構成に
敵キャラ
• 出現時間をリストにして、
NSArray *anArray =
@[@{@"name":@"teppan",@"time":@1.0f,@"posX":@100.0,@"pos
Y":@600.0},
@{@“name":@"te...
敵キャラ
• リストに基づいてキャラクタを作成するようにした。
//0.3秒間隔で調査
if ((beforeTime + 0.3f) < currentTime){
[enemyArray enumerateObjectsUsingBlock...
敵キャラ
• 毎回オブジェクトを作っているけど使い回し
したほうがいいのか?
?
敵キャラ
• 「後日談」
• できれば面の最初にオブジェクトを作っておく
ほうが良い。
• まあでも毎回作っても大丈夫だけどアトラスの
作成には時間がかかるので、最低限アトラスだ
けは先に作っておいて使いまわしする。ほうが
よい
?
• 敵キャラが規則
的に出現するよ
うになった
サウンド
• 音がないと寂しいよね。
• 効果音とBGM
重大な問題
• 作曲できない。
• 楽器も演奏できない。
• 困った…。
ガレージバンドで
• なんとかやってみる。
• 売るときはプロに頼めばいいか。
効果音
• NodeにActionとしてつけるとよいらしい。
?
EmitterNodeにAction
SKAction *soundPlay;
!
soundPlay = [SKAction playSoundFileNamed:@"bomb.m4a" waitForCompletion:NO];
!
//...
BGM
• AVFoundationを使うらしい
?
BGM
@property (nonatomic) AVAudioPlayer *
backgroundMusicPlayer;
!
//BGM
NSError *error;
NSURL * backgroundMusicURL = [[NS...
背景
• 背景も真っ暗だと寂しい。宇宙みたい。
• 地面の絵がほしいよね。
• ナスカの地上絵とか
どうするんだろう
• 小さなノードをブロック上に置く
• 大きな一枚絵のノードを作る
• どちらにしても、はみでる部分を継ぎ足し継
ぎ足し
• 今回は大きなノードを作ることに
?
?
• 背景を動かしてみた
速度は大丈夫か
• Air 11インチのシミュレータでは30fpsに落ち
るが、iPhone5s実機では60fpsを保ったまま
• 大丈夫みたい。 ?
背景画像を変えてみる
• ノードを作り直す
• ノードはそのままで、テクスチャ画像を変え
る
• →下のほうがコストが低そう ?
背景画像を変えてみる
• 「後日談」
• テクスチャ画像を変える方法で作ったが、微妙に「カ
クッ」と動く
• →結局最初に面の背景を全部作っておくことにしま
した。
• 途中で作り直すことにした。最初から常にパフォー
マンスを意識して制作しない...
背景もパラパラしたい
• atlasをたくさん作って切り替える ?
• パタパタと2種類の
背景を切り替えな
がら4枚の背景をつ
なげて動かしてみ
た。
砲弾
• 自機から砲弾が飛ぶようにしたい。
• またサブクラスを作って動作を定義
?
• 砲弾を発射してみた
当たったら終了
• 自機が爆発したら終了
• 別のシーンに切り替えたい。
• presentScene:transition:でよいようだ。
?
• 自機が破壊された
ら終了画面に行く
遅延移行
• 爆発後少し余韻を残すため、2秒後に
presentScene:を呼び出しています。
//シーンの遅延移行
-(void)delayPresentScene:(NSDictionary *)obj;
{
SKView * skVie...
参考文献
• Sprite Kitリファレンス
• シーン遷移等
• http://cocoaapi.hatenablog.com/entry/
SpriteKit/index
整理
• キャラクターが増えてきたので、共通部分を
抜き出し、キャラクタのベースクラスとなる
抽象クラスを作成
• 定期的なアップデート、衝突時の処理、範囲
外に出た場合など共通の処理を記載
• ベースクラス作成
キャラクタクラス
!
!
自機クラス
敵キャラクラス
自機クラス
敵キャラクラス
くるくる動く敵キャラ
• まっすぐ向かってくるだけではつまらないの
で、一定の法則で動くキャラを作る
• 動きはベジェパスで作ることができる。
• 速さはspeedで設定可能
パスで動きをつけるとは?
• パスは計算
式で曲線を
表すことが
出来ます。
→位置を計
算できる
• http://www.slideshare.net/oogon/cocoa201303pdf
?
• パスで敵キャラの
動きを作ってみた
でもね…
• パス作るのってなんだかわけがわからない。
• イラストレーターとかでSVG書き出し?
• ごもっとも!
モーションパスエディタ
• こんなのを作ってみました。
• 作っている最中ですが、デモ...
参考文献
• パスで動きを作る
• http://stackoverflow.com/questions/
19215223/spritenode-not-following-cgpath
砲弾が当たるように
• シーンで衝突が起こると、呼ばれるメソッド
//衝突検知
-(void)didBeginContact:(SKPhysicsContact *)contact
{
}
• このメソッドで衝突したオブジェクトがわか
るので衝...
砲弾が当たるように
• この処理も大きくなりがちなので、各オブジェ
クトに移していきたい。
• 当たり判定とか調
整してみてゲームっ
ぽく動く状態に
再スタート
• ゲームオーバーになったあと、もう一度ゲー
ムをするための処理を行う
• 一旦別のシーンに移ったあともう一度プレイ
シーンに戻るには?
• 別のシーンに移るときに、プレイシーンを保
持しておいて、プレイシーンに戻ればいい。
?
?
再スタート
• 移行先シーンにプロパティを設定して
@property (retain) SKScene* beforeScene;
!
• プレイシーンから移行するときに保持してお
く ?
ノードをボタンに
• 再スタートボタンはどうする?
SKLabelNode?
• スプライトノードを作ってボタンにしてもい
いか。
?
?
タッチしたノードは?
• タッチした部分にあるノードは何?か調べて
SKNode *nodeAtPoint = [self nodeAtPoint:[touch
locationInNode:self]];
• あとはノードの名前で処理を変える...
• 再スタートボタン
をつけてみた
参考資料
• SKLabelNodeをボタンにする
• http://spritekit.jp/tutorial/example/
参考資料
• チュートリアル
• http://www.raywenderlich.com/42699/
spritekit-tutorial-for-beginners
ゲームのやり直し
• 前のシーンの続きになっているので、ゲーム
を最初からやり直すには、ゲーム設定を戻す
ことが必要。
• いままで、シーンの初期化メソッドに書いて
いたのを-(void)startGameというメソッドに
まとめることに。
• ゲームのやり直し
スコア
• そうだ、スコアを残そう
• とりあえずラベルで。余裕ができたらスプラ
イトノードで数字を表示しよう!
• スコアの表示
実機で
• このあたりから実機で確認することに
• 音付きで
オープニング
• いきなりゲームが始まるのもおかしいので、
オープニングシーンを作ってみる。
• スコアシーンと同じ
• オープニング画面
自動運転
• テスト面倒。
• デモ画面では自機が自動的に操作されている。
• 自機を自動操縦させよう。
自動運転
• 敵キャラの発生と同じように時間が来たら指
定の操作をすればいいんではないか?
• 自動操縦画面
自動運転
• 物理シミュレーションは毎回同じ結果になる
とは限らないようだ。
• →と、当初考えていたけれど、初期化にかか
る時間の違いで少しのズレが発生していた。
初期化が終わってから開始時間を0にするこ
とで解消。
?
地上敵キャラ
• 今度は地上の敵キャラを作る
• また同じようにキャラクタクラスを作って…
地上敵キャラ
• ん?自機が地上敵キャラに隠れる時があるぞ。
そうか、あとから描画するほうが上へ上へと
くるのか
• 奥行きの順をコントロール
するにはどうするのだ?
レイヤー
• zPositionというプロパティを使うみたいだ。
数字が大きくなるほど手前に来る。
• デフォルトは1のようなので、地上物は3、空
中物は5、スコアは7ぐらいにしておこう。
?
• 上下関係を直してみた。
参考資料
• シーンを組み立てる
• http://spritekit.jp/tutorial/scene/
自由移動
• 今まで水平方向にしか動かなかったけれど前
後方向もうごかせるようにした。
• 自由に、とはいっても動くことのできる範囲
を限定するためのコードを追加
• 自由に動き回れる
ようになった
整理
• 当たり判定を、シーンでゴニョゴニョしてい
るので、オブジェクト指向的に処理したい。
• シーンで衝突時にSKPhysicsContactをそれぞ
れのオブジェクトに渡す。それぞれのオブジェ
クトで処理を行う。シーンでは判断しない。
整理
• ベースクラスに衝突時のメソッドを作る。
• サブクラスでそれぞれの処理をオーバーライ
ドする。
• キャラクタの独立性を高めて、キャラクタが
増えた時の対応をしやすくする
整理
• シーンでの-didBiginContact{}の処理がSKPhysicsContactオブ
ジェクトを渡すだけになった。
• ここで、ベースクラスOOOChatacterNodeを作った効果が出
てくる。
-(void)didBegi...
シーンクラス
!
-(void)didBeginContact:
(SKPhysicsContact *)contact
{
!
!
}
• 衝突時の処理
自機クラス
-(void)contact:
(SKPhysicsCon
tact *)c...
敵砲弾
• 敵キャラも砲弾を
敵砲弾
• 敵も砲弾を撃つ
いっしょに動く
• 地上攻撃のマーカーを作りたい。
• 自機と一緒に動くノードSKPhysicsJointFixed
で自機と接続
• ハマった所:シーンにすでに接続するものが
ないと例外発生
地上照準を表示
• 自機と一緒に動
くマーカーを表
示
もろもろ調整
• プレイしながら細かいところを修正する
画面が暗くならないよう
• デモプレイしているとタッチしないのでスリー
プする。
• スリープしないようにするにはデリゲートで
application.idleTimerDisabled = YES;として
やればよい。(ゲームが終了・バックグ...
参考資料
• iOSデバイスをスリープしないようにする
• http://program.station.ez-net.jp/special/
handbook/objective-c/uiapplication/
idletimer.asp
終了画面から
タイトル画面へ
• 再スタートボタンしかなかったので終了して
からタイトル画面に戻るボタンを付けた。
• 一定の時間で強制的にタイトルに戻るように
したほうがいいかもしれない。 ?
パフォーマンス向上
• 概ね60fpsで動いているが、背景画像の切替
時にもたつきが発生している。
• 画像差し替え時の読み込みに時間がかかって
いる?
• 事前にバックグラウンドで読み込んでおく?
?
パフォーマンス向上
• SKTextureAtlasの作成時に時間がかかっているようなので
事前に画像を読み込み、非同期読み込み。
-(void)preloadAtlas:(int)atlasNumber
{
dispatch_async(di...
パフォーマンス向上
• 回転する鉄板もサイズは大きくないが15枚つ
かっているので、atlas読み込み時に時間がか
かっている模様。
• 同じキャラクタでアトラスを使いまわしでき
るよう、キャッシュする。
?
パフォーマンス向上
• アトラスをキャッシュすることオブジェクト
の独立性が薄くなる懸念がある。
• メモリが許せば面に登場するキャラクタオブ
ジェクトを最初に全部作っておくこともいい
かも?
• →出来る限り最初に作成しておく!
?
パフォーマンス改善後
• アトラスをキャッ
シュすることで
もたつき感解消
地上攻撃
• 地上のターゲットを攻撃するときの処理を加
えた ?
地上ターゲットを攻撃
• 地上のターゲッ
トを攻撃する処
理を追加
敵キャラAI
• 敵キャラに、状況に応じて行動させる。
• 敵キャラと自機の距離が一定以下になったら
砲弾を発射
• SKActionで、位置チェック、敵キャラごとに
違う処理の実装可能 ?
敵キャラAI
• 敵キャラと自機の距離が200pt以下になった場合砲弾を発射する。
SKAction *attackAction = [SKAction customActionWithDuration:2.0 actionBlock:^(SK...
面構成
• ボスキャラを倒したら面クリアというのが定
番
• 速度重視のために最初にリソースを読み込ん
でおく事が必要なため、このパターンが生ま
れた。
?
面構成
• ユーザーの体調面を考慮して、休憩を挟むこ
とが必要かと。
• ボスキャラを倒す、他はある地点まで到達す
ることで面クリア。 ?
面クリア
• ボスキャラを倒
したら面クリア
ポーズ対応
• シナリオ実行は経過時間で実行しているので
バックグラウンドに入った時などには対応が
必要。(開始時刻ー現在時刻=経過時間)
• 停止時間を計測して開始時刻に足してやる。
?
残機設定
• 自機が1回衝突になったらGame Overになっ
ていたが、酷すぎるので最初は3機からス
タート。
• Score sceneを改良して、残機が0の場合と0
以上の場合で分けてみる
残機設定
• ゲームスタートの設定も、新規ゲームと残機
スタートで、クリアする内容を分ける
キャラクタの動作
• キャラクタの動作はキャラクタで固定のパスだったけれ
ど、シナリオファイルにmotionとしてパスを持たせるこ
とにした。
• ベジェパスの内、点の移動、直線描画、曲線描画を辞書
で持たせて、ベジェパスを作成する。コードから...
キャラクタの動作
キャラクタの動作
NSArray *maruMotionArray =
@[@{@"command":@"move",@"x":@0.0,@"y":@0.0},
@{@"command":@"curve",@"x":@150.0,@"y":@-...
パワーアップアイテム
• アイテムを取ると自機の機能が強化される。
• 敵キャラを応用して、衝突時に自機が消滅し
ないようにして、パワーアップフラグを立て
ればOK
パワーアップアイテム
• パワーアップアイテ
ムを取ると砲弾が0.5
間隔で発射されるよ
うになる
音声合成
• パワーアップアイテムが出現したら音声合成
でお知らせしてみた。
• これだけではさみしいので、スタート時に日
本語を話すようにしてみた。
AVSpeechUtterance
参考資料
• iOSアプリ開発メモメモ
• http://iosmemo.ou-net.com/?p=314
実機デモ
今後の課題
• iPad,Mac対応。アスペクト比が違うので、何
か対応がいるのかな。
• 広告。ライトゲームだと無料+広告モデルが
主体だろうと思うので、使いこなせるように
今後の課題
• Game center対応。Game centerが変わるよ
うな があるので、変わってからにしようか
なと。
• 課金。面構成のゲームだと無料+追加面とい
うシステムも取れるのかなあと。
ゲームを作ってみて
• いくつか感じたことを述べたいと思います。
ゲームを作ってみて
• パフォーマンスを常に意識して作る。カクカク
する動きはダメなので、そのような動きになら
ないように常に気を使うこと。
• 速度落ちそうなことはやらない。
• 実機重要。Sprite KitはGPUの効果で高速化がな
され...
ゲームを作ってみて
• フレームワークを使えばわりと簡単にできる。
技術的には敷居は高くない。
ゲームを作ってみて
• Sprite Kitの後に、Scene Kitという3Dゲーム
フレームワークが控えているので(今はMac
のみ)応用できそう。
ゲームを作ってみて
• クラスの独立重要
• キャラクタをクラスで分割したり、シナリオ
ファイルを作ったり、分業できるシステムで
作っておくと、一旦ゲームシステムを作れば、
あとは人海戦術で対応出来る。
ゲームを作ってみて
• Apple TVとかきっとゲームできる用な感じで
出てくるんじゃないかと思う。
ゲームを作ってみて
• だいたい作り方はわかったので、設定変えて
オリジナルゲーム作ってみたいなあと。
ゲームを作ってみて
• ガッポガッポは「アイデア」と「運」次第
ありがとうございました
• デモ用のiPod touchを持ってきていますの
で、休憩時・懇親会などに試してみて意見を
聞かせてください。
Upcoming SlideShare
Loading in …5
×

Cocoa勉強会20140419ゲームをつくってみる

  • Login to see the comments

Cocoa勉強会20140419ゲームをつくってみる

  1. 1. ゲームを作ってみた 2014.4.19 Cocoa勉強会 関西 大森智史 @oogon / satoshi.oomori
  2. 2. あんた、誰? • と、いうわけで自己紹介。
  3. 3. • 大森智史といいます。 • 某印刷会社勤務。 • Cocoa勉強会関西は第0回からいます。
  4. 4. ゲーム • ガッポガッポでウハウハらしい。 • 当然モテモテらしい。 • 作るしかない!
  5. 5. …でも作ったことない • どうやって作るんだろうか • Cocos-2Dとかあるけど • Sprite Kitでしょ
  6. 6. 参考資料 • Sprite Kit iPhone 2Dゲームプログラミング • http://www.amazon.co.jp/dp/479804055X/
  7. 7. 参考資料 • Sprite Kitもろもろ • http://spritekit.jp
  8. 8. さて今日は • 試行錯誤で作ってみましたので間違い・   もっといい方法があるかもしれません。
  9. 9. はてなポイント • スライドにこのマークが付いているところは よくわかってないところです。 • 随時ツッコミをお願いします。 • 某社サービスとは無関係です。 ?
  10. 10. どんなのを作るか • シューティングゲーム • 単純明快なもの
  11. 11. どんなのを作るか • 宇宙船 • 空中戦 • 地上攻撃 • きれいなグラフィック
  12. 12. どんなのを作るか • 中ボス登場 • 大ボス登場 • アンドアジェネシス!
  13. 13. 参考資料 • ゼビウス • http://ja.wikipedia.org/wiki/ゼビウス • ゼビウス軍事パレード • http://www22.tok2.com/home/takaakit/xevi/ parade.html
  14. 14. まずはサンプル • Appleのサンプルを動かしてみる。 • とりあえずSKSpriteNode=キャラクタのよう なのでそれを動かしてみれば良いみたいだ。
  15. 15. 2014.2.15 • プロジェクト始動!
  16. 16. いろいろさわってみる • サンプルをもとにスプライト画像をさしかえ たり、アクションを変更したりしてみる。
  17. 17. 重大な問題 • 絵が描けない。 • 描く道具がない。 • 困った。
  18. 18. Nintendo 3DS • 「うごめも」で描いてみる。 • パラパラ漫画が描ける。 • 着色はPixelMetorで(Photoshopより安い) ?
  19. 19. 参考資料 • Nintendo3DS • http://www.nintendo.co.jp/3ds/
  20. 20. 参考資料 • Nintendo3DS • http://www.nintendo.co.jp/3ds/
  21. 21. 参考資料 • うごくメモ帳3D • http://www.nintendo.co.jp/3ds/eshop/jkzj/ index.html
  22. 22. とりあえず描いてみた • うごメモからGIF出力 • 着色はPixelMetorで • PNGファイルにする
  23. 23. 次は敵キャラ • 敵キャラも作る。 • まずは簡単な動きのキャラクタから。 • でも、鉄板くるくる回したい!
  24. 24. アトラス • アトラス(SKTextureAtlas)っていうのでパ ラパラ漫画にするといいようだ。 • パラパラ漫画なら「うごめも」! ?
  25. 25. アトラス ! • テクスチャだけ SKSpriteNode *obj = [SKSpriteNode spriteNodeWithImageNamed:@“Spaceship"]; • テクスチャアトラス SKTextureAtlas *plateAnimatedAtlas = [SKTextureAtlas atlasNamed:@"spaceship"]; NSUInteger numImages = plateAnimatedAtlas.textureNames.count; for (int i=0; i < numImages; i++) { NSString *textureName = [NSString stringWithFormat:@"%03d", i]; SKTexture *temp = [plateAnimatedAtlas textureNamed:textureName]; [plateFrames addObject:temp]; }
  26. 26. ちょっとしたTips • 連番画像を使うとき@“%03d”とすると 001,002,003…とできます。 for (int i=0; i < numImages; i++) { NSString *textureName = [NSString stringWithFormat:@"%03d", i]; SKTexture *temp = [plateAnimatedAtlas textureNamed:textureName]; [plateFrames addObject:temp]; }
  27. 27. アトラス • テクスチャアトラスはRetina用と非Retina用 がいるんだそうだ。Retina用は”@2x”を追加 • ビルド設定 SPRITEKIT_TEXTURE_ATLAS_OUTPUT = YES にしておかないといけない • とりあえずRetina用だけで作っとく ?
  28. 28. 参考文献 • テクスチャアトラス • http://www.raywenderlich.com/ja/51748/ sprite-kit-チュートリアル-アニメーションと テクスチャ
  29. 29. 衝突 • ぶつかったらやっぱり爆発するよね • エミッターからパーティクルが飛んでいく • パーティクルクラスっていうのはないので注 意が必要
  30. 30. エミッター //エミッターの作成 NSString *path = [[NSBundle mainBundle]           pathForResource:@"FireParticle" ofType:@"sks"]; static SKEmitterNode* spark2 = nil; spark2 = [NSKeyedUnarchiver unarchiveObjectWithFile:path]; spark2.position = contact.contactPoint; spark2.numParticlesToEmit = contact.collisionImpulse; //アクション SKAction *fadeOut = [SKAction fadeOutWithDuration:1.0f]; SKAction *remove = [SKAction removeFromParent]; SKAction *sequence = [SKAction sequence:@[fadeOut, remove ]];   [spark2 runAction:sequence];   [spark2 runAction:soundPlay]; !   [self addChild:spark2];
  31. 31. パーティクル • エミッターから飛んで行くもの • パーティクルクラスはないよ! • エミッタークラスを探せ! ?
  32. 32. とりあえず作って 動かしてみた
  33. 33. 整理 • シーンにごにょごにょ書いていたので、見苦 しい。 • オブジェクト志向っぽく書こう • SKSpriteNodeを使っていたけれど、キャラ クタごとにサブクラス作成。自分のことは自 分でするように。 ?
  34. 34. • クラス分け 自機クラス 敵キャラクラス シーンクラス シーンクラス ! ! ! ! ! ! ! ! ! 自機の動き 敵キャラの動 き タッチ処理等 タッチ処理等
  35. 35. 枠の外 • キャラクタが枠の外に出たら消さないと。 • -(void)didSimulatePhysics ?
  36. 36. 枠の外 • いや、ここはやはり時間が取れる      -(void)update:(CFTimeInterval)currentTime  を使うべきかと • このメソッドが呼ばれた時、前回呼ばれた時 間との間隔を測り、一定時間が経っていたら チェックすることに(今回は0.3秒間隔で) ?
  37. 37. 枠の外 • 「後日談」 • (今回は0.3秒間隔で)としていたが… • →やはり0.015秒間隔で(1/60=0.0166..)
  38. 38. 敵キャラの出現 • ランダムだと、パターン攻略本出せないよね。 • 一定の法則で敵キャラを出すには? ?
  39. 39. 敵キャラ • タイマー? • さっき、一定時間ごとにチェックするように したんだから、それを使えばいいやん ?
  40. 40. 敵キャラ • 出現時間をリストにして、時間が来たらキャ ラクタを出すようにした。 • NSDictionaryをNSArrayで • 将来的には別ファイルにして「面」構成に
  41. 41. 敵キャラ • 出現時間をリストにして、 NSArray *anArray = @[@{@"name":@"teppan",@"time":@1.0f,@"posX":@100.0,@"pos Y":@600.0}, @{@“name":@"teppan",@"time":@3.0f,@"posX":@200.0,@"posY" :@600.0}, ……
  42. 42. 敵キャラ • リストに基づいてキャラクタを作成するようにした。 //0.3秒間隔で調査 if ((beforeTime + 0.3f) < currentTime){ [enemyArray enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { if ([[obj objectForKey:@"time"] floatValue] < (currentTime - durationTime) ) { if ([[obj objectForKey:@"name" ] isEqualToString:@"teppan"]) { … } … … } … } … }
  43. 43. 敵キャラ • 毎回オブジェクトを作っているけど使い回し したほうがいいのか? ?
  44. 44. 敵キャラ • 「後日談」 • できれば面の最初にオブジェクトを作っておく ほうが良い。 • まあでも毎回作っても大丈夫だけどアトラスの 作成には時間がかかるので、最低限アトラスだ けは先に作っておいて使いまわしする。ほうが よい ?
  45. 45. • 敵キャラが規則 的に出現するよ うになった
  46. 46. サウンド • 音がないと寂しいよね。 • 効果音とBGM
  47. 47. 重大な問題 • 作曲できない。 • 楽器も演奏できない。 • 困った…。
  48. 48. ガレージバンドで • なんとかやってみる。 • 売るときはプロに頼めばいいか。
  49. 49. 効果音 • NodeにActionとしてつけるとよいらしい。 ?
  50. 50. EmitterNodeにAction SKAction *soundPlay; ! soundPlay = [SKAction playSoundFileNamed:@"bomb.m4a" waitForCompletion:NO]; ! //エミッターの作成 NSString *path = [[NSBundle mainBundle]           pathForResource:@"FireParticle" ofType:@"sks"]; static SKEmitterNode* spark2 = nil; spark2 = [NSKeyedUnarchiver unarchiveObjectWithFile:path]; spark2.position = contact.contactPoint; spark2.numParticlesToEmit = contact.collisionImpulse; //アニメーション SKAction *fadeOut = [SKAction fadeOutWithDuration:1.0f]; SKAction *remove = [SKAction removeFromParent]; SKAction *sequence = [SKAction sequence:@[fadeOut, remove ]];   [spark2 runAction:sequence];   [spark2 runAction:soundPlay]; !   [self addChild:spark2]; ココ ココ
  51. 51. BGM • AVFoundationを使うらしい ?
  52. 52. BGM @property (nonatomic) AVAudioPlayer * backgroundMusicPlayer; ! //BGM NSError *error; NSURL * backgroundMusicURL = [[NSBundle mainBundle] URLForResource:@"BGM" withExtension:@"m4a"]; self.backgroundMusicPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:backgroundMusicURL error:&error]; ! self.backgroundMusicPlayer.numberOfLoops = -1; [self.backgroundMusicPlayer prepareToPlay]; [self.backgroundMusicPlayer play];
  53. 53. 背景 • 背景も真っ暗だと寂しい。宇宙みたい。 • 地面の絵がほしいよね。 • ナスカの地上絵とか
  54. 54. どうするんだろう • 小さなノードをブロック上に置く • 大きな一枚絵のノードを作る • どちらにしても、はみでる部分を継ぎ足し継 ぎ足し • 今回は大きなノードを作ることに ? ?
  55. 55. • 背景を動かしてみた
  56. 56. 速度は大丈夫か • Air 11インチのシミュレータでは30fpsに落ち るが、iPhone5s実機では60fpsを保ったまま • 大丈夫みたい。 ?
  57. 57. 背景画像を変えてみる • ノードを作り直す • ノードはそのままで、テクスチャ画像を変え る • →下のほうがコストが低そう ?
  58. 58. 背景画像を変えてみる • 「後日談」 • テクスチャ画像を変える方法で作ったが、微妙に「カ クッ」と動く • →結局最初に面の背景を全部作っておくことにしま した。 • 途中で作り直すことにした。最初から常にパフォー マンスを意識して制作しないといけません。 ?
  59. 59. 背景もパラパラしたい • atlasをたくさん作って切り替える ?
  60. 60. • パタパタと2種類の 背景を切り替えな がら4枚の背景をつ なげて動かしてみ た。
  61. 61. 砲弾 • 自機から砲弾が飛ぶようにしたい。 • またサブクラスを作って動作を定義 ?
  62. 62. • 砲弾を発射してみた
  63. 63. 当たったら終了 • 自機が爆発したら終了 • 別のシーンに切り替えたい。 • presentScene:transition:でよいようだ。 ?
  64. 64. • 自機が破壊された ら終了画面に行く
  65. 65. 遅延移行 • 爆発後少し余韻を残すため、2秒後に presentScene:を呼び出しています。 //シーンの遅延移行 -(void)delayPresentScene:(NSDictionary *)obj; { SKView * skView = (SKView *)self.view; [skView presentScene:[obj objectForKey:@"scene"] transition:[obj objectForKey:@"transition"]]; } ! ! [self performSelector:@selector(delayPresentScene:) withObject:dic afterDelay:2];
  66. 66. 参考文献 • Sprite Kitリファレンス • シーン遷移等 • http://cocoaapi.hatenablog.com/entry/ SpriteKit/index
  67. 67. 整理 • キャラクターが増えてきたので、共通部分を 抜き出し、キャラクタのベースクラスとなる 抽象クラスを作成 • 定期的なアップデート、衝突時の処理、範囲 外に出た場合など共通の処理を記載
  68. 68. • ベースクラス作成 キャラクタクラス ! ! 自機クラス 敵キャラクラス 自機クラス 敵キャラクラス
  69. 69. くるくる動く敵キャラ • まっすぐ向かってくるだけではつまらないの で、一定の法則で動くキャラを作る • 動きはベジェパスで作ることができる。 • 速さはspeedで設定可能
  70. 70. パスで動きをつけるとは? • パスは計算 式で曲線を 表すことが 出来ます。 →位置を計 算できる • http://www.slideshare.net/oogon/cocoa201303pdf ?
  71. 71. • パスで敵キャラの 動きを作ってみた
  72. 72. でもね… • パス作るのってなんだかわけがわからない。 • イラストレーターとかでSVG書き出し? • ごもっとも!
  73. 73. モーションパスエディタ • こんなのを作ってみました。 • 作っている最中ですが、デモ...
  74. 74. 参考文献 • パスで動きを作る • http://stackoverflow.com/questions/ 19215223/spritenode-not-following-cgpath
  75. 75. 砲弾が当たるように • シーンで衝突が起こると、呼ばれるメソッド //衝突検知 -(void)didBeginContact:(SKPhysicsContact *)contact { } • このメソッドで衝突したオブジェクトがわか るので衝突処理
  76. 76. 砲弾が当たるように • この処理も大きくなりがちなので、各オブジェ クトに移していきたい。
  77. 77. • 当たり判定とか調 整してみてゲームっ ぽく動く状態に
  78. 78. 再スタート • ゲームオーバーになったあと、もう一度ゲー ムをするための処理を行う • 一旦別のシーンに移ったあともう一度プレイ シーンに戻るには? • 別のシーンに移るときに、プレイシーンを保 持しておいて、プレイシーンに戻ればいい。 ? ?
  79. 79. 再スタート • 移行先シーンにプロパティを設定して @property (retain) SKScene* beforeScene; ! • プレイシーンから移行するときに保持してお く ?
  80. 80. ノードをボタンに • 再スタートボタンはどうする? SKLabelNode? • スプライトノードを作ってボタンにしてもい いか。 ? ?
  81. 81. タッチしたノードは? • タッチした部分にあるノードは何?か調べて SKNode *nodeAtPoint = [self nodeAtPoint:[touch locationInNode:self]]; • あとはノードの名前で処理を変える if ([nodeAtPoint.name isEqualToString: @"restart"]) { OOOLabelButtonNode *startButton = (OOOLabelButtonNode *)nodeAtPoint; startButton.highlighted = YES; }
  82. 82. • 再スタートボタン をつけてみた
  83. 83. 参考資料 • SKLabelNodeをボタンにする • http://spritekit.jp/tutorial/example/
  84. 84. 参考資料 • チュートリアル • http://www.raywenderlich.com/42699/ spritekit-tutorial-for-beginners
  85. 85. ゲームのやり直し • 前のシーンの続きになっているので、ゲーム を最初からやり直すには、ゲーム設定を戻す ことが必要。 • いままで、シーンの初期化メソッドに書いて いたのを-(void)startGameというメソッドに まとめることに。
  86. 86. • ゲームのやり直し
  87. 87. スコア • そうだ、スコアを残そう • とりあえずラベルで。余裕ができたらスプラ イトノードで数字を表示しよう!
  88. 88. • スコアの表示
  89. 89. 実機で • このあたりから実機で確認することに
  90. 90. • 音付きで
  91. 91. オープニング • いきなりゲームが始まるのもおかしいので、 オープニングシーンを作ってみる。 • スコアシーンと同じ
  92. 92. • オープニング画面
  93. 93. 自動運転 • テスト面倒。 • デモ画面では自機が自動的に操作されている。 • 自機を自動操縦させよう。
  94. 94. 自動運転 • 敵キャラの発生と同じように時間が来たら指 定の操作をすればいいんではないか?
  95. 95. • 自動操縦画面
  96. 96. 自動運転 • 物理シミュレーションは毎回同じ結果になる とは限らないようだ。 • →と、当初考えていたけれど、初期化にかか る時間の違いで少しのズレが発生していた。 初期化が終わってから開始時間を0にするこ とで解消。 ?
  97. 97. 地上敵キャラ • 今度は地上の敵キャラを作る • また同じようにキャラクタクラスを作って…
  98. 98. 地上敵キャラ • ん?自機が地上敵キャラに隠れる時があるぞ。 そうか、あとから描画するほうが上へ上へと くるのか • 奥行きの順をコントロール するにはどうするのだ?
  99. 99. レイヤー • zPositionというプロパティを使うみたいだ。 数字が大きくなるほど手前に来る。 • デフォルトは1のようなので、地上物は3、空 中物は5、スコアは7ぐらいにしておこう。 ?
  100. 100. • 上下関係を直してみた。
  101. 101. 参考資料 • シーンを組み立てる • http://spritekit.jp/tutorial/scene/
  102. 102. 自由移動 • 今まで水平方向にしか動かなかったけれど前 後方向もうごかせるようにした。 • 自由に、とはいっても動くことのできる範囲 を限定するためのコードを追加
  103. 103. • 自由に動き回れる ようになった
  104. 104. 整理 • 当たり判定を、シーンでゴニョゴニョしてい るので、オブジェクト指向的に処理したい。 • シーンで衝突時にSKPhysicsContactをそれぞ れのオブジェクトに渡す。それぞれのオブジェ クトで処理を行う。シーンでは判断しない。
  105. 105. 整理 • ベースクラスに衝突時のメソッドを作る。 • サブクラスでそれぞれの処理をオーバーライ ドする。 • キャラクタの独立性を高めて、キャラクタが 増えた時の対応をしやすくする
  106. 106. 整理 • シーンでの-didBiginContact{}の処理がSKPhysicsContactオブ ジェクトを渡すだけになった。 • ここで、ベースクラスOOOChatacterNodeを作った効果が出 てくる。 -(void)didBeginContact:(SKPhysicsContact *)contact { //それぞれのオブジェクトに衝突情報を渡す [(OOOCharacterNode *)contact.bodyA.node contact:contact]; [(OOOCharacterNode *)contact.bodyB.node contact:contact]; }
  107. 107. シーンクラス ! -(void)didBeginContact: (SKPhysicsContact *)contact { ! ! } • 衝突時の処理 自機クラス -(void)contact: (SKPhysicsCon tact *)contact{} 敵キャラクラス -(void)contact: (SKPhysicsCon tact *)contact{} シーンクラス ! -(void)didBeginContact: (SKPhysicsContact *)contact { ! ! ! ! ! ! } 自機の処理 敵キャラの処理 contactオブジェクト contactオブジェク contactオブジェクト
  108. 108. 敵砲弾 • 敵キャラも砲弾を
  109. 109. 敵砲弾 • 敵も砲弾を撃つ
  110. 110. いっしょに動く • 地上攻撃のマーカーを作りたい。 • 自機と一緒に動くノードSKPhysicsJointFixed で自機と接続 • ハマった所:シーンにすでに接続するものが ないと例外発生
  111. 111. 地上照準を表示 • 自機と一緒に動 くマーカーを表 示
  112. 112. もろもろ調整 • プレイしながら細かいところを修正する
  113. 113. 画面が暗くならないよう • デモプレイしているとタッチしないのでスリー プする。 • スリープしないようにするにはデリゲートで application.idleTimerDisabled = YES;として やればよい。(ゲームが終了・バックグラウ ンドになる時はNOにしておく) ?
  114. 114. 参考資料 • iOSデバイスをスリープしないようにする • http://program.station.ez-net.jp/special/ handbook/objective-c/uiapplication/ idletimer.asp
  115. 115. 終了画面から タイトル画面へ • 再スタートボタンしかなかったので終了して からタイトル画面に戻るボタンを付けた。 • 一定の時間で強制的にタイトルに戻るように したほうがいいかもしれない。 ?
  116. 116. パフォーマンス向上 • 概ね60fpsで動いているが、背景画像の切替 時にもたつきが発生している。 • 画像差し替え時の読み込みに時間がかかって いる? • 事前にバックグラウンドで読み込んでおく? ?
  117. 117. パフォーマンス向上 • SKTextureAtlasの作成時に時間がかかっているようなので 事前に画像を読み込み、非同期読み込み。 -(void)preloadAtlas:(int)atlasNumber { dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGR OUND, 0), ^{ // Background operations preloadTextureAtlas = [SKTextureAtlas atlasNamed:[NSString stringWithFormat:@"ground%03d", groundNum]]; //dispatch_async(dispatch_get_main_queue(), ^{ // Main Thread //}); }); } ?
  118. 118. パフォーマンス向上 • 回転する鉄板もサイズは大きくないが15枚つ かっているので、atlas読み込み時に時間がか かっている模様。 • 同じキャラクタでアトラスを使いまわしでき るよう、キャッシュする。 ?
  119. 119. パフォーマンス向上 • アトラスをキャッシュすることオブジェクト の独立性が薄くなる懸念がある。 • メモリが許せば面に登場するキャラクタオブ ジェクトを最初に全部作っておくこともいい かも? • →出来る限り最初に作成しておく! ?
  120. 120. パフォーマンス改善後 • アトラスをキャッ シュすることで もたつき感解消
  121. 121. 地上攻撃 • 地上のターゲットを攻撃するときの処理を加 えた ?
  122. 122. 地上ターゲットを攻撃 • 地上のターゲッ トを攻撃する処 理を追加
  123. 123. 敵キャラAI • 敵キャラに、状況に応じて行動させる。 • 敵キャラと自機の距離が一定以下になったら 砲弾を発射 • SKActionで、位置チェック、敵キャラごとに 違う処理の実装可能 ?
  124. 124. 敵キャラAI • 敵キャラと自機の距離が200pt以下になった場合砲弾を発射する。 SKAction *attackAction = [SKAction customActionWithDuration:2.0 actionBlock:^(SKNode *node, CGFloat elapsedTime){ NSArray *nodes = [self.parent children]; [nodes enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { SKNode *node = (SKNode *)obj; if ([node.name isEqualToString: @"spaceship"]){ float deltaX = node.position.x - self.position.x; float deltaY = node.position.y - self.position.y; float delta = sqrtf(pow(deltaX,2.0) + pow(deltaY,2.0)); //NSLog(@"%.2f",delta); if (delta > 200.0) { NSDictionary *dic = [NSDictionary dictionaryWithObjectsAndKeys:@150.0,@"posX", [SKTransition fadeWithDuration:1],@"transition", nil]; if (!fireing){ [self performSelector:@selector(fireEnemyShell:) withObject:dic afterDelay: 0.0f]; fireing = YES; } } } }]; }]; [obj runAction:attackAction];
  125. 125. 面構成 • ボスキャラを倒したら面クリアというのが定 番 • 速度重視のために最初にリソースを読み込ん でおく事が必要なため、このパターンが生ま れた。 ?
  126. 126. 面構成 • ユーザーの体調面を考慮して、休憩を挟むこ とが必要かと。 • ボスキャラを倒す、他はある地点まで到達す ることで面クリア。 ?
  127. 127. 面クリア • ボスキャラを倒 したら面クリア
  128. 128. ポーズ対応 • シナリオ実行は経過時間で実行しているので バックグラウンドに入った時などには対応が 必要。(開始時刻ー現在時刻=経過時間) • 停止時間を計測して開始時刻に足してやる。 ?
  129. 129. 残機設定 • 自機が1回衝突になったらGame Overになっ ていたが、酷すぎるので最初は3機からス タート。 • Score sceneを改良して、残機が0の場合と0 以上の場合で分けてみる
  130. 130. 残機設定 • ゲームスタートの設定も、新規ゲームと残機 スタートで、クリアする内容を分ける
  131. 131. キャラクタの動作 • キャラクタの動作はキャラクタで固定のパスだったけれ ど、シナリオファイルにmotionとしてパスを持たせるこ とにした。 • ベジェパスの内、点の移動、直線描画、曲線描画を辞書 で持たせて、ベジェパスを作成する。コードから切り離 すことで、モーションエディタで動作の設定が可能 • 動的なベジェパスの作成は、AIでの動作にも応用できる
  132. 132. キャラクタの動作
  133. 133. キャラクタの動作 NSArray *maruMotionArray = @[@{@"command":@"move",@"x":@0.0,@"y":@0.0}, @{@"command":@"curve",@"x":@150.0,@"y":@-100.5,@"cp1x":@60.0 ,@"cp1y":@-110.5,@"cp2x":@40.0,@"cp2y":@-90.5}, @{@"command":@"curve",@"x":@-50.0,@"y":@-340.0,@"cp1x":@-100 .0,@"cp1y":@-100.0,@"cp2x":@-110.0,@"cp2y":@-350.0}, @{@"command":@"curve",@"x":@500.0,@"y":@-140.5,@"cp1x":@400. 0,@"cp1y":@-240.0,@"cp2x":@600.0,@"cp2y":@-40.0} ]; ! 上記パスをシナリオ配列に組み込む 20秒後に出現するキャラクタに動きを設定 @{@"name":@"maru",@"time":@20.0f,@"posX":@100.0,@"posY":@600 .0,@"motion":maruMotionArray},
  134. 134. パワーアップアイテム • アイテムを取ると自機の機能が強化される。 • 敵キャラを応用して、衝突時に自機が消滅し ないようにして、パワーアップフラグを立て ればOK
  135. 135. パワーアップアイテム • パワーアップアイテ ムを取ると砲弾が0.5 間隔で発射されるよ うになる
  136. 136. 音声合成 • パワーアップアイテムが出現したら音声合成 でお知らせしてみた。 • これだけではさみしいので、スタート時に日 本語を話すようにしてみた。 AVSpeechUtterance
  137. 137. 参考資料 • iOSアプリ開発メモメモ • http://iosmemo.ou-net.com/?p=314
  138. 138. 実機デモ
  139. 139. 今後の課題 • iPad,Mac対応。アスペクト比が違うので、何 か対応がいるのかな。 • 広告。ライトゲームだと無料+広告モデルが 主体だろうと思うので、使いこなせるように
  140. 140. 今後の課題 • Game center対応。Game centerが変わるよ うな があるので、変わってからにしようか なと。 • 課金。面構成のゲームだと無料+追加面とい うシステムも取れるのかなあと。
  141. 141. ゲームを作ってみて • いくつか感じたことを述べたいと思います。
  142. 142. ゲームを作ってみて • パフォーマンスを常に意識して作る。カクカク する動きはダメなので、そのような動きになら ないように常に気を使うこと。 • 速度落ちそうなことはやらない。 • 実機重要。Sprite KitはGPUの効果で高速化がな されているので、実機でないと60fpsでない。
  143. 143. ゲームを作ってみて • フレームワークを使えばわりと簡単にできる。 技術的には敷居は高くない。
  144. 144. ゲームを作ってみて • Sprite Kitの後に、Scene Kitという3Dゲーム フレームワークが控えているので(今はMac のみ)応用できそう。
  145. 145. ゲームを作ってみて • クラスの独立重要 • キャラクタをクラスで分割したり、シナリオ ファイルを作ったり、分業できるシステムで 作っておくと、一旦ゲームシステムを作れば、 あとは人海戦術で対応出来る。
  146. 146. ゲームを作ってみて • Apple TVとかきっとゲームできる用な感じで 出てくるんじゃないかと思う。
  147. 147. ゲームを作ってみて • だいたい作り方はわかったので、設定変えて オリジナルゲーム作ってみたいなあと。
  148. 148. ゲームを作ってみて • ガッポガッポは「アイデア」と「運」次第
  149. 149. ありがとうございました • デモ用のiPod touchを持ってきていますの で、休憩時・懇親会などに試してみて意見を 聞かせてください。

×