1. iOSでゲーム(脱獄アプリ)を開発するまで

 の備忘録。

2. 脱獄する等環境準備

cf.あんま関係ないけど自分の必須アプリメモ
iFile
MxTube
FreeVideo
MusicMeow
mobile terminal

iSaveWebLite
SBSettings
Backgrounder
Activator

chrome
Googleカレント
Dropbox
Pocket
PlainText
iLunascape

iComicViewer
 

3. Xcodeをいじる

 →しかし、XIB(NIB)ファイルにコードが隠蔽されてて、全体像が見えない。
 →InterfaceBuilderを使う方法と使わない方法、両方知るべき

4. Objective-Cを覚える

4.1. 文法とか

・アクセス関数=アクセッサメソッド
・アクセス関数がアクセスするメンバ変数=プロパティ

・@property と @synthesize
 →「setterとgetter(アクセッサメソッド)をコンパイルの前に生成させる」
 →ドットによるアクセスを、セッターメソッド呼び出しに変換する
  例 インスタンス.プロパティ=xx; みたいにアクセスできる。
 →注意すべきは、retainなどにした場合、参照カウントが増える。
  ドットによるアクセスなので、参照カウントが増えたことを忘れがち。

・publicなメンバへのアクセス
 →a->b = 10;のようにする
 →id型だとできない。

・NULLとnilは違う。
 場合に応じて適切な方を使う

・プロトコルを宣言する
 @protocol プロトコル名 <親プロトコル1 , ...>
 プロトコル本体
 ...
 @end
・具体例
 @protocol NSCoding
 - (void)encodeWithCoder:(NSCoder*)coder;
 - (id)initWithCoder:(NSCoder*)decoder;
 @end

・プロトコルを採用する
 @interface クラス名 : スーパークラス名 <プロトコル1, ...>
・具体例
 @interface MyObject : NSObject 
 {
     ...
 }
 ...
 @end

・conformsToProtocol:メソッド
 →あるオブジェクトがプロトコルに適合しているかどうかを実行中に調べることもできる


・変数が指定したプロトコルを採用していることを明示的に宣言することができます。
 型名 <プロトコル名> 変数名 ...

5. イベント伝達の仕組み

5.1. 種類

 特に、has-a関連の保持されている側から親元を呼び出すときに問題となる。
 (その逆は簡単。)
 ・Protocol-Delegateパターン
 ・KVO(Key-Value Observing)
  →プロパティの値が変更されたら、予め指定したオブジェクトのメソッドをコールバックする。
  →コールバックはいくつでもOK
 ・NSNotification
  呼び出し元→メッセージを送る→NotificationCenter→予め登録しておいたメソッドをコールバック→呼び出し先

5.2. Delegate

5.2.1. Delegateとは

 →代理人とか代表者とか委譲などという意味
 →別のクラスに、送られてきたメッセージを丸投げ
 →関数の間接的な呼び出し
・sender→(メッセージ)→receiver→(メッセージ丸投げ=委譲)→delegate(あくまでこれがデリゲート!!)

5.2.2. 混乱する前に抑えておくべき点

・どっちがデリゲートか?
 →委譲「される」方の「インスタンス」!!
・デリゲートメソッド(委譲される側=デリゲートの「メソッド」)をデリゲートと呼ぶ場合もある。実に紛らわしい。
・delegateという変数名に騙されるな。
 →メンバ変数であるdelegateは、委譲「する」側につくる。
 →委譲「する」側がデリゲートメソッドにアクセスするために、デリゲートインスタンスを保持するため

5.2.3. しくみ

 呼び出し側
  ・デリゲート(にデリゲートメソッド)がある場合: デリゲートに丸投げ
   〃 が無い場合: デフォルトの処理
  ・delegateというメンバ変数を持たせる。
  ・setDelegateメソッドをつくる。これによってデリゲートを指定する。

    ↓丸投げ

 デリゲート側
  ・@interface ..(略).. <プロトコル>とする
   →プロトコルを用いるのは、デリゲートメソッドがあることを保障するため
   →逆に言えば、プロトコルさえ採用すればどんなクラスもデリゲートになれる
  ・デリゲートメソッドを実装


5.2.4. なぜ使うか

 ・情報のやり取り
 ・承継を使わずにできる利点


