新しいこと、枯れたこと問わずサムザップ大庭が興味を持ったものを調査、生活の効率を求める完全趣味の技術ブログ。基礎を大事にしています。#2の12/11の記事です。※後編リリースしました。株式会社サムザップでUnityエンジニアをしている大庭です。グラフィックデザイナー、Flashデベロッパーを経て、現在はUnityエンジニアでスマホゲームを作っています。Unity歴は6年程です。これから紹介していく内容は、開発初期に後回しにされがちな、でも初期にやっておいた方が良い事ばかりです。僕はUnityエンジニアとしてプロジェクトを進める上で以下の事を重要視しています。動くものをいち早く作ってメンバーに共有ワークフロー構築、開発手法を整える事が最優先いきなり正解は出ないので、不都合が出てきたら修正していくスタイル修正見直しの数を繰り返して精度を上げていくこの思想も取り入れた形でリストアップしました。新規ゲームプロジェクトがスタートし、あなたはUnityエンジニアとして参画する事になったと想定します。ゲーム仕様はまだ真っ白な状態で、計画しているモック開発の要件も決まっていません。「あ〜仕様も決まってないし、やる事がなくて暇だな〜」って、んな事ぁない!!仕様が無くてもやる事はいくらでもあります。今回のタイトルを見て「42Tips!?多すぎじゃない・・・!?」と思ったあなた!ぜひ読んでみてください。今まで新規開発に携わる事の多かった僕が思いつく「仕様がなくてもUnityエンジニアが開発序盤にやれる事」をツラツラ紹介していきます。決める本記事の読者はUnityエンジニアが多いと思いますので、開発環境はUnityをベースに考えていきます。開発環境UnityについてUnityのアップデートに関して、随時追いかけていくのが良いと考えています。バージョンが離れてしまうと、APIのインターフェースに変更が入っていたりして、思わぬ修正が発生する場合があります。メジャーアップデート直後はバグが多い可能性もあるので、アップデートの通知が来てもすぐにバージョンアップせず少し様子を見る事にしています。20XX.5、20XX.6辺りまでマイナーバージョンが上がってくると、比較的安心してバージョンを上げる気持ちにさせてくれます。ソースコードのエディタについてコードフォーマットの共通化に時間をかけたくない自動でフォーマットする機能があるUnity開発をする上で機能が充実しているソースコードを書くメンバーは、以上の点を満たすRiderを使うようにしています。コードフォーマットの設定はカスタマイズする時間がもったいないのでデフォルト状態で使用しています。Riderのバージョンによって微妙にデフォルト状態が違うので揃えた方が安全です。開発思想についてここはプロジェクト次第で、一概には言えないため、僕がよく採用する開発思想を紹介します。a.基本はUnityの機能を素直に使うようにするUnityエンジンの設計思想に反しないように機能を使うようにします。これはUnityがアップデートした時にトラブルが起き辛いようにする他、後から参画したメンバーが理解しやすくするためでもあります。※ピーキーにチューニングしたい場合は、置き換え可能な部分に切り出して実装するなど工夫をするのが良いと考えています。b.巨大なサードパーティフレームワークに依存しないようにする各メンバーの学習コストが増えて即戦力にならない問題や、採用フレームワークがアップデートされなくなるなどの運用後のリスクを回避するためです。新しい技術を使った方が便利だったり、一時的な開発効率が上がる場合もあるのですが、開発から運用後の事を想定して採用技術は選定していきたいところです。新しいものは、その分検証期間が必要ですし、見えていないトラブルもありえます。世の中に情報が溜まっていないので余計に時間がかかります。開発時間に余裕のあるプロジェクトであれば新しい技術の採用を検討する価値はあり、逆に短納期の場合は速度を優先し、枯れた安定した技術を採用するという事も重要だと思っています。2.Unityプロジェクトディレクトリ構成を決める何をどこに格納するのが良いかは最初に方針だけでも決めておいた方が良いです。僕が最近採用する構成は以下です。※Assetsフォルダ配下の構成です。Projectで唯一のResourcesフォルダ。・・・各開発者ごとにディレクトリを切ったテストコードResourcesフォルダは無闇に増やさないどのようなリソースがアプリに含まれるか分からなくなるため、無闇にResourcesフォルダは作らない事にしています。アセットストアからダウンロードしたものをまとめるアセットストアからダウンロードしたものは、基本的にAssets配下に散らばってしまいフォルダ構成を汚し、視認性を悪くしてしまうため、ExternalAssetsにまとめています。ソースコードとprefabを無理やり分けないよく見かける以下のような区分けはしません。視認性が悪くなるため、スクリプトとPrefabを別々に分けず、同じディレクトリに入れる事が多いです。機能単位でディレクトリを切ってそこにまとめるのが良いのではないかと思っています。本記事は細かい粒度で書いてしまっているので、当たり前やん!!みたいな事も書いていますが、.gitignoreを設定していなかったプロジェクトにぶち当たった事もあるので一応言及しておきます。最初から.gitignoreを作成しておきましょう。初期状態ではファイル自体存在しないので、とりあえず用意はしておきます。ゲーム開発でAndroidのマルチウィンドウに対応する事は少ないと思うので、マルチウィンドウをオフにする処理だけ追加しておきます。5.コーディング規約を決めるコーディング規約は、メンバー間で宗教戦争みたいなものに発展する場合もあり、地味に時間を取られがちです。コーディング規約決定に時間を割くのはナンセンスなので、Riderデフォルト設定のコーディング規約に準ずるという事で、今のプロジェクトは進めています。※ただしRiderのバージョンごとに微妙に設定内容が異なる可能性があるので注意です6.MonoBehaviourクラスのAwake、Startメソッドは極力使わないUnity特有のコード規約みたいな話です。Orderを無闇に変更したくない知らない所で処理が走られてしまうのが嫌だ(管理できない)以上の理由からMonoBehaviourが提供するAwakeやStartなどのイベント関数は、極力使わないようにしています。後から入ったメンバーも処理の流れを理解しやすいというメリットにも繋がります。人数が集まれば開発速度、単純に上がるという事はありません。以上の理由の他にもう一つ大きな要因として、大人数で開発できる設計になっているか?です。疎結合な実装になっているかViewとロジックが別れているかソースコードだと上記のような話になってきます。Unityの場合は、GameObjectの設計も重要です。アウトゲームであれば、合計で100画面あるUIが一つのPrefabになっていたりすると、20画面同時に修正箇所が発生しても同時に1人しか作業ができません。※Prefabは競合するとうまくマージされないため当たり前かもしれませんが、各画面を1Prefabずつ分けておくと良いです。その画面を構成するパーツ要素も細かくprefab化できていると大人数で開発をしやすくなります。できる限りシーンを触らなくても開発できるようにしておくのも重要です。任意の文字列をソースコードやPrefab(Textコンポーネント等)に直書きすると、以下の状況で困ります。文字列を一括で置き換える必要が出た時大変ローカライズする時にどこで使用しているか探すのが大変そんな時は、キーと値をセットにしたデータを用意し、そこから文字列を取得するようなヘルパークラスを用意する手が考えられます。hp,HP,HP上記のようなcsvを実行時にロードできるようにしておきます。はいこのような感じでキーを引数にして文字列を取得するように実装しておくと、文字列がソースコードに散らばらなくなって良いです。Localization.SetLanguage("EN");とすれば、機械的に英語に文字列が置き換わるようになるので、ローカライズ対応の手助けになるかもしれません。基盤開発系9.Unityのシーン遷移基盤開発ある程度大きなUnityプロジェクトになってくると、シーンを分ける事があります。分けた際に、そのシーン間を遷移させる仕組みを設計、実装しておくと良いです。LoadSceneMode.Single);上記のUnity標準機能を個々に書いていっても実現できますが、シーン間に演出、表現をはさみたい場合、このAPIだけでは不足しています。いう場合はSceneManagerのラッパークラスを作って対応します。どんな演出が考えられるか?前述した通り、演出が挟まるのであれば、その分の余白を残して実装する事が重要になります。それらを仕様が無いながらも、他ゲーム、今までの経験を参考に拡張できるようなシーン遷移管理クラスを作っておくと後々楽になります。シーンの話でしたが、今回は1つのシーン内での画面遷移についてです。画面遷移は必ず必要になります。デザイン次第で大きく変わりますが、とりあえず動くものを作っておきます。}画面をEnumで定義します。上の画像のように、Enum値と画面prefabを紐付けておきます。画面遷移開始DisplayManger.Instance.Goto(DisplayType.Home);上記のようなコードで画面が遷移するように実装しています。すごくざっくりな話になっていますが、表示中の画面Prefabを指定したPrefabに入れ替える実装をしています。11.どのシーンからでも開発できるような仕組みの設計9.Unityのシーン遷移基盤開発の内容に関連しますが、どのシーンからでも起動できるようにしておくと、開発の効率は上がります。それを実現するために、各シーンのエントリーポイントとなる共通クラスを継承したクラスをシーンのルートに配置しておくというルールを設けて実装しています。タイトルシーンアウトゲームシーンバトルシーンという3シーン構成であれば、それぞれにSceneEntryクラスを継承したのコンポーネント(TitleSceneEntry、OutGameSceneEntry、BattleSceneEntry)を各シーンのルートに配置します。以下各シーン毎の起動例を紹介します。タイトルシーンから起動した場合(通常起動)ユーザーが存在しなければ利用規約のダイアログを出す(ユーザーが存在すれば省略してログイン)利用規約を許可したらユーザーを作成マスターデータの取得アセットのダウンロードタイトルシーン以外から起動した場合(非通常起動)ユーザーが存在しなければ、利用規約など出さずにユーザー作成。存在すればログインする。マスターデータの取得アセットのダウンロードという処理フローになり、通常起動と違って色々省略されています。これらの処理の分岐をシーンエントリーポイントクラスが担っており、このクラスを使う事でどのシーンからでも起動できるようにしています。保守していきたいところです。12.サウンド再生基盤ゲーム開発する上ではサウンド再生は必ず必要になります。開発序盤ではどんな技術を採用するかは決まらない可能性が高いです。Unity標準サウンドCRIなど。そこで何を採用しても大丈夫なように再生部分を以下のようなインターフェースにしておきます。}開発初期はUnity標準サウンドで実装しておいて、仕様が決まってきたら必要な技術に内部実装を置き換えていく想定です。動画を再生させる可能性がある場合は、事前に再生検証をしておきたいです。12.サウンド再生基盤の動画版なので内容は割愛します(考え方は同じです)。一点、ゲーム内設定のBGMボリュームを動画再生時に反映するというのを忘れやすいので注意です。実機で再生できるかどうかの検証はもちろんですが、動画は比較的ファイル容量が大きくなるため、ファイルをサーバーに置いて、ダウンロード時間の確認(実機で)を早めにしておくとよいでしょう。14.ゲームエフェクトの再生と再利用機構バトル中に大量のゲームエフェクトが表示される事が想定される場合、エフェクトオブジェクトを再利用できる機構を事前に作っておくと良いです。大量のエフェクトを毎度生成するとGCが発生しやすいInstantiateがそもそも処理的に重いため、スパイクの原因になるユーザー体感をより良くするためにできる限りエフェクトを再利用できるように設計しておきます。バトル開始前のローディング中にあらかじめ必要なエフェクトをロードする機能を追加しておくと良いかもしれません。15.ローカルファイル保存の仕組みローカルに保存するのはよくあるので、事前に仕組みを用意しておく事ができます。内部の実装は置き換えられるように一旦ラッパークラスを定義します。使いそうなメソッドを用意しておきます。Sumzap16.外部アセットを想定したロード処理アプリ外部からアセットをダウンロード、ロードして使用する場合は、アセットバンドルや、今だとアドレッサブルアセットシステム(以下:AAS)を使用すると思います。開発序盤ではそのシステムが無い、または何を使用するかを決めていない場合もあると思います。決まっていないからといって、実装は後回しにすると手戻りが多く発生する可能性があるため、そうならないような実装を最初から心がけたいところです。}このような外部アセットをロードと解放を実装したインターフェースを定義しておきます。開発序盤はResources.Loadで実装しておき、後から実際のAssetBundleまたはAASに置き換えていくと良いかもしれません。後に出てくる32.アセットバンドル名を自動で設定されるようにするであえてアセットバンドルのパスをAssetBundles/Resources/のようにResouresをわざわざ挟んでいるのは、アセットバンドル名とResources.Loadのパスを合わせる事で、本実装時の手戻りを最小限にするためです。後に回すと面倒なのがAndroidバックキー対応です。バトル中やバトルのリザルトでは不要な場合がほとんどなので、Androidバックキーが必要になるのは主にアウトゲーム側です。API実行中などAndroidバックキーが動いては不都合な状態が存在するので、演出中、API実行中などAndroidバックキーが動いてはいけない状態をフラグとして取得できるようにして、trueだったらAndroidバックキーを押しても動かないようにしておきます。18.ローカルPush通知の実装ローカルPush通知もスマホゲーム開発なら必ず実装する機能なので、先に手を付けておきます。以下のような要件が想定されます。Managerからインストールできます。続きは後編へ今回は42のTipsのうち18までを紹介しました。半分ですが「うへぇ〜〜」と思ったあなた!これでも暇しなくて済みますね。Qiitaの12/24に公開致します。今回紹介した内容を実装し終えたとしても、それでFIXではありません。プロジェクトが進む中で見直し、修正して適正な姿に変化させていく必要があります。手を動かし形にしてみてください。これらのTipsが皆様の開発のお役に立てたなら幸いです。明日は@kazuhiro1128さんの記事です。10:40開発初期に暇を持て余しているUnityエンジニアができる42のTips前編Tweet読者になるボタンを押して貰えるととてもがんばれます。大庭俊介(bao_bao)渋谷のUnityエンジニア。スマホゲーム作って生活しています。設計、組み込み、演出一通りを自分1人で完結して作るのが得意です。よろしければTwitterフォローお願いします。(@ohbashunsuke)株式会社サムザップでUnityエンジニアとしてスマホゲーム作っています。(1)スターをつけました引用をストックしましたストック一覧を見る閉じる引用するにはまずログインしてくださいログイン閉じる引用をストックできませんでした。再度お試しください閉じる限定公開記事のため引用できません。