ゲーム作りをSwiftからUnityに変えた理由

#はじめに
僕はもともと`Objective-C`の時代からゲームを作っていました。
そして半年前から`Unity`でゲームを作るようになりました。
本記事では、この変更の理由と`Swift`と`Unity`のテクノロジーの違いを書いています。

#僕が使ったテクノロジーについて
ここでは僕が使ってきたテクノロジーとそのテクノロジーを使わなくなった理由を書いていきます。

##OpenGL2
ゲームと言ったら動的に動く物！
という理由で調べたところアニメーションなどに適しているのが`OpenGL`だったため、一冊の本を買って`OpenGL`を学びました。
`OpenGL`を`Objective-C`で制御してシューティングゲームを作りました。

###辞めた理由
`OpenGL`は2Dのゲームを描画するための技術なのですが、知識がないとかなり大変です。
例えば`PNG`ファイルを取り込んで画面に出すだけでもかなり多くのコードを書く必要があります。
また、制御を誤ると画面がちらついたり1フレーム前に描画した画像が残像のように残ってしまったりします。

例えばドラクエのようにマップ内を歩き回る処理を書く場合は、移動のたびに背景のマップを全描画する必要がありました。（僕のスキルでは）

とにかく難しくコード量も多いため３作品ほど作ったところで他のテクノロジーに変えることにしました。

##Cocos2d-iPhone(SpriteBuilder)
「OpenGLを直接使わずにOpenGLを扱える技術が何かないか？」
と調べたところ`Unity`と`Cocos2d`が見つかりました。

この時に`Unity`を選択すればよかったのですが、当時の`Unity`は3Dに特化していて僕には敷居が高い物でした。
また、`Cocos2d-iPhone`は当時の最新言語である`Swift`でコーディングができたため勉強も兼ねて`Cocos2d-iPhone`を選択しました。
（おかげで当時業務で使っていたSwiftのスキルが上達しました。）

`Cocos2d`は`OpenGL`を容易に操作できる上に`Tiled`というタイルマップのライブラリーもサポートしていたため開発速度は一気に向上しました。
（`Cocos2d-iPhone`を使う前は自前で`Tiled`のデータを読み込んでいたので、バグも多くコード量も多かったのです。）

正直この技術最高じゃない！と思ってました。

###辞めた理由
しかし、僕がこの`Cocos2d-iPhone`に飛びついた瞬間に`Cocos2d-iPhone`のサポートは終わってしまいました。
サポートが終わってもしばらく使っていたのですが、XCodeがアップデートするたびに`Cocos2d-iPhone`の中身を書き換えないと動かない状況に陥りました。
（この作業は本当に苦痛でした。サポートされていないため文献もなく、`Cocos2d-iPhone`の中身をあてもなく彷徨って変更して確認して・・・という地獄のような作業でした。）
ここで僕は再び露頭に迷ってしまいました。

##SpriteKit
`OpenGL`を直接触るのは嫌だし、どうしようかと思っていたところにApple社から`SpriteKit`が発表されました。
これを使えば`OpenGL`を直接触らずに操作ができる上に、Apple社がサポートしているからサポートが打ち切られる事はないと思い飛びつきました。

`Cocos2d-iPhone`の二の舞は嫌だったので`Cocos2d-x`を選択する事はありませんでした。
（C++を触るのが嫌だったという理由もあります。）

###辞めた理由
しかし、`SpriteKit`は`Cocos2d`に比べると様々な点で劣ってました。
例えば`Tiled`のサポートは無い（途中で中途半端にサポートされましたが・・・）ため、マップを作る処理は自前でした。
また、`Scene`の作成用のツールを使っているととにかく落ちまくりました。

長い間使っていたのですが、なかなか便利になりませんでした。
どうやらApple社はこのテクノロジーを改善していく気が無いのだと感じたのです。

