Categories

  • Android開発
    android marketで目指せ億万長者(ウソ)
  • cocos2d
    pythonでも使えるゲームフレームワーク
  • Google
    ここには未来を開くためのAPIがたくさん用意されている。
  • GoogleAppEngine
    どこまでもスケールアウトするクラウドサービス。使いこなすのが大変
  • Hack
    様々な電子機器を本来の用途とは別の用途に使ってみる。
  • iPhone開発
    app storeで目指せ億万長者(ウソ)
  • python
    LightWeightLanguageで一番難しいがLispにも通じるところがある面白い言語。
  • TIPS
    覚えておくともしかしたら役に立つかもしれないチョットしたこと。
  • うまくいきません
    やってみたけど、うまくいかなかった失敗記事
  • ネット世界
  • 夢見るソフトウェア
    こんなのいいな、できたらいいな、いつかつくろう
  • 開発環境
    開発するまえに環境を整えた記録、次に同じことをするためめの忘備録
無料ブログはココログ
My Photo

« cocos2dのリファレンスマニュアル | Main | cocos2dの練習 Game Demo Tutorial #3: Collisions & Particle Systems をやってみた »

August 16, 2009

cocos2dの練習 Game Demo Tutorial #2: Hello Spaceをやってみた

どこから取り組んだらいいのかよくわからないcocos2dのとっかかりとして、Mac DEVでチュートリアルが提供されている。

Mac Dev » Game Demo Tutorial #2: Hello Space

読んでいるだけでなく、実際にやってみた。
もともとのチュートリアルはcocos0.72をベースにしている。現時点での最新バージョンは0.81だ。
ダウンロードしたサンプルを実行して確認した後、0.81のテンプレートに入れて動かす

まずは LandscapeをFALSEにして縦横を修正
resourceにbg.pngおよびship.pngを追加する。しかしながら、何故かターゲットディレクトリにコピーされないので手動でコピーした。

retainというのはなんだろうな。ソースコードを調べてみるとテクスチャーデーター管理で、頂点データーのバッファーを確保するときに頂点サイズに掛け合わされている。どこで定義されているかはわからないが、増減すると書いてあるので変にいじらない方がよさそうだ。
Sprite *bg = [[Sprite spriteWithFile:@"bg.png"] retain];

表示位置は以下のように指定する。いやbg.position=で代入してもいいんだけどね。オブジェクトに大しては中心点。
cpvは物理エンジンchipmunkのベクター型らしい。中にはx,yのfloat

[bg setPosition:cpv(160,240)];

Spriteの定義をするのは、Ver0.81ではGameSceneではなくGameLayerのほうがよさそうだ。
Bulletを後回しにしてビルド、宇宙船が左右に移動する。

@selectorはセレクターをオブジェクトのメソッドから取得する。関数へのポインタみたいなものか?Objective-C良くわからない。
 schedule にセレクターを渡すと、定期的に呼び出してくれるみたいだ。

[self schedule: @selector(moveShip:)];

呼び出されるmoveShip関数にはdtが引数としてついてくる。前回呼び出されたときの時間との差分らしい。
もっと精度が必要であればdoubleで再定義しろとマニュアルに書いてある。

