ゲームが作れる!新人iPhone開発者のためのSpriteKit入門
新人iPhoneディペロッパーをにとってゲーム開発は、一つの大きな壁なのではないでしょうか。
「ツール系のアプリは作れるようになったけど、ゲームはちょっとハードル高いなー」って方に朗報です。
XcodeにはSpriteKitっていうゲームを作成するためのフレームワークがあるのです!
これで新人でも簡単にゲームアプリが作れちゃいます。
SpriteKitで作るiPhoneゲーム入門
1.プロジェクトを立ち上げてみよう
2.パーティクルで見栄えをよくしよう
3.ゲームを作ろう
3.1.仕様を考えよう
3.2.ターゲットや対象となるオブジェクトの作成
3.3.接触したときの当たり判定を実装しよう
1.プロジェクトを立ち上げてみよう
まずは、触ってみましょう。プロジェクト立ち上げて実行します。
スーパーマンみないなアイコンを選択します。デザインが一際目立ちますね。なんかフラットデザインに合ってないような…
立ち上げてみます!
触ったら戦闘機が永遠に増えていく…なんか怖いですね…
===========================================================
デフォルトであったファイルです。
・MainSceneクラス
・ViewControllerクラス
・AppDelegateクラス
===========================================================
MainSceneにおもに画面のレイアウトを書いていて、ViewControllerでSpriteKitのView貼っつけてその上にSceneとして取得しています。
ViewController.mのviewDidLoadが画面をロードした後に呼ばれるメソッドなのですが、そこで見慣れないクラスがちらほらあります。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
- (void)viewDidLoad { [super viewDidLoad]; // Configure the view. SKView * skView = (SKView *)self.view; skView.showsFPS = YES; skView.showsNodeCount = YES; // Create and configure the scene. SKScene * scene = [colorBBAMainMyScene sceneWithSize:skView.bounds.size]; scene.scaleMode = SKSceneScaleModeAspectFill; // Present the scene. [skView presentScene:scene]; } |
①SKView:SpriteKitのSpriteなどをのせる基礎となるViewです。
②SKScene:ゲーム内にある場面毎に用意する素材で基本的に一つです。
ちなみにSpriteはゲームに登場する「敵」であったり、「主人公」であったり、いわゆる背景とは違い、独立的に動作する絵みたいなイメージです。
showFPSとshowNodeCountが右下の数値です。FPSはFrame Per Second。要するに1秒に何フレーム?みたいな意味です。もっと言うとパラパラ漫画みたいな要領です。
showNodeCountはそのシーンに対して何Nodeあるかです。ちなみに上の画面ではラベルと戦闘機がNodeですね。
どちらもYESだから見えるんですよー!!
次はMainSceneです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 |
-(id)initWithSize:(CGSize)size { if (self = [super initWithSize:size]) { //ここにシーン表示時したいことを書く。 //背景色設定 self.backgroundColor = [SKColor colorWithRed:0.15 green:0.15 blue:0.3 alpha:1.0]; //HelloWorldラベル生成 SKLabelNode *myLabel = [SKLabelNode labelNodeWithFontNamed:@"Chalkduster"]; //テキスト、フォントの大きさ、位置を指定。 myLabel.text = @"Hello, World!"; myLabel.fontSize = 30; myLabel.position = CGPointMake(CGRectGetMidX(self.frame), CGRectGetMidY(self.frame)); //自分自身(MainScene)にラベルを追加。(描画) [self addChild:myLabel]; } return self; } -(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { //タッチされた際、呼ばれます。 for (UITouch *touch in touches) { //タッチされた位置取得。 CGPoint location = [touch locationInNode:self]; //スプライトを生成。 SKSpriteNode *sprite = [SKSpriteNode spriteNodeWithImageNamed:@"Spaceship"]; //スプライトのポジションをタッチ位置に指定。 sprite.position = location; //回転するアクション生成 SKAction *action = [SKAction rotateByAngle:M_PI duration:1]; //回転アクションを永遠にしてスプライトのアクションに追加。 [sprite runAction:[SKAction repeatActionForever:action]]; //自分自身(MainScene)にスプライトを追加。(描画) [self addChild:sprite]; } } -(void)update:(CFTimeInterval)currentTime { //FPS/1000s毎に呼ばれます。 } |
解説はコメントにて。
基本的なことはSceneに書けば、いいんですよ〜。
戦闘機がぐるぐると怖いので、
少し戦闘機らしい動きをしてもらいましょう!
これで降下して発進!みたいな感じですね!うん!怖くなくなりましたね。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { //タッチされた際、呼ばれます。 for (UITouch *touch in touches) { //タッチされた位置取得。 CGPoint location = [touch locationInNode:self]; //スプライトを生成。 sprite = [SKSpriteNode spriteNodeWithImageNamed:@"Spaceship"]; //スプライトのポジションをタッチ位置に指定。 sprite.position = location; //自分自身(MainScene)にラベルを追加。 [self addChild:sprite]; //ちっちゃくなって SKAction *scaleMin = [SKAction scaleTo:0.2f duration:1.0f]; //飛んでっちゃえーー SKAction *up = [SKAction moveToY:900 duration:1.0f]; //アクションを組み合わす。 SKAction *seq = [SKAction sequence:@[scaleMin, up]]; [sprite runAction:seq]; } } |
そしてこの一行を足すだけで仮想的な重力を適用できます!
1 |
sprite.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:sprite.frame.size]; |
是非試して下さい!(挙動は少し変ですが…)
2.パーティクルで見栄えをよくしよう
パーティクルってなんぞ?と、思った方は多いのではないでしょうか…
パーティクル・システム(英: particle system)はコンピュータグラフィック技術のひとつで、型のレンダリング技術では再現が難しい、ある種の曖昧さを持った事物をシミュレートするために使われる。(by.wikipedia)
つまり花火や星空や雨など表現するのに適したシステムです。
Spritekitはそれを簡単に追加できます!!
いやほんと超簡単ですよ!
まずXcodeに用意されています。
こんな感じで…
そう!新規ファイルを追加するかの如く、導入出来ます。というより右クリック>NewFile>Resourceですから如くではなく、普通に新規ファイルですね。
とりあえず入れちゃいましょ。
テンプレートも用意されていて至れり尽くせりですね。テンプレートは以下の8つです。
===========================================================
・Bokeh
・Fire
・Firefiles
・Magic
・Rain
・Smoke
・Snow
・Spark
===========================================================
今回はFireを導入します。すると、sksという見慣れない拡張子のファイルが生成されます。
中身はこんな…
右のパラメータを弄ればいろいろ変化します!結構綺麗で楽しいので是非試して下さい。
では次にコードの方に導入してみましょう。タッチした際、その座標に出現するようにしましょう。
Fireはパーティクルノード(パーティクルとパーティクルシステムを作成・制御するもの)が出っぱなしなので少し燃えたら勝手に鎮火するようにしましょう!
ParticlesのMaximumに0以上を与えてやると勝手に消えます!
1とかではしょぼいので50~200くらいで。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
//Fireパーティクルを発生するメソッド - (void)fireAppear:(CGPoint)firePos { //パスを当ててファイルを読み込みます。 //同時にパーティクルのオブジェクトを生成。 SKEmitterNode* emitter = [NSKeyedUnarchiver unarchiveObjectWithFile: [[NSBundle mainBundle] pathForResource:@"MyParticle" ofType:@"sks"]]; //ターゲットをself.sceneに設定。 emitter.targetNode = self.scene; //ポジションは引数(タッチ座標を受け取ります。) emitter.position = firePos; //そして魔法の呪文で表示。 [self addChild:emitter]; } |
1 2 |
//touchesBeganメソッドに以下の行を追加。 [self fireAppear:location]; |
これだけで発生します。試して下さい!
時には燃え上がるHelloWorldもいいですね!
これだけでも、とてもゲームの案が浮かび上がりませんか?
3.ゲームを作ろう
サンプルゲームを作ってみましょう!
3.1.仕様を考えよう
ゲームを作る上で重要なことはデザインであったり、コンセプトであったりと、山ほどあります!
ですが今回は「SpriteKitの使い方」なのでまずゲームの仕様だけを考え、その実装方法を解説していこうと思います!
===============================================
1.画面タッチで飛行機が発射!
2.飛行機から爆弾投下!
3.画面下部にある玉?共に爆発!
===============================================
仕様はこんな感じです。
今回のポイントは簡単に取り入れられちゃう重力世界ですね!
是非、下記のサイトを参考にして下さい!
公式リファレンス
参考にしたサイト1
参考にしたサイト2
そんでもって当たった際はパーティクルエフェクトを用いようと思います。さらに今回はBGMやSEなんかもいれちゃおう!
3.2.ターゲットや対象となるオブジェクトの作成
1章で作ったプロジェクトをベースとします。
1.プロジェクトを立ち上げてみよう
このベースプロジェクトに玉みたいなターゲットを作ります!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
- (SKNode*)createBall { //ボールのノードを生成。 SKShapeNode* ball = [SKShapeNode node]; //描画パスの生成。 CGMutablePathRef path = CGPathCreateMutable(); //乱数の生成。 CGFloat r = [self Rand:3 high:30]; //円のパスを生成。 CGPathAddArc(path, NULL, 0, 0, r, 0, M_PI * 2, YES); //ボールの描画パスを適用。 ball.path = path; //紫色が好きなので。 *ご自由に! ball.fillColor = [SKColor purpleColor]; //淵は透明色 ball.strokeColor = [SKColor clearColor]; //ポジションはまぁまぁランダムで ball.position = CGPointMake(CGRectGetMidX(self.frame), self.frame.size.height - r); //ボールにも重力を ball.physicsBody = [SKPhysicsBody bodyWithCircleOfRadius:r]; return ball; } |
よく見る描画するやり方をSKNodeに適用しています!これでボールを生成するインスタンスメソッドが完成しました。ここでRandというメソッドがありますが、これは少し考えてみましょう。。。
詳細は以下です。
Randメソッド:引数1から引数2の範囲でランダムの値を生成。
戻り値:float型
引数1:float型
引数2:float型
この実装はゲームを作る上で必須項目です。ここをしっかり理解すればたくさん応用できますよ。
このメソッドを
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
-(id)initWithSize:(CGSize)size { if (self = [super initWithSize:size]) { //ここにシーン表示時したいことを書く。 //背景色設定 self.backgroundColor = [SKColor colorWithRed:0.15 green:0.15 blue:0.3 alpha:1.0]; //重力世界へ誘う魔法 self.physicsBody = [SKPhysicsBody bodyWithEdgeLoopFromRect:self.frame]; //ボール一杯作っちゃう! for (int i = 0; i < 50; i++) { [self addChild:[self createBall]]; } } return self; } |
上記のfor文でたくさん呼んじゃおう!気付いた方もいらっしゃると思いますが、物理エンジンを導入しています。これだけでボールがランダムに描画されたポジションからブワっと下に落下します!(実際は速くて確認しにくいですが…)
気付かなかった人は読み飛ばしてますね…
ballノードは半径rの円に対して面積全体に適用しています。基本的にSpriteKitはこれで物理エンジンを加味してくれます!
例えばSpriteをaddChildした場合はbodyWithRectangleOfSizeメソッドの引数にそのSpriteのsizeを与えると適用されるんです!!
なんと便利な…
次にタッチする際に前々回の記事で作成した飛行機と同時に
ぽろっと落下する爆弾を作りましょう!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 |
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { //タッチされた際、呼ばれます。 for (UITouch *touch in touches) { //タッチされた位置取得。 CGPoint location = [touch locationInNode:self]; //ちゃんと落下してるのを確認するため画面半分より上で if (location.y >= CGRectGetMidY(self.frame)) { //飛行機を生成。 sprite = [SKSpriteNode spriteNodeWithImageNamed:@"Spaceship"]; //飛行機のポジションをタッチ位置に指定。 sprite.position = location; //飛行機の大きさを1/5にします。 sprite.xScale = 0.20f; sprite.yScale = 0.20f; //自分自身(MainScene)にラベルを追加。 [self addChild:sprite]; //スプライトを生成 *ボールの画像は用意してね。 SKSpriteNode* bomb = [SKSpriteNode spriteNodeWithImageNamed:@"bomb"]; //爆弾のポジションをタッチ位置に生成。 bomb.position = location; //爆弾の大きさも1/5にします。 bomb.xScale = 0.25f; bomb.yScale = 0.25f; //重力世界へようこそ! bomb.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:bomb.size]; //飛んでっちゃえーー SKAction *up = [SKAction moveToY:900 duration:1.0f]; [sprite runAction:up]; [self addChild:bomb]; } } } |
これで下の画像のような感じになります。デザインが全然イケてないのはこの段階では多めにみてください。
3.3.接触したときの当たり判定を実装しよう
当たり判定はゲームを作る上で不可欠な要素です。ですが実際は非常にめんどくさいのです。それがSpriteKitではとても簡単に実装できちゃいます!さっそく判定を追加してみましょう。
まずはコチラのグローバル領域に以下を宣言します。
1 2 3 |
static const uint32_t bombCategory = 0x1 << 0; //16進数の1に対して左に0(2進数分)シフト...つまり0倍!! bombCategory = 0001(2進数) = 1(10進数) = 1(16進数) 32ビット(8バイト)の整数型 static const uint32_t ballCategory = 0x1 << 1; //16進数の1に対して左に1(2進数分)シフト...つまり0倍!! bombCategory = 0010(2進数) = 2(10進数) = 2(16進数) 32ビット(8バイト)の整数型 static const uint32_t worldCategory = 0x1 << 2; //16進数の1に対して左に2(2進数分)シフト...つまり0倍!! bombCategory = 0100(2進数) = 4(10進数) = 4(16進数) 32ビット(8バイト)の整数型 |
このグローバル変数は、当たり判定を行う際の区別する定数として用います。
各々、生成時に以下のように与えます。
1 2 3 4 5 6 7 8 |
//当たり判定の定数を指定。(シーン全体) self.physicsBody.categoryBitMask = worldCategory; //当たり判定の定数を指定。(下のターゲットの玉) ball.physicsBody.categoryBitMask = ballCategory; //当たり判定の定数を指定。(爆弾) bomb.physicsBody.categoryBitMask = bombCategory; |
さらにbombには以下を追記します。
1 2 3 4 5 6 7 8 |
//衝突の際のMask bomb.physicsBody.collisionBitMask = ballCategory; //接触の際のMask bomb.physicsBody.contactTestBitMask = ballCategory | worldCategory; //衝突、接触の際の演算を正確にする。 bomb.physicsBody.usesPreciseCollisionDetection = YES; |
上記の三つの追加したコードがまず当たり判定の際の分類と相互作用になります。
そして次にコンタクトデリゲートをシーンに適用してデリゲートメソッドを実装します!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
-(id)initWithSize:(CGSize)size { if (self = [super initWithSize:size]) { //ここにシーン表示時したいことを書く。 ・・・ //当たり判定のデリゲートメソッドを実装するのに必要なデリゲートを適用! self.physicsWorld.contactDelegate = self; ・・・ } return self; } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 |
//接触の際に呼ばれるデリゲートメソッド - (void)didBeginContact:(SKPhysicsContact *)contact { SKPhysicsBody *firstBody, *secondBody; /* 引数のcontactには接触した際の情報を持っている。 そして先ほど設定したcategoryBitMaskから 接触したphysicsBodyを判断し、firstBody,secondBodyに振り分ける。 この条件文でfirstBodyに爆弾のphysicsBodyが格納されるね! */ if (contact.bodyA.categoryBitMask < contact.bodyB.categoryBitMask) { firstBody = contact.bodyA; secondBody = contact.bodyB; } else { firstBody = contact.bodyB; secondBody = contact.bodyA; } //firstBodyのcategoryBitMaskがbombCategoryならば if ((firstBody.categoryBitMask & bombCategory) != 0) { //secondBodyのcategoryBitMaskがballCategoryならば if ((secondBody.categoryBitMask & ballCategory) != 0) { //適当に「爆発するパーティクルを追加。 NSString *sparkPath = [[NSBundle mainBundle] pathForResource:@"spark" ofType:@"sks"]; SKEmitterNode *spark = [NSKeyedUnarchiver unarchiveObjectWithFile:sparkPath]; spark.position = secondBody.node.position; spark.xScale = spark.yScale = 0.7f; [self addChild:spark]; //ぶつかり合った爆弾と玉を除外。 [firstBody.node removeFromParent]; [secondBody.node removeFromParent]; //secondBodyのcategoryBitMaskがworldCategory(つまり画面枠)ならば } else if ((secondBody.categoryBitMask & worldCategory) != 0) { //爆弾のみ除外。 [firstBody.node removeFromParent]; } } } |
うむ。。。。。これで当たり判定を組み込んだゲームらしい実装が出来たんですよ!
ビルドして遊んでみましょう!まだいろいろ問題はありますが…
ここまでの知識が身に付けば自分でもゲーム作れるんじゃない?
となるのではないのでしょうか….そう!
自分でもゲームが作れるんです!!
ですが、SpriteKitは多機能で、もっと多くの物理空間やアニメーションを用意してくれているんですよ。それを取り入れてより面白いゲームを作ってみましょう!よりゲームらしい工夫を凝らしていきましょう!
それでは!
田村昂之
最新記事 by 田村昂之 (全て見る)
- ゲームが作れる!新人iPhone開発者のためのSpriteKit入門 - 2015年4月30日
- ブログハック!コンテンツマーケティングを成功させる珠玉のチップス35連発!! - 2015年1月13日
- 「速攻!1時間で作るPRサイト」 - 2014年11月27日