##Swift
上記の理由から`Apple`社純正のSwiftでUIよりもゲーム性に特化したゲーム作りに舵を切り始めます。
`SpriteKit`はアニメーションが必要なところだけで使おうと割り切りました。

###辞めた理由
純正なのでUI周りの作成は問題がなかったのですが、アニメーションを入れるために`SpriteKit`を入れると座標計算方法が違うため`xib`ファイルと`sks`ファイルでの操作性を統一させるのが困難でした。

もう少しアニメーションを入れたいと思っても、上記の理由で煩雑なコードになるため実現ができませんでした。

#Unityに変えた理由
このような紆余曲折があって`Unity`にたどり着きました。（すごく回り道をした気分です。）
この時代になると`Unity`も2Dゲームの実績が多くなってました。

`Unity`を選んだ大きな理由は以下になります。

* 僕が調べた限りでは一番メジャーだった。
* クロスプラットフォームだった。
* 文献が数多く存在していた。
* Unityだけで完結していると思った。（実際はEditerはVBなどの選択をする必要があった）
* C#で書ける。（僕はC#の経験者だった。）

これらの理由から`Unity`でゲームを作ってみようと決めました。

##戸惑った点
`Unity`を始めた時に一番戸惑ったのが`Scene`という単位でゲームを作る事でした。
`Xcode`でゲームを作る場合は`StoryBoard`を使ってスタート画面を設定したり、`AppDelegate`から`sks`や`xib`を呼び出せるので導線がわかりやすかったのですが`Unity`にはこのような明確な導線が見当たらなかったので戸惑いました。
「`Scene`間でのデータのやりとりはどうすれば良いのか？」なども戸惑いました。

共通部品の作成方法である`Prefab`についても開発者ごとに意見が違ったため、どの方法がベストなのだろうか？と悩みました。

あと、フォルダの名前によって役割が変わるためどのようなフォルダ構成にすれば良いのか悩みました。
（これは今も悩んでます。）

ついでに言うとモダン言語である`Swift`から若干レガシーな言語である`C#`に戻ったので、コードを書く時に不便だなーと思いました。
（もちろん便利な点もありますが、やはり後発言語の方が書きやすいです。C#のオプショナルは貧弱なので、Swiftから移ってくるとコードに違和感があります。ラムダも冗長だし・・・とこの辺りは書き出すと止まらないです。）

##変えて良かった点
書くまでもなくOpenGLやMetalという下位層のレイヤーを触らなくて済むのがUnityの素晴らしい点です。
`SpriteKit`も下位層は触らなくても良いのですが、エディターがよく落ちる上に扱えるオブジェクトが少ない（テキストボックスさえ使えない）レベル。なので、これだけでもUnityに置き換えた大きなメリットです。

###UI周り
これが一番の恩恵です。
`Unity`は`UI`周り処理がとても書きやすいです。
`SpriteKit`を使うと途中で落ちてしまうことが多々ありましたが、`Unity`ではあまり落ちませんでした。（稀に落ちる）
それに、煩雑なUIを作ることに長けている上に、共通部品である`Prefab`の設計など本当に秀逸でした。

ライティング周りも少し調べれば2DLightなどがあったため雰囲気を出しやすかったです。

とにかくUI周りに関しては、Googleで調べれば大抵の事が実現できたのが良かったです。

###クロスプラットフォーム
他にも各プラットフォームのサポートが手厚いため多少の手間で`iOS`と`Android`の対応ができるのもよかったです。
C#はコード量が若干多いですが、慣れるとあまり気になりませんでした。

###音源周り
これもUnityがサポートしていて、細かい設定周りのサポートが簡単（本を参考にしてますが・・・）に設定できるのが良かったです。
（これ、XCodeで実装すると音が鳴らないタイミングとかがありました。）

###タイルマップ
`Cocos2d`に比べると弱いですが、タイルマップ周りの描画がサポートされていたのは助かりました。
（配置したタイルに情報を埋め込みたかったのですが、これはサポートされてませんでした。）

