どうも。新人エンジニアのhondaです。 入社して早くも2ヶ月半が過ぎ、システム開発の全体像が少しずつ見え始めた感じです。
「Invaderゲームのiphoneアプリをつくろう」という研修課題に取り組んだので、今日はその成果を報告します。
Space Invaders
そもそもInvaderゲームってどんなだっけ?
というところから調べました。 もちろんInvaderがどんなゲームかはなんとなく知っていましたが、細かいルールなど知らない部分も多かったので、調べたところ
「画面の中央やや上方に縦に5段、横に11列の計55のインベーダーが現れる。インベーダーはまとまって横移動をしながら、端にたどり着くたびに一段下がって再び逆方向に進 行する。これを繰り返すことによって、だんだんと下に降りてくる。インベーダーが画面 最下部のプレイヤーの位置まで降りてきたら占領されたことになり、残機があってもゲー ムオーバーとなるために、それまでにインベーダーを全滅させなければならない。」 『wikipedia』
とのこと。これを参考に作ることにしました。
まず設計
まずはインベーダーゲームのクラス図を作成することから始めました。
結果的に下記のようなものが出来上がりました。(画像小さくてすみません。)
登場するオブジェクトは、invader(画面上方に55匹いる宇宙人)、tank(プレイヤー)、lazer(インベーダーの攻撃)、missile(プレイヤーの攻撃)、wall(レーザー攻撃を防 ぐ壁)、field(各キャラクターの座標を記録)、それとコントローラー(viewを管理、モデルに指示を送る)と各種Viewです。
当時はjavaでプログラミングの学習をしていたのですが、viewを作成する方法などをいま いち理解していなかったところもあり、なんだかこのままクラスを実装しても動かないの では?という感じのクラス図になってしまってます。
当初考えていたのは、各キャラクターのクラスが存在し、その座標を(二次元配列などで)管理するFieldクラスがあり、そのFieldクラスが各インスタンスの座標を比較して、衝突判定などを行うというものでした。
各キャラクターの座標の移動や、ミサイルとインベーダーの衝突でイン ベーダーが消えるといったことをこまめに確認していかないといけないのですが、そのた めに考えたのが、Fieldのインスタンスを、たとえば0.3秒ごとにnewして、Fieldのコンス トラクタ内で各キャラクターのインスタンスの座標を受け取り、衝突や移動を確認すると いう解決策でした。(ややこしいですね。つまりFieldの更新=Fieldインスタンスの生成、みたいに考えてました。)
結果的にはこんな面倒なことをする必要はなかったのですが、xcodeとObjective-cを学習 するまえは、こんな感じで考えてました。
Objective-cを学ぶ、そして実装
iphoneアプリを作るということで、入社後一ヶ月ほど勉強していたJavaから離れて、まず はObjective-cを勉強し始めました。
書籍にて、objective-cの基礎とxcodeの使い方を流し読みし、コードを一部写経しつつ一 通りの内容を学んでから実装に入りました。
流し読みしただけだったので実装では、コードの書き方が分からない、という事態が多発 しました。その度に参考書の該当箇所を参照しながら実装しました。
たとえば、画面にviewを追加するにはまずUIImageView クラスのインスタンスを生成し、そ こに画像をプロパティとしてもたせて、最後に親viewにaddするという手順を踏みますが、このコードを書くにも、その度に本を参照しながら書いてました。
また、配列の使い方にも苦労しました。objective-cでは配列に基本データ型を代入できないので、配列に入っている座標(数値)を取り出す際にintValueメソッドを使う必要が あります。これを知らずに配列から数値を変数に取り出して使っていると、いつの間にか 変数に13740といったすごく大きな数値が入っていたりして驚きました。これも、ちゃんと参考書の第三章の配列の使い方の箇所に載っていました。
ほかにもインベーダーがおかしな動きをしたり、尋常じゃない数のレーザー攻撃が降って きたりと、数え切れないほどのバグが発生したのですが、その都度googleや参考書を頼り ながらひとつずつ解決していきました。
そしてついにインベーダーゲームが完成しました。
コードをここにアップするのもあまりに冗長なので、完成したゲームのクラス図をアップ します(下記のものです)。
当初の設計とはかなり違ったものになりました。
まず、Fieldクラスはなくしました。よく考えてみれば、各クラスの座標はわざわざどこかにまとめて管理しなくても、その都度、各インスタンスの座標を比較して衝突を判定す ればいいということに気がつきました。
それから当初Fieldクラスのコンストラクタ(objective-cのイニシャライザ)に記述しよ うと考えていた、衝突判定やViewの移動といった処理は、他のクラスとしておのおの切り 出しました。基本的にViewControlerがこれらのクラスのメソッドを(timerをつかって) 一定時間ごとに呼びだすことにして、処理を行わせました。
model側とview側の両方にinvaderやtankといったクラスが存在しますが、これはmodelとviewを分けて、viewはmodelを参照するだけにするという形にしたかったので分けました。 model側の座標はx = 0 ~ 22, y = 0 ~ 11の範囲の整数値で持っています。viewはその座標値に定数を掛けて、画面上の座標を決定しています。
感想
インベーダーアプリを作ってみて、かなりいい勉強になりました。
何より、アプリをひとつ作ったという達成感があります。
これまで書いてきたプログラムと違って、viewが存在するということもあり、自分がコー ドで書いているものがそのたびに形になっていくのがうれしかったです。ものをつくって いるという感覚があって、没頭して作業することができました。
それから、実装していく中で感じたのが、ものをつくるのは問題解決の繰り返しなんだと いうことです。
今回、実装していく中でバグが発生することが多々あったのですが、その たびに原因を追究して、代替策を考え、実装してみて狙い通り動作するかを試すというこ とを繰り返してました。
ときには問題にはまってしまって解決するのに数時間かかるよう なこともありました。ただ、何度もそれを繰り返しているうちに、問題が発生してから原 因を探るスピードがあがっていくのを感じました。それもまた、大きな収穫かと思います 。
総じて、ひとつのものを作り上げることの苦労とやりがいを感じた課題でした。 新しい言語(objective-c)の習得に始まり、ツール(xcode)の使い方を学び、実装では問題 を解決しつづけるというなかなか密度の濃い内容だったと思います。
この課題を通して得た、地道にこつこつと問題を解決していく考え方をしっかり今後に生 かしていきたいです。