-(void) moveShip: (ccTime) dt{

次は後回しにした弾つまりBulletに取り組む。なんと、いきなり物理エンジン登場

cpInitChipmunk();
space = cpSpaceNew(); //空間を定義
cpSpaceResizeStaticHash(space, 400.0f, 40);//演算の高速化のためのハッシュ領域の大きさを調整
cpSpaceResizeActiveHash(space, 100, 600);
space->gravity = cpv(10, 0); //重力の方向
space->elasticIterations = space->iterations;//弾性物体の反復処理、デフォルトを使う
//Update Chipmunk
[self schedule: @selector(step:)]; //スケジューラーに定期的にstepを呼び出してもらう。

//弾10個分を初期化する
bullets = [[NSMutableArrayalloc] init];
for(int i = 0; i <10; i++)
{
Bullet *b = [[Bullet alloc] initWithCPBody:[self makeBulletX:-50 y:-50]];//初期位置は画面外
[bullets addObject: b];
[b release];
}

bulletsは配列、各要素を呼び出すときは以下のようなループが使える。
 for(Bullet *b in bullets){}
ここでようやくObjective-Cがかっこいいと思う

BulletクラスにはcpBodyとreadyのフラグしかない。NSMutableArrayに直接格納できないのでラッパーとしての役割の方が大きい。
bulletBodyに対して直接外から代入しても良さそうなものだが、BulletはinitWithCPBody:でオブジェクトを作成する。

(id) initWithCPBody: (cpBody *) bodyIn
{
self = [super init];
if(self != nil){
bulletBody = bodyIn;
ready = YES;
}
return self;
}

cpBodyの定義はこちら 
http://code.google.com/p/chipmunk-physics/wiki/cpBody
cpShapeの定義はこちらをみたほうが早いかも
http://code.google.com/p/chipmunk-physics/wiki/cpShape

-(cpBody *) makeBulletX: (float) x y:(float) y
{
Sprite *laser = [[SpritespriteWithFile:@"laser.png"] retain];
laser.position = cpv(x, y);
[self addChild: laser]; //スプライトとして画像を追加

cpVect verts[] = { //あたり判定で使用するobjectの大きさを定義
cpv(-7,-10),
cpv(-7, 10),
cpv(7, 10),
cpv(7,-10),
};
//質量100.f慣性モーメント無限大で作成
cpBody *laserBod = cpBodyNew(100.0f, INFINITY);
laserBod->p = cpv(x, y);
laserBod->v = cpv(0, 0);
//物理空間に追加 body
cpSpaceAddBody(space, laserBod);
cpShape * laserShape = cpPolyShapeNew(
laserBod, //cpBody
4, //頂点数
verts, //頂点座標
cpvzero //中心点
);
laserShape->e = 0.9f;// Elasticity つまり弾性。0.0で無反射、いかなる場合も1.0を超えるな
laserShape->u = 0.9f;// Friction coefficient つまり摩擦係数。0がツルツル
laserShape->data = laser;// ユーザーデーターにSpriteを関連づける
//物理空間に追加 shape
cpSpaceAddShape(space, laserShape);

return laserBod;
}
摩擦係数の設定に関してはこちらを見るといいらしい。
http://www.roymech.co.uk/Useful_Tables/Tribology/co_of_frict.htm


物理エンジンは定期的に呼び出してあげないと動作しない。
呼び出しの頻度が多いほどシミュレーションは正確になるが、CPUの負担になる。
このサンプルでは、呼び出し回数を2倍にしている。
http://code.google.com/p/chipmunk-physics/wiki/cpSpace

-(void) step: (ccTime) delta
{
int steps = 2;
cpFloat dt = delta/(cpFloat)steps;
for(int i=0; i cpSpaceStep(space, dt); //物理エンジンをすすめる
}
cpSpaceHashEach(space->activeShapes, &eachShape, nil);//ハッシュの更新
cpSpaceHashEach(space->staticShapes, &eachShape, nil);//ハッシュの更新
[self updateBullets];
}
cpSpaceHashEachについてはドキュメントに記載されていない。

最後にupdateBulletsが呼び出されている。これは画面外に出た弾を初期化して次の発射に備えて再利用するためのものだ。
弾の位置を更新するというような作業は物理エンジンがやってくれる。でも、方向ベクトルは処理されているのに、グラビティが無視されているのは何故だろう。次のチュートリアルで考える。

-(void) updateBullets
{
for(Bullet *binbullets)
{
if([bgetY] >500)
{
[b resetPosition];
[b setReady: YES];
break;
}
}
}

タッチしたら弾を発射するようにするには、まずisTouchEnabled = YESが初期化で行われていなければならない。
幸い、フレームワークを使うと既に入っているので、特に追加することはない。
フレームワークでは、画面をタッチするとメニューに戻るようになっているのだが、戻ると困るのでこれはなくす。
そういえばiphoneのゲームの基本構造として、起動画面→メニュー→ゲーム開始→終了→メニューとなっているので、ゲーム内のどこかにメニューに戻る仕掛けが必要になるのか。
 その場合、次のゲームでSceneとかLayerの初期化とかどうなるんだろう。前回のものを解放しないで、再初期化すればいいんだろうか。addObjectで追加しまくっているので、これを全部管理して解放するなんてぞっとする。
 なんてことは後で考えるとして、弾を撃たなきゃ。

- (BOOL)ccTouchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
for(Bullet *b in bullets) //foreachみたいなもの、かっこいい
{
if([b ready])//唯一のメンバ変数 ready 使用可能な状態
{
[b fireFromX:ship.position.xy:ship.position.y+12];//自機の前から発射
[b setReady: NO];//@synthesizeで自動生成されたセッター
break;
}
}
return kEventHandled; //処理が終わりましたというフラグを返さないといけない
}
もちろん10個全部を撃ったら、再利用が可能な状態になるまで弾が出ない。

fireFromXは弾の初期位置と速度を設定する。
-(void) fireFromX: (float) x y:(float)y
{
 bulletBody->p = cpv(x,y);
 bulletBody->v = cpv(0, 200);
}

 物理計算を行うだけでスプライトの位置も一緒に更新されるのは実にありがたい。
不思議な現象は、物理上の位置と表示上の位置がずれていることに起因するケースがあるからだ。

さて、次はチュートリアルの#3をやるか。

以下のブログを参考にさせていただきました。
shukujitsuの開発ブログ : MAC DEVのサンプルを読む その1 - livedoor Blog(ブログ)


余談だが、ココログにプログラムのソースコードを掲載するのは、インデントも強調表示も色分けも何もかも難しい。

« cocos2dのリファレンスマニュアル | Main | cocos2dの練習 Game Demo Tutorial #3: Collisions & Particle Systems をやってみた »

iPhone開発」カテゴリの記事

Comments

Post a comment

Comments are moderated, and will not appear on this weblog until the author has approved them.

(Not displayed with comment.)

TrackBack


Listed below are links to weblogs that reference cocos2dの練習 Game Demo Tutorial #2: Hello Spaceをやってみた:

« cocos2dのリファレンスマニュアル | Main | cocos2dの練習 Game Demo Tutorial #3: Collisions & Particle Systems をやってみた »