はじめに
こんにちは、東京大学 工学部 電気電子工学科というところに通っている学部3年生です。
インターンなどの機会を利用させていただきながらプログラミングの学習に励んでいます。
大学でのプログラミングに関する授業はアルゴリズムや計算量、CPUなどの低レイヤーな分野を中心的に扱うことが多いです。
そのため、Webサービスを利用しているユーザに近い部分を扱うことが少なかったりします。
この記事がそのあたりに興味を持つきっかけになればとても嬉しいです。
この記事の目的
次の理由から、SwiftによるiOSゲーム開発を行うチュートリアルを作りました。
よりユーザに近い部分を開発することに興味を持っていただければ嬉しいです。
- 自分が持っているiPhoneで作ったものが動くと楽しい
- Macを持っていればXcodeをダウンロードするだけで開発構築が完了する
- メモアプリとかを作るよりはゲームを作ったほうが楽しいんじゃね?
この記事の内容
- Swiftというプログラミング言語を使ってiPhoneのシューティングゲームを作ります
- フレームワークにはSpriteKitというものを使います
- iOSなのでMacを持っている人が対象です
- 当方、Swiftでのサービス開発の経験はありますが、ゲーム開発の経験はないので何かおかしいところがあるかもしれません
- Xcode 9.2(Swift 4.0.3)を利用するのでここから「Xcode 9.2」をダウンロードしておいてください
- Swiftの文法(変数定義やif文、for文など)について軽く知っておくと理解がスムーズになると思います(軽く検索すると出てくるのでこの記事では割愛します)
- Xcodeのダウンロードには時間がかかるので、その間にSwiftの文法をちょっと見てみるのはいかがでしょうか!
シューティングゲームを作ろう
完成品
本チュートリアルで作成するゲームの完成品コードはここに置いています。
リンク先に今回作るゲームのGIF画像が置いてあるので、どんなものを作るか知りたい人は見てみてください。
プロジェクトの作成
ゲームアプリを開発するためにプロジェクトを作成します。
- Xcodeを開く →
Create a new Xcode project
を選択 →Game
を選択してNext
- Product Nameには「shooting-game」と入力
- Teamは「None」のままでOK(なんでもOK)
- Organization Nameは自分の名前とかにする(なんでもOK)
- Organization Identifierには「com.自分の名前」とかにする(なんでもOK)
- Languageは「Swift」に
- Game Technologyは「SpriteKit」に
- チェックボックスは全部外しておいてOK
ビルドできるか確認
プロジェクトが作成できたら次のような画面が表示されているはずです。
まずは何もせずに左上の「三角ボタン」を押してください。
デフォルトで生成されているコードがビルドされて「Hello, World」と表示されたiPhoneシミュレータが立ち上がるはずです。
試しに画面をタップしたりスワイプしたりしてみましょう。なんか四角のリングみたいなのがフワフワ出てくるゲーム(?)で遊べます。
ちなみに、三角ボタンの横の「iPhone 8 Plus」を変更すると好きなiPhoneでビルドすることができます。
コーディングの準備
コーディングを始める前に、デフォルトで記載されているコードを削除して、ゲームに使用する素材画像をインポートします。
-
GameScene.sks
というファイルを開いて「Hello, World」というラベルを選択してDelete -
GameScene.swift
を開いていらないコードを消す- ここを参考にコードを削除してください
- この記事ではこんな感じでGitHubの「Pull Request」を参考に実装を進めていきます
- GitHubもPull Requestもよく分からなくても大丈夫です。背景が赤い部分は消すコード、緑の部分は追加するコードということを覚えておけば問題ありません。ファイル名も書いてあるのでどのファイルのコードを触ればいいかも分かります。
- ここの画像をすべてダウンロードする(すべてフリー素材です)
-
Assets.xcassets
を開いて、先程ダウンロードした画像をドラッグ&ドロップする
メニュー画面とゲーム画面
アプリ起動時に表示されるメニュー画面からゲーム画面に遷移するところまでを作ります。
ちょっとこのあたりはXcodeの使い方を知らないと分かりにくいところなので、画像多めで解説していきます。
-
Main.storyboard
を開くと次のような画面が表示される
- 黒い画面の横あたりにに右下の「View Controller」をドラッグ&ドロップする
- 黒い画面の左側に付いている矢印マークを先程追加した白い画面に持っていく
- 右下の検索ボックスに「button」と入力して出てくる「Button」を白い画面の中央に持ってきて、そのボタンをダブルクリックして「Start」というテキストに変更する
- 先程追加したボタンを**「Ctrlキー」を押しながら**右側の黒い画面にドラッグ&ドロップする。2つの画面の間に矢印が表示されれば成功です。
- 左上の三角ボタンを押してRunしてみましょう。Startボタンをタップすると黒い画面に遷移するようになっているはずです。(ちなみに、
Cmd + R
でRunすることもできます)
地球の追加
ゲーム画面の下の方にずっと表示される地球を追加します。
ここを参考にGameScene
クラスのdidMove(to:)
メソッドにコードを追加しましょう。
-
didMove(to:)
メソッドはゲーム画面に遷移したときに自動的に実行される - 地球を表示する処理はこのメソッド内に書けば良い
-
SKSpriteNode
はゲーム画面に表示するオブジェクトのクラスです -
CGPoint
は画面内の座標(原点は画面中央)を表すクラスで、そのインスタンスをearth
のposition
プロパティに代入します -
earth
のスケールや位置を調整して、最後にaddChild(_:)
を呼んで画面に地球を表示します
宇宙船の追加
ゲーム画面に「端末の傾きで横方向に移動し、タップでミサイルが発射される宇宙船」を追加します。
- ここを参考に宇宙船を画面に追加する
-
ここを参考に、端末の傾きによって宇宙船が動くようにする
-
CoreMotion
というフレームワークをimportし、CMMotionManager
というクラスを利用すると端末の傾きを取得できます - ここからは端末の傾きを取得する必要があるため、iPhone Simulatorではなく実機でビルドする必要があります。ここなどを参考にお持ちのiPhoneでビルドしてみてください。iPhoneを持っていないなら、少し難しいかもしれませんが、画面タップで宇宙船が左右に移動するように変更してみましょう。
-
-
ここを参考に、画面をタップすると宇宙船からミサイルが発射されるようにする
-
SKAction
クラスの機能を使ってSKSpriteNode
を動かすことができます -
SKSpriteNode
クラスのメソッドrun(_:)
を利用して実際に指定したSKAction
を実行させることができます
-
小惑星を追加
ゲーム画面に「1秒ごとにランダムな位置に出現して迫ってくる小惑星」を追加します。
ここを参考に一定時間ごとに小惑星を出現させましょう。
-
Timer
クラスのscheduledTimer(withTimeInterval:repeats:block:)
メソッドを使うと一定時間後にblock
に渡した関数を実行してくれます -
repeats
をtrue
にすると一定時間ごとに同じ処理を行ってくれます -
arc4random_uniform(_:)
という0から引数の間のランダム値を生成してくれる関数を利用して出現する小惑星を3種類から選択しています
衝突処理
「ミサイルと小惑星」、「小惑星と宇宙船」、「小惑星と地球」の衝突処理を実装します。
-
File
→New
→File
→SpriteKit Particle File
を選択してNext
- Particle temprateが
Fire
になっていることを確認してNext
-
Explosion.sks
という名前にして保存 -
ここを参考に衝突処理を行うための準備を行う
-
SKSpriteNode
にはphysicsBody
というプロパティがある - そこに
SKPhysicsBody
のインスタンスを代入するとSKSpriteNode
が当たり判定を持つようになる -
SKPhysicsBody
にはcategoryBitMask
、contactTestBitMask
、collisionBitMask
という3つの衝突に関するプロパティがある - このあたりは説明すると少し長くなるので、今回は
categoryBitMask
にはそのノードのIDに相当するもの(2進数)、contactTestBitMask
にはそのノードが衝突する相手のID(2進数)を代入すると良いということだけ覚えておけば十分です。
-
-
ここを参考に衝突時に小惑星が爆発するようにする
- 衝突すると
didBegin(_:)
が自動的に呼ばれる -
didBegin(_:)
に渡ってくるcontact
はbodyA
とbodyB
を持っていて、それぞれ衝突した2つのノードのSKPhysicsBody
になっている - しかし、例えば小惑星と宇宙船が衝突したとき、
bodyA
とbodyB
のどちらが小惑星でどちらが宇宙船かどうかは分からないため、categoryBitMask
を利用して自分で判別してあげる必要がある - 爆発のエフェクトには
SKEmitterNode
を使用する。特にSKEmitterNode
に特有の処理は行わないのであまり気にする必要はない
- 衝突すると
スコアとライフ
「小惑星をミサイルで破壊したときにスコアが増える処理」と「宇宙船と地球が小惑星に衝突したときにライフが減る処理」を実装します。
-
ここを参考にライフを導入する
-
didMove(to:)
の中でSKSpriteNode
の配列であるhearts
を初期化する - 小惑星が宇宙船か地球と衝突したときに
hearts
を1つ減らす処理を行う
-
-
ここを参考にスコアを導入する
- スコアの表示にはテキストを表示するためのノードである
SKLabelNode
を利用する - スコアを管理する
score
プロパティにdidSet
を持たせることで、score
に代入する度にscoreLabel
のtext
プロパティを変更する処理を行うことができる - ミサイルが小惑星と衝突したときにスコアを加算するのを忘れずに
- スコアの表示にはテキストを表示するためのノードである
ゲームオーバー
ライフが0になるとゲームが終了するようにします。
-
ここを参考に、ライフが0になったときにゲームを一時停止するようにする
-
isPaused
プロパティにtrue
を代入するとゲームを一時停止できる - そのときに
timer
プロパティのinvalidate()
を呼んでおく
-
-
ここを参考に一時停止してから1秒後にメニュー画面に戻るようにする
-
GameViewController
クラスのdismiss(animated:completion:)
メソッドを呼ぶと最初のメニュー画面に戻ることができるが、GameScene
からGameViewController
のメソッドを直接呼ぶことができない - よって
GameViewController
からGameScene
のインスタンスを作るときにself
をGameScene
に渡すようにして、gameOver()
メソッドの中で渡ってきたgameVC
(型はGameViewController
)のdismiss(animated:completion:)
を呼ぶ
-
ベストスコアの保存と表示
ここを参考にゲーム画面にベストスコアを表示するようにしましょう。
- 値を保存するのには様々な方法がありますが、今回は一番簡単な
UserDefaults
を使う方法で行う -
AppDelegate.swift
のapplication(_:didFinishLaunchWithOptions:)
に書かれた処理は初めてアプリが起動されたときにのみ実行される -
UserDefaults
では辞書型で値を保持していて、キーに対する値をAppDelegate
で登録する - ベストスコアが更新されていたら
UserDefaults
に保持している値を更新する
難易度を上げていく
このままでは無限にプレイできてしまうので、小惑星が迫ってくるスピードを時間が経つにつれて早くなるように変更します。
ここを参考に、時間とともに小惑星のスピードを上げていくようにします。
ここは今までの知識を使えばできるので答えを見ずに実装してみてもいいかもしれません。
完成です!
これでとりあえずは完成です!
お疲れ様でした!
ちなみにこのゲームの僕のベストスコアは390点です。
皆さんはどうでしたか?
アレンジする
しかし、このアプリにはまだまだ改善点があります。
例えば以下のようなものがあるのでできそうなものから改善に挑戦してみましょう。
もちろん自分でオリジナルの機能を考えて実装してみるのも素晴らしいです!
- 一時停止機能を追加する
- 最初から始める機能を追加する
- 途中でやめる機能を追加する
- 難易度の上げ方(今は単純に小惑星のスピードを上げているだけ)を工夫する
- 左右だけでなく上下にも動けるようにする
- ときどきアイテムが落ちてきてミサイルが強くなったりする
-
didMove(to:)
にいっぱいコードを書いていて可読性が悪いのでコードを整理(リファクタリング)する
おわりに
最後まで読んでいただきありがとうございました!
今回はSwiftでiOSゲームを開発しましたが、ゲームに限らずユーザに近い部分の開発に興味を持っていただければ嬉しいです。
もちろんSwiftを使ったiOSアプリ開発もオススメです。
まずは今回作ったゲームを改良してもいいですし、オリジナルのアプリを作ってみてもいいですね。
ゲーム以外のアプリを作るなら、ちょっと内容が古い記事になってしまっていますが、僕が以前書いた記事が参考になるかもしれません。