Objective-C でシューティングゲーム を作成してみる
description
Transcript of Objective-C でシューティングゲーム を作成してみる
Objective-C でシューティングゲーム
を作成してみる
使用する技術
OpenGLES を使用して作成していきます。http://ja.wikipedia.org/wiki/OpenGL_ES
※OpenGLES に関わる部分は解説致しません。Objective-C の仕様に関わる部分を基本に
解説致します。
使用するツール : Xcode
作成するファイル
・① ViewController : 画面描画を管理するクラス・② Sprite : 画面に表示されるオブジェクトを表現するクラス
処理の流れ
ViewController Sprite
viewDidLoadinitWithFile
glkView
render
updateupdate
ゲームのルール
・主人公は自動的に上下に動く。・画面をタップすると弾を発射する。・弾を敵に当てれば敵を倒せる。・敵は 5 体。登場する位置、動くスピードはランダム。・敵が主人公に触れる、もしくは弾切れ (5 発 ) になると負け。・敵を全て倒すと勝ち。
Objective-C のクラスの作成方法
・ Objective-C ではクラスは「インターフェース」と「実装部分」を分けて記述します。 (Java の interface とその実装みたいなもの )
・ インターフェース部には、クラスのインスタンス変数と、実装すべきメソッドを宣言します。
・ インターフェースはヘッダファイル ( 拡張子 .h) として作成し、実装部分 ( 拡張子 .m) のファイル内でインクルードします。ex. Test クラスを作成しようと思ったら、 Test.h と Test.m を作成することになります。
ViewController.h 解説
@interface クラス名 : スーパークラス名ここにインスタンス変数やメソッドを宣言します。@end
@property と書いた後に変数名を記述すると、ゲッタとセッタも定義したことになります。
NSMutableArray は変更可能な配列。 NSArray とすると変更不可の配列となります。
Sprite.h①#import <Foundation/Foundation.h>#import <GLKit/GLKit.h>
@interface Sprite : NSObject~ Framework に関わるプロパティは省略// 座標@property GLKVector2 position;// 加速度@property GLKVector2 velocity;// 拡大率@property float scale;// オブジェクトのサイズ@property CGSize contentSize;// 透明度@property GLKVector4 color;// 透明度の変化率@property GLKVector4 colorVelocity;
※ 頭に GL と付くものは、 OpenGLES のライブラリで定義されているクラスです。
Sprite.h②
~ 続き// 初期化処理
- (id)initWithFile:(NSString *)fileName effect:(GLKBaseEffect *)effect;// 描画処理
- (void)render;// 毎フレームオブジェクトの位置を計算するメソッド
- (void)update:(float)dt;// 当り判定
- (CGRect)boundingBox;@end
Sprite.h ② 解説
メソッドの定義方法- ( 戻り値の型 ) メソッド名 :( 引数の型 ) 引数名 ;頭に「 - 」を付けると、メソッドを定義したことになります。
(id) の id は型で、オブジェクトはどのクラスでも、 id という特別な型で表現されます。そのため、 initWithFile はオブジェクトを返却するメソッドということになります。
NSString は変更不可な文字列クラスです。NSMutableString は変更可能な文字列クラスです。
尚、 Objective-C では文字列は以下の様な形式で表現します。@" 文字列 "
後ほど使用例が登場します。
ViewController.m : viewDidLoad( 初期処理 )
~ Framework の初期処理は省略 self.hero = [[Sprite alloc] initWithFile:@"hero.png" effect:self.effect]; /// 主人公作成 self.hero.position = GLKVector2Make(860, 320); // 主人公の位置を設定 self.hero.velocity = GLKVector2Make(0, 300); // 主人公の移動速度を設定 [self.sprites addObject:self.hero]; // 管理用配列に追加 self.enemies = [NSMutableArray array]; // 敵管理配列生成 for (int i = 0; i < 5; i++) { // 敵オブジェクト生成 Sprite * enemy = [[Sprite alloc] initWithFile:@"jyoumu.png" effect:self.effect]; int rand = arc4random() % 640 + 10; enemy.position = GLKVector2Make(0, rand) // 敵の位置をランダムに決める ; rand = arc4random() % 100 + 10; enemy.velocity = GLKVector2Make(rand, 0); // 敵の速度をランダムに決める ; [self.sprites addObject:enemy]; [self.enemies addObject:enemy]; } self.bulletCount = 5; self.enemyCount = 5; self.bullets = [NSMutableArray array]; self.dogezas = [NSMutableArray array];
ViewController.m : viewDidLoad( 初期処理 ) ①解説
Objective-C では「メッセージ式」というものが頻繁に登場します。例えば以下のようなオブジェクト、 obj が宣言されていたとします。
id obj;
この obj が、 msg というメソッドを持っていた場合、以下のように呼び出します。
[obj msg];
これは obj に対し、 msg というメッセージを送信して、 msg メソッドを呼び出しています。これが「メッセージ式」と呼ばれるものです。メッセージ式は、オブジェクトが受信したメッセージを処理した値を返却します。この場合、 [obj msg] が、 msg メソッドの戻り値そのものとなります。
ViewController.m : viewDidLoad( 初期処理 ) ②解説
インスタンスの生成は以下の様に行います。
[ クラス名 alloc]
よって、以下の式は Sprite クラスのインスタンスを生成した後、initWithFile, effect メソッドを呼び出しています。
引数は 「 : 引数」のように表します。生成したインスタンスを self.hero という変数に代入しています。
self.hero = [[Sprite alloc] initWithFile:@"hero.png" effect:self.effect]; /// 主人公作成
尚、変数 self はその処理を行っているオブジェクト自身を指します。(Java でいう「 this 」のようなものです )この場合、 ViewController.m のインスタンスを指します。
ViewController.m : viewDidLoad( 初期処理 ) ③解説
オブジェクトを生成したら、その管理用の配列に追加しています。以下の例で言えば、自身のプロパティ sprites は配列なので、sprites の addObject メソッドを呼び出し、自身のプロパティの hero を引数として渡し、配列の要素としています。
self.hero = [[Sprite alloc] initWithFile:@"hero.png" effect:self.effect]; /// 主人公作成self.hero.position = GLKVector2Make(860, 320); // 主人公の位置を設定self.hero.velocity = GLKVector2Make(0, 300); // 主人公の移動速度を設定[self.sprites addObject:self.hero]; // 管理用配列に追加
ViewController.m : glkView( 描画処理 )
// 弾描画 for (Sprite * sprite in self.bullets) { [sprite render]; } // 主人公描画 NSLog(@"hero.position.y == %f", self.hero.position.y); if (self.hero.position.y >= 600) { self.hero.velocity = GLKVector2Make(0, -400); } else if (self.hero.position.y <= 30) { self.hero.velocity = GLKVector2Make(0, 400); } [self.hero render]; // 敵描画 for (Sprite * sprite in self.enemies) { [sprite render]; } // 弾が当たった時のエフェクト描画 for (Sprite * sprite in self.dogezas) { [sprite render]; }
ViewController.m : glkView( 描画処理 ) 解説
ログ出力は NSLog クラスを使用します。使い方は書式文字列と、それに対する引数を渡して使用します。C 言語の printf() と良く似ています。
NSLog(@"hero.position.y == %f", self.hero.position.y);
上記の場合、「 %f 」の箇所に、引数の「 self.hero.position.y 」が展開されます。 配列は以下のように要素を巡回して操作することができます。for (Sprite * sprite in self.enemies) { [sprite render];}
敵配列の要素を巡回して、全ての要素の描画処理を呼び出しています。
ViewController.m : update( 更新処理① )
for (Sprite * sprite in self.sprites) { [sprite update:self.timeSinceLastUpdate]; } NSMutableArray * bulletsToDelete = [NSMutableArray array]; NSMutableArray * enemiesToDelete = [NSMutableArray array]; for(Sprite * enemy in self.enemies) { if ( CGRectIntersectsRect(self.hero.boundingBox, enemy.boundingBox)) { [self viewAlertTitle:@" ゲームオーバー!! " viewAlertBody:@" 出向になった。。。 "]; } for (Sprite * bullet in self.bullets) { if ( CGRectIntersectsRect(enemy.boundingBox, bullet.boundingBox)) { [bulletsToDelete addObject:bullet]; [enemiesToDelete addObject:enemy]; [self generateDogeza:bullet.position]; self.enemyCount--; } } }
ViewController.m : update( 更新処理② )
for( Sprite * bullet in bulletsToDelete ){ [self.bullets removeObject:bullet]; [self.sprites removeObject:bullet]; } for( Sprite * enemy in enemiesToDelete ){ [self.enemies removeObject:enemy]; [self.sprites removeObject:enemy]; } NSMutableArray * toDelete = [NSMutableArray array]; for( Sprite * dogeza in self.dogezas ) { if ( dogeza.color.w < 0 ) { [toDelete addObject:dogeza]; } } for ( Sprite * dogeza in toDelete ) { [self.dogezas removeObject:dogeza]; [self.sprites removeObject:dogeza]; } if (self.enemyCount == 0) { [self viewAlertTitle:@" ゲームクリア!! " viewAlertBody:@" 出向を阻止した!! "]; }
ViewController.m : generateDogeza( 弾が当たった時の画像表示処理 )
- (void)generateDogeza:(GLKVector2)position { Sprite * dogeza = [[Sprite alloc]initWithFile:@"dogeza.png" effect:self.effect]; GLKVector2 emptyVector2 = GLKVector2Make(0, 0); dogeza.position = GLKVector2Add(position, emptyVector2); dogeza.colorVelocity = GLKVector4Make(0, 0, 0, -1); [self.sprites addObject:dogeza]; [self.dogezas addObject:dogeza];}
ViewController.m : touchesBegan( 画面をタップした時の処理 )
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { if (self.bulletCount == 0) { [self viewAlertTitle:@" ゲームオーバー!! " viewAlertBody:@" 声が出なくなった。。。 "]; } [self shotBullets]; self.bulletCount--;}
GLKViewController を継承したクラス内( 正確には UIResponder, または UIView を継承しているクラス ) で、上記名前のメソッドを実装すれば、画面をタップした時に自動的に呼び出されるようになります。
ViewController.m : shotBullets( 画面をタップして、弾を撃つ処
理 )
- (void)shotBullets { Sprite * bullet = [[Sprite alloc]initWithFile:@"message.png" effect:self.effect]; bullet.position = self.hero.position; bullet.velocity = GLKVector2Make(-200, 0); [self.sprites addObject:bullet]; [self.bullets addObject:bullet];}
Sprite.m : update( 更新処理 )
- (void)update:(float)dt { GLKVector2 deltaX = GLKVector2MultiplyScalar(self.velocity, dt); self.position = GLKVector2Add(self.position, deltaX); GLKVector4 deltaC = GLKVector4MultiplyScalar(self.colorVelocity, dt); self.color = GLKVector4Add(self.color, deltaC);}
※他殆ど Framework 的な処理だったので省略
動作確認
サンプルを持ってきたので試して見て下さい。
参考
・簡単な iPhone ゲーム制作の解説http://kozukamahiro.blog.fc2.com/blog-entry-3.htmlOpenGLES を使う為の方法や、ゲームを作る上でのテクニックなど、殆どの部分を参考にさせて頂きました。
・詳解 Objective-C 2.0 第 3 版http://p.tl/mrKAObjective-C の仕様を詳細に解説して下さっています。ボリュームは多いです。