5.2.5. デリゲート使用時の注意点

・retainはダメ
 @property (assign) id delegate;
 ∵参照の循環が起こる・・・らしい

5.2.6. デリゲートメソッドの実装

 典型的なコードは、以下

 - (id)delegateMethod:(id)anObject
 {
   //delegateインスタンスがメッセージに応答可能かチェック
   //応答可能なら丸投げ
   //respondsToSelectorは、どんなインスタンスでも持ってるメソッド
     if ( [delegate respondsToSelector:@selector(delegateMethod:)] ) {
         return [delegate delegateMethod:anObject];
     }
 
     // デフォルトの動作
 }

6. クラスライブラリを知る

6.1. 理由

 車輪の再開発はしたくない。

6.2. 種類

■objective-c
標準のライブラリ(CoreGraphicなど)
cocos2d
kobold2d
sparrow

■C++,android等への移植性も確保
cocos2d-x

■JavaScript
titanium mobile
 quicktigame2d
enchant.js
Impact
など。

7. Cocoa touchなど

7.1. GEOプログラミング(位置情報)

CoreLocation
 →Significant Location Change:だいぶ位置が変わったら知らせる
 →Location Region Monitoring:特定の位置に入ったら知らせる
 →逆ジオコーディング
 →電池くう
 →位置情報取得できない場合の対応。BGで動かす時は殺されないように
MapKit

7.2. イベント

→加速度センサー:UIAccelerometerクラスを介してUIAccelerometerDelegateのデリゲートメソッドが呼ばれる
→デバイスの向きの変化:UIViewControllerのメソッドをオーバーライドして対応

7.3. IBでのカスタムビュー

 IBでViewを選択し、クラスをカスタムビューのクラスにする
 カスタムビューを作る場合はUIViewを継承するのが無難
 initWithFrameは呼ばれない。awakeFromNibを使う
 →ただし、awakeFromNibは複数回呼ばれうる落とし穴がある

7.4. 音声

 ・AVAudioPlayerによる方法
  →ゲームでやるならこっちか?
  →stopはcurrentTimeを0にしないことに注意
 ・AudioToolboxによる方法
  →システムサウンドも再生できる
 ・Ipodとの共存はAudioSession
 ・cafファイルを使うと、サイズが小さくなる。
  作成は、端末から、afconvert -f caff -d ima4 元ファイル名、のようにする

7.5. 加速度センサ

 ・UIAccelerometerDelegateの実装

7.6. Core motion

 →加速度センサーとジャイロ機能。ios4以降など、環境が限定される
 →取得できるのは以下の情報
  ・デバイスの加速度
  ・デバイスの回転速度
  ・磁力計によって検出される磁場
  ・ユーザーによって掛けられた加速度
  ・重力加速度
  ・デバイスの姿勢
 →CMMotionManagerクラスの利用
  →インスタンスは一つにしないと重くなる。シングルトンと思え
  プル型→CMMotiomManagerのインスタンスに値が更新される
  ブッシュ型→ブロックで処理
  delegateも使える?

7.7. 保存読み込み

 NSData
 プリリファレンス

7.8. 画面遷移?実証

 ・rootViewコントローラーを切り替える?

 ・view(addSubview) & animation
  ・Viewコントローラーは単一のままでViewのみ切り替え
  ・ViewのみでなくViewコントローラーごと切り替え
   →こっちの方がいいらしい
   方法としては
    ・UIWindowのaddSubviewで2つビューを設定する
     e.g.
     [window addSubview:viewController1.view];
     [window addSubview:viewController2.view];
    ・片方をbringSubviewToFrontで全面に持ってきて
    ・sendSubViewToBackで切り替える
 ・modal view
 ・Navigation
  階層
 ・TabBar
  並列 
 ・Storyboardなんて知らない
  →Xcode4から
  →結局、上の3つが隠蔽されてる

7.9. ブルートゥース

 ・Corebluetooth
  →iOS5以上
 ・GameKit
   GKPeerPickerControllerクラスはiOSデバイス同士のBluetooth接続に必要な処理とUIを提供しており、PtoP通信を確立する手順が簡単に実装できるようになっています。


7.10. 処理の高速化

 ・Gcd
 ・OpenGL


7.11. その他

・起動当初よりフルスクリーン(ステータスバーを消す)
 →plistで設定


8. cocos2d

・導入
 ・Xcode3はcocos2dのver1のみ対応
 ・cocos2dをダウンロード&解凍&テンプレートインストール(install-templates.shを実行)
 ・リファレンス超読みやすいw、図とか多用してて感動したw Appleも見習って欲しい。
  http://www.cocos2d-iphone.org/api-ref/latest-stable/annotated.html

・画像表示
 ・Xcodeで新規プロジェクトを作成。cocos2d applicationを選択
 ・画像をResourcesに追加
 ・ClassesのHelloWorldLayer.mを修正
  ・グローバルスコープ(#importの後ぐらい)にCCSprite *my_image;みたいに変数宣言。
  ・init()のHelloWorld表示部分を削除
  ・init()に、
      my_image = [CCSprite spriteWithFile: @"画像ファイル名"];
    my_image.position = ccp(100,100);//位置
    [self addChild:my_image];
   として、画像追加

・画面サイズを得る
 [[CCDirector sharedDirector] winSize];

・画像を動かす1(コールバック)
 ・init()内に、[self schedule:@selector(nextFrame:)];とする
  →nextFrameメソッドが、毎フレームコールバックされる

 ・-(void) nextFrame:(ccTime)dt メソッドを作る
  →毎フレームの動作処理を書く
  →スプライトを動かす場合
   my_image.position = ccp(my_image.position.x+10,my_image.position.y);
   のようにする
   →xのみ、yのみの代入はできないらしい

・タッチイベントの捕捉
 ・standardとtargetedの2種類がある。デリゲートを使う。
 ・以下、CCLayersクラス内orそれを承継したクラスでの話。それ以外だとさらにめんどくさい。
  詳細は http://www.cocos2d-iphone.org/wiki/doku.php/tips:touchdelegates
  



 ・standartの場合(概要のみ)
   ・HelloWorldLayer.hに
    #import "CCTouchDispatcher.h" を追加
   ・init()で、self.isTouchEnabled = YES;とする
   ・あとはCCStandardTouchDelegateプロトコルを実装する。
    - (void)ccTouchesBegan:(NSSet *)touches withEvent:(UIEvent *)event;
    - (void)ccTouchesMoved:(NSSet *)touches withEvent:(UIEvent *)event;
    - (void)ccTouchesEnded:(NSSet *)touches withEvent:(UIEvent *)event;
    - (void)ccTouchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event;
    がある。すべて@optional
   ・特徴
    →すべてのタッチイベントを補足する。マルチタッチに対応できる。
     ただし、マルチタッチに対応するには
      AppDelegateのapplicationDidFinishLaunching:メソッド内で
      [glView setMultipleTouchEnabled:YES];とする必要がある。
    →その分、余計なタッチイベントにお付き合いするはめになる



 →targetedの方法の場合
  ・HelloWorldLayer.hに
   #import "CCTouchDispatcher.h" を追加
  ・init()で、self.isTouchEnabled = YES;とする
  ・以下のように、registerWithTouchDispatcherメソッドを追加する。
   -(void) registerWithTouchDispatcher
   {
        [[CCTouchDispatcher sharedDispatcher] addTargetedDelegate:self priority:0 swallowsTouches:YES];
   }
  ・CCTargetedTouchDelegateプロトコルを実装する。
   - (BOOL)ccTouchBegan:(UITouch *)touch withEvent:(UIEvent *)event;
   以下は、@optional
   - (void)ccTouchMoved:(UITouch *)touch withEvent:(UIEvent *)event;
   - (void)ccTouchEnded:(UITouch *)touch withEvent:(UIEvent *)event;
   - (void)ccTouchCancelled:(UITouch *)touch withEvent:(UIEvent *)event;

   →具体的には
   - (BOOL)ccTouchBegan:(UITouch *)touch withEvent:(UIEvent *)event {
    return YES;
   }
   のように実装する。YESとすれば、そのタッチについての以後起こるイベントを処理するようになる。

   そして、
   - (void)ccTouchEnded:(UITouch *)touch withEvent:(UIEvent *)event {
   }
   などに、スプライトの動作処理などを入れれば、タッチして離された時に、スプライトが動く。
  ・特徴
   →1つのタッチの処理に向いている。
    だから、standardと違い、プロトコルのメソッド名も〜Touch〜、と単数形になっている。



・画像を動かす2(タッチイベント)
 targetedのイベントを捕捉して、アニメーションを表示する

 - (void)ccTouchEnded:(UITouch *)touch withEvent:(UIEvent *)event {
        CGPoint location = [self convertTouchToNodeSpace: touch];
        [スプライト stopAllActions];
        [スプライト runAction: [CCMoveTo actionWithDuration:1 position:location]];    
 }
 のようにする


・シーン(CCScene)の操作
 ・シーンの操作は、CCDirectorで行う

 ・シーンの切り替え
  [[CCDirector sharedDirector] runWithScene: [HelloWorld scene]];
  のようにする。
  runWithSceneの他、replaceScene:、pushScene:、popScene:などがある。

 ・シーンの中断再開もできる

 ・Fancy Transitions
  ・シーン切り替え時にアニメチックにできる

・メニュー


9. opengl

・描画処理
 ・renderメソッドに記載する
 ・glBegin(),glEnd()は、OpenglESではサポートされてない。

・2D描画はテクスチャを作る
 CoreGraphics等の機能で画像ロード→Openglでテクスチャ作成、描画


10. game 構造

10.1. アクションゲームの構造

UIViewController
 ・シーン切り替え
  →保持しているUIViewからのNotificationに応じてシーンの切り替え
  →addSubviewで切り替え。
  →delegateだと、呼び出し最中に呼び出し元をreleaseすることになる



 ↓has-a


UIView
 ↓承継
カスタムビューの基礎クラス
 ・changeScene(message)
 ↓承継
シーン別カスタムビュー
 ・UILabel、UIButton、UIImageView(背景など)、AVAudioPlayer(特にBGM)など保持

 ・postInit
  cf.一度ロードしたら変わらないもの=UIImageViewと、それ以外の初期化を区別

 ・move=動作処理
  ・NSTimerで呼び出してもよいが、
   加速度センサーを使うと定期的にデリゲートメソッドが呼び出されるので、そこで動作処理をすればいい。
 ・RunLevelによる状態処理
  →HPが零になったなど
 ・描画処理
  ・Quartz2d(CoreGraphic)をつかう場合は必要
  ・UIImageViewを使うなら不要
 ・ちょっとした画面変化は、シーン切り替えでなく、この中で処理
  e.g. ボタンを押す→UIImageViewが遠くに移動、別のUIImageviewが出てくる、とすれば
	Sceneの切り替えを用いるまでもない


 ↓has-a



キャラ等管理クラス
 ・UIImageView、x,y,w,h,など保持。効果音を保持してもよいか。
 ・状態管理フラグ e.g.生きてる、死んでる、死んでる途中
 ・フレーム数と上限フレーム
  →フレーム0の時Xの位置で、フレーム100の時yの位置にいるように、moveで補完する

 ・postInit
  →initの後も初期化できるようにする
 ・awake(他キャラの位置など、初期化に必要なもの)
  →キャラ起動準備
  →変数のイニシャライズ。起動後にもできりょうに
 ・load(カスタムビュー,)
 ・move(入力の情報 e.g.加速度)
 ・hittest(windowの枠)
 ・hittest(対象となる物のRect)


その他、便利ツール
 RELEASEマクロ
 #define RELEASE(x) if(x){[x release];}
 

11. 配布

11.1. debianファイル作成

 ルートのフォルダ(ソフト名など)
  ・DEBIANフォルダ
   controlファイル
  ・インストールパスと同様のフォルダ構造
   インストールしたいファイル、フォルダ

 の構造を作る。

 dpkg-deb -b ルートのフォルダ名、でdebファイル作成

11.2. リポジトリ作成

 ・dpkg-gettext.pl入手
  →/etc/perlに入れて777にする

 ・dpkg-scanpackagesの入手
  →パスの通るところに配置、パーミッション777実行可能に。

 ・dpkg-scanpackages –m . /dev/null >Packages

 ・bzip2 Packages

 ・debファイルとPackagesファイルをUP

 ・UPしたURLがリポジトリ

 ・debファイルの追加ごとに同様にやる