###ローカルデータの扱い
XCodeでローカルデータを扱うと、フォルダの階層がとても深い上にXCodeのバージョンを上げると紛失してしまいます。
しかし、Unity内部でのローカルデータは固定フォルダに出力されるのでUnityのバージョンが変わってもローカルデータがなくならないのは地味に便利でした。
（Unityはバージョン変更が容易にできます。gitで管理しておけば元に戻すのもあまり大変ではありません。）

###FPSタイマーを自作しなくて良い
UnityはFPSタイマーで動くことが前提になっているので、この辺りをの処理を自作で作らなくて良いです。
`SpriteKit`の場合は`Move`などのメソッドでオブジェクトを動かすのですが、`C#`にはコルーチンが用意されているためアニメーション周りでもそこまで困りませんでした。
（FPSとメソッドで移動させるのについて、どちらが良いかは今だにわかりませんが`iTweet`みたいなライブラリがあるのを見るとメソッドで動かしたい人がUnityにも居るようです。）

僕はOpneGL時代に自作FPSを作って苦労してたので、コルーチンがあるだけで随分楽にコードがけるようになったなーと思いました。

###ScriptableObjectが使える。
XCodeで設定周りを記述するときは`Plist`が主力になります。
この`Plist`はある程度のレベルなら良いのですが、データが増えてくると管理が煩雑になります。
その点はUnityの`ScriptableObject`を使うと(癖はありますが）スキーマーを自在に作れるので管理が楽にできました。
他にもExcelなどを使ってインポートするなどもできるようですが、これはXCodeでも出来るので割愛。
（僕は出来るだけ同一環境でデータを管理したいのでExcelなどでコンバートする二重管理方式があまり好きでは無いのです・・・）

###課金周り
スモール実装なら驚くほど簡単です。
IAPを使えばほとんどコードを書かずに実装できます。
レシートチェックとかを入れると。。。まぁ、それはどの環境でも大変ですよね。

###AssetStoreがある
欲しいプログラムや素材をすぐに購入できる仕組みがあります。
これは本当にありがたいです。
僕はまだ`Unity`でゲームをリリースしていませんが、このストアでは数万円〜もしくはもっと買い物をしたかもしれません。
それだけ便利です！

###その他
他にもありますが、とりあえず思い出せる点を書き連ねてみました。
個人的には上記に記載しているUI周りとクロスプラットフォーム周りの恩恵が一番大きいと思っています。

##変えてキツかった点
とはいえ、やはりキツかった点もあります。
戸惑った点にも書いていますが・・・

###プログラム周りが弱い
モダン言語は大抵において過去のプログラムの良いところを採用して作られている場合がほとんどです。
なので、15年近く前に作られたC#とは大きな差があります。
`RxSwift`などのライブラリは`UniRx`などで代用が効くのが良いのですが、、、基本構文はなかなかの面倒臭さがあります。

* Enumにメソッドがつけられない。（拡張は出来るがコードが散らばる）
* EnumのCaseに漏れがあってもコンパイルエラーにならない。（賛否両論があると思いますが、Swiftのこの機能は生産性を高めます。）
* ラムダ式で`$0`などの省略が使えない。（簡単なラムダ式でも冗長になってしまいます。）
* オプショナル型が弱い（一応`?`などは使えますが、オプショナル型ではないのでnullチェックが必要。Nulltable型のコードが冗長になる。）
* キャストが弱い（`if let = optional`が使えないのはもちろんで、親オブジェクトに暗黙的にキャストしてくれないので一つ一つジェネリクスのコードを書く必要がある。これ、地味に面倒です。）
* Switch文で同じcaseが使えない。（`case a,b:`ということが出来ない上に`break`必須なので、同じような属性の値でもコードを書く必要が出てくる。）
* 変数のスコープが少しわかりにくい。（if文の中だけで使う変数をif文を抜けてから再定義する事ができない。これは僕のプログラムの書き方の問題もあるけれど`item`などの変数をスコープ外と内で別の意味で使わせてくれても良いのに・・・）
* `flatMap`系がない。（似て非なる物ならあるのですが、、、まぁ、これは言語特性の問題なので仕方なし。僕はflatMap系を多用するのでちょっと困りました。）

と、まぁ、これも書き出すとキリがないのですが・・・
`Swift`から`Unity`に移った人は`ラムダ`と`オプショナル`、`Enum`に関して「え？」と思うかもしれません。

###多画面対応
`Unity`って様々なプラットフォームに対応しているのですが、多画面の対応が少し弱い感じがしました。
`XCode`なら`Constraints`を使って`ラベルが横に長くなったり縦に長くなっても`対応に大した問題はありませんでした。
しかし、`Unity`ではこれができません。
基本的には画面のどのあたりに配置するのか？と言うところまでしか対応できません。（多分）

ただ、これは`SpriteKit`を使った場合に同様の問題にぶち当たるので処理速度を優先した結果なのかもしれないと思っています。

###スクロールが貧弱
`Unity`のスクロールは何も考えずに使うと大量のメモリを消費するようにできています。
ついつい500個くらいのアイテムをスクロールに表示すると上部を表示するだけでも500個のデータがレンダリングされてしまいます。

ただ、これも`SpriteKit`と比べると・・・`SpriteKit`にはスクロールがないですからね・・・（確か
XCodeのスクロールは癖がありますが、高速にレンダリングできるようになっているので安心して使えます。

結論として、僕はこのスクロール問題を解決するために自作しました。

###Firebaseに冷たい
Firebaseだけじゃないかもしれません。サードパーティーはAndroidとiOSで別々に作られている場合があるためAndroidとiOSで設定の仕方が違います。
Androidはそんなに問題ないのですが、iOSは最終的に`Pods`を使う必要があるので`Unity`ネイティブの人はインストールに難儀する気がします。
あと、地味にAndroidとiOSで処理結果が違う時があります。（アナリティクスのみ。`Realtime database`は同じ感じがします。Firestoreはサポートされていません。）

###多言語は自前
`Unity`は全世界で使われているので多言語対応もバッチリかと思ってましたが、これは自前でやるしかありません。
それはXCodeでも実は同じ（XCodeに用意されている機構は使いにくいので大体の人は自前で実装すると思います。）。

###イニシャライズに罠がある
`RuntimeInitializeOnLoadMethod`を使うとアプリが起動する前に処理ができるのですが・・・
ここで設定ファイルの読み込みとか、別スレッドを発行とか色々やると稀にアプリが落ちる事があります。
これはただの癖の問題ですが、ついつい便利だから初期化処理をすべて詰め込んでしまうと謎のバグ（起動直後に固まる）に遭遇します。
これ、原因に気がつくまで２〜３週間くらいかかりました。

###動かないバージョンがある
これも仕方がないのですが、Macのバージョンが`Catalina`になったてからしばらくの間は`Unity`から実機転送しても動かない時期がありました。
XCodeの場合は同じApple社が作っているので実機転送後に動かないことはなかったのですが・・・

なので、Unityのバージョンを変える場合は注意が必要です。

また、Unityのバージョンを変えるとXCodeの`Pods`を更新する必要があったり多言語用ファイルを作り直す必要があったりするので鬼門です。

#最後に
最後に色々問題点を書きましたが、これは僕が`Unity`に慣れていない事も原因ですし乗り越えられない問題でもないです。
むしろ開発効率を見ると遥かに`Unity`の方が高いです。
ゲームを作りたい人で、上記に書いている事がクリアーできる人なら`Unity`を選択する事をお勧めします。

ただし、業務系に関しては`Swift`と`Kotlin`で別に書く事をお勧めします。
OSに関して直接指示が出せないのは少し厳しいのと、業務系のプログラムはそこまで大きくならない（サーバーで処理を吸収できるはず）ので個別に書く事をお勧めします。