本記事は、サムザップ Advent Calendar 2019 #1 の12/24の記事です。
株式会社サムザップでUnityエンジニアをしている大庭です。
グラフィックデザイナー、Flashデベロッパーを経て、現在はUnityエンジニアでスマホゲームを作っています。Unity歴は6年くらいです。
今までコマンドバトル、ピンボール、麻雀などを作ってきました。
これから紹介していく内容は、開発初期後回しにされがちな、でも初期にやっておいた方が良いことばかりです。 僕はUnityエンジニアとしてプロジェクトを進める上で以下の事を重要視しています。
- 動くものをいち早く作ってメンバーに共有
- ワークフロー構築、開発手法を整える事が最優先
- いきなり正解は出ないので、不都合が出てきたら修正していくスタイル
- 修正見直しの数を繰り返して精度を上げていく
この思想も取り入れた形でリストアップしました。
本記事の導入
新規ゲームプロジェクトがスタートし、あなたはUnityエンジニアとして参画する事になったと想定します。しかし、ゲーム仕様はまだ真っ白な状態で、計画しているモック開発の要件も決まっていません。
「あ〜仕様も決まってないし、やる事がなくて暇だな〜」
って、
んな事ぁない!!
仕様が無くてもやる事はいくらでもあります。
今回のタイトルを見て「42Tips!?多すぎじゃない・・・!?」と思ったあなた!ぜひ読んでみてください。
今まで新規開発に携わる事の多かった僕が思いつく「仕様がなくてもUnityエンジニアが開発序盤にやれる事」をツラツラ紹介していきます。
本記事は開発初期に暇を持て余しているUnityエンジニアができる42のTips前編の後編です。
UI系
19.UI解像度を決める(UIを作り出す前に必ずやる)
uGUIを使用する場合は、CanvasScalerに設定するReference Resolution
の値を決めるという話です。
デザイナーの元データにも影響があるので、UIを作り出す前に決めておく必要があります。
解像度毎のパターンを作り、対象となる実機何点かで実際に確認して決めれば良いと思います。 その際、スマホと解像度比率の大きく違うタブレット端末での見え方も注意すると良いでしょう。
20.共通UIパーツを共通のPrefabにする
UI画面にはよく各画面同じようなパーツが出てきます。
- 戻るボタン
- ダイアログに表示するOKやキャンセルボタン
- ゲージ
- 各種アイコン
などなど。
これらをそれぞれの画面で新規で作成するのは無駄なので、デザインがまだ仮の状態の時から共通のPrefabとして使い、デザインが仕上がってきたら、該当するPrefabの中身を差し替えていくのが良いでしょう。ただし、開発が進む中で無理やり使い続けている感が出てくる場合は、ケースバイケースで作り直した方が良いです。
21.ダイアログなどのシーンをまたいで使用するものはDontDestroy領域へ
以下のようなシーン間、または各シーンで共通して使用したいもの、シーンをまたいで削除されたくないものはDontDestroyOnLoad(gameObject);
として、シーン遷移時にDestroyされないようにします。
- ダイアログUI
- タップ・スワイプエフェクト
- ローディング、ダウンロード表示
- Tips表示
- UIのブロッキング(画面タップを無効化するオブジェクト)
22.ダイアログシステム作成
ダイアログは大抵どのようなゲームでも必要になるので、先に取り掛かります。
暫定要件
- ダイアログ以外の部分をタップしても、その後ろのコンテンツは反応しない
- ダイアログ以外の部分をタップすると、ダイアログを閉じる事ができる
- 細かい事だけど、ダイアログを開く・閉じる時にはSEが鳴るようにする
- ダイアログは重なって表示できるようにする
- OKボタン、キャンセルボタン押した時のコールバック設定
- OK、キャンセルの表示ON/OFF
などなど。
この中でも「ダイアログは重なって表示できるようにする」は、デザイン次第ではやりたくないという意見も出る場合がありますが、その時に考えて一旦動くものを作って体感を確認しながら進めるのが僕は好きです。経験的にもその方がプロジェクトはより早く前に進みます。
// hogehogeとメッセージを表示させるダイアログを表示する DialogManager.Instance.Open(new AlertDialog(){ message = "hogehoge", okCallback = ()=> Debug.Log("OK"), cancelCallback = ()=> Debug.Log("cancel") });
上記のような感じでダイアログを呼べるように一旦実装しています。
23.UI用のCanvas共通化処理
UI用のCanvas、CanvasScalerの設定はプロジェクトで共通設定になる事が多いです。 それを毎度毎度設定するのは非効率のため、以下のような自動化できるコンポーネントを作っておきます。
using UnityEngine; using UnityEngine.UI; /// <summary> /// Canvas初期化 /// </summary> [ExecuteInEditMode] [DisallowMultipleComponent] public class UICanvasInitializer : MonoBehaviour { private CanvasScaler _scaler; void OnEnable() { Execute(); } #if UNITY_EDITOR void OnValidate() { Execute(); } #endif void Reset() { Execute(); } void Execute() { if (_scaler == null) { _scaler = GetComponent<CanvasScaler>(); } // 内容は適宜 _scaler.uiScaleMode = CanvasScaler.ScaleMode.ScaleWithScreenSize; _scaler.screenMatchMode = CanvasScaler.ScreenMatchMode.Expand; _scaler.referenceResolution = new Vector2(1920f, 1080f); } }
このような共通コンポーネントを作っておくと、修正が入った時に対応しやすいです。
24.UI階層構造のルールを決める
デザインによって大きく変わる可能性がありますが、大抵以下のような構成になるので一旦これで作ってしまいます。
各シーン
- 最背面レイヤー・・・背景
- コンテンツレイヤー・・・各シーンのコンテンツ
DontDestroyOnLoad領域
- 通常ダイアログレイヤー・・・通常ダイアログ
- オーバーレイレイヤー・・・ローディング、Tipsなど
- システムダイアログレイヤー・・・システム系ダイアログ
- 最前面レイヤー・・・・タッエフェクトなど
25.タップ・スワイプエフェクトを実装しておく
スマホゲームを作る場合、必ずと言っていいほどタップした時やスワイプした時のエフェクトが必要になります。 優先度的には後でもよい判断をされるかもしれませんが、先に実装しておく事をおすすめします。
タップ・スワイプエフェクトは最前面に表示されなくてはいけません。
後から実装すると、UIの階層関係を構築する際に見落としてしまって、手戻りをしてしまう可能性があります。
僕は表示階層に関わるものに関してはできるだけ優先的に実装しておく事にしています。
また、先に実装し、prefab化しておけば、クリエーターにブラッシュアップをお願いするのも楽です。
26.UI Environmentの設定
Prefab Editing Environmentsは、Unity2018.3からNestedPrefabが導入され追加された設定です。
必ず設定しておいたほうが良いのは UI Environment
です。
uGUI関連のPrefabをプレファブモードで編集する際、uGUIはCanvas配下に存在しないと表示が崩れます。
UI EnvironmentにCanvasやカメラを配置したシーンを設定しておくとプレファブモード時にそのシーンを使用してくれます。 NestedPrefabを使用する際は必須設定なので序盤に忘れないように設定しておきたいです。
ちなみに、UI Environmentに設定するシーン内のCanvasは23.UI用のCanvas共通化処理で紹介した設定を適用すると良いでしょう。
27.iPhoneXセーフエリア対応
セーフエリア対応はデザインの序盤に思想を決めて動けていないと、後々大きな手戻りが発生する可能性があります。
デザイナーとしっかり相談して、意志を統一していきたいところです。
また、iPhoneX系の実機で日々確認するのも大事です。
28.UIカラー設定コンポーネントを作っておく
デザインが進んでいくとキーとなるカラーが決まってきます。
uGUIのパーツを予め白色で作成しておき、プログラムで色を重ねて作成する場合があります。
その際、指定カラーをUnity上でひとつひとつセットしていくのはとても無駄な作業です。 少しでも楽をするための以下のようなコンポーネントを用意しておきます。
/// <summary> /// 決まったカラーをセットする /// </summary> public class GraphicColorSetter : MonoBehaviour { [SerializeField] private ColorType _colorType; [SerializeField] private Graphic _graphic; // trueにすると実行時に指定色を反映する [SerializeField] private bool _isRuntimeApply; void OnEnable() { if (isRuntimeApply) ApplyColor(); } public void SetColorType(ColorType type) { _colorType = type; ApplyColor(); } void ApplyColor() { // ColorResolverにColorTypeに対する色を定義して返却できるようにしておく _graphic.color = ColorResolver.Resolve(_colorType); } #if UNITY_EDITOR void OnValidate() { if (Application.isPlaying == false) { if (_graphic == null) { _graphic = GetComponent<Graphic>(); } else { ApplyColor(); } if (_graphic == null) { Debug.LogError("Graphic is not Found", this); } } } #endif }
このようにプルダウンでカラーを指定し反映する事ができるので効率的ですし、非エンジニアでも対応可能になります。 また開発序盤中盤はカラーの変更はザラにありますが、その反映もソースコードを変更するだけで対応できるのもメリットです。
作業効率化
29.よく使う拡張メソッドを用意しておく
- Transform
- RectTransform
- List
- String
- Dictionary
- Component
- GameObject
これらよく使うクラスのよく使う処理は、拡張メソッドとして用意しておくと開発効率が上がります。
using UnityEngine; namespace Hoge.Extensions { public static class GameObjectExtensions { /// <summary> /// 存在しなかった場合はAddComponentする /// </summary> public static T GetOrAddComponent<T>(this GameObject target) where T : Component { var comp = target.GetComponent<T>(); if (comp == null) { comp = target.AddComponent<T>(); } return comp; } } }
※GameObjectの拡張メソッド例
拡張メソッドの注意点
拡張メソッドを使用しているのかどうかがパッと見、分かりづらくなるためネームスペースにExtensions
をつけておく事をオススメします。
先の例でいうとnamespace Hoge.Extensions
の部分です。
30.よく使いそうなシェーダを用意しておく
乗算、加算(カラー加算、加算ブレンドの両方)、ブラーシェーダ等を用意しておくと良いと思います。
※ググれば出てくるので詳細は割愛します
31.テクスチャインポータの作成
ゲームを作っていると様々な画像アセットが必要になります。
ターゲットプラットフォームごとに指定する圧縮テクスチャの種類やクオリティが変わってきます。
これらを毎度設定するのはオペミスに繋がりますし、ミスをしてても気づきづらいため、AssetPostprocessorを使って所定のディレクトリに格納したら、自動でインポータが実行されるようにしておくと良いです。
32.アセットバンドル名を自動で設定されるようにする
アセットバンドルを利用する場合は、参照用のアセットバンドル名を必ず付ける必要があります。
以下は2.Unityプロジェクトディレクトリ構成を決めるで解説したUnityプロジェクトのディレクトリ構成です。
┌ AssetBundleModule/ ・・・サブモジュール ├ AssetBundles/・・・AssetBundleにビルドされるものを格納 Resources/ ├ chara ├ hoge ├ foo
AssetBundlesディレクトリ
配下に格納すると、AssetPostprocessorで自動でアセットバンドル名が付与されるようにしています。例えばhogeディレクトリにtest.prefab
を格納すると、 chara/hoge/test
というアセットバンドル名に自動で追加されます。
※16.外部アセットを想定したロード処理で説明していますがアセットバンドル名
とResources.Loadのパス
を共通化するためにResourcesフォルダ名を抜いたパス名にしています
33.最低限使用するであろうアセット導入
以下のアセットはどのようなプロダクトの開発でも必要な汎用性の高いアセットなので、初期から導入しています(執筆時点)。
- DOTween (無料版で十分)
- SRDebugger (有料)
- UniWebView3 (有料)
- MemoryProfiler ※UnityEditorのPackageManagerからインストール
- TexturePacker (有料)
- TexturePackerImporter (無料)
TexturePackerの採用理由
Unity純正のSpriteAtlasではなく、TexturePackerを採用している理由ですが、以下に挙げている通り、単純にTexturePackerの方がとても優秀だからという点で採用しています。
- アトラスに固める前の画像をUnity内部に格納する必要がない
- TexturePackerアプリ上でスライス設定できる(とても操作しやすい。UnityのSpriteEditorは見づら...ごにょごにょ...)
- 指定したテクスチャサイズから溢れた時に気づきやすい(自動でテクスチャ数が増えないため)
- フォルダ分けするとフォルダをSprite名にしてくれるので、命名ミスを減らせる
- 有料といえど安価
34.ランタイム中のデバッグ機能としてSRDebuggerの導入
33.最低限使用するであろうアセット導入でも紹介しましたが、SRDebuggerは入れておいた方が良いです。
SRDebuggerとはUnityEditor、実機上ともにランタイム中のデバッグをしやすくしてくれる外部アセットです。 AssetStoreから購入可能です。リンクはコチラ。
詳細は割愛しますが、これひとつでデバッグツールをまかなえるくらい汎用的な神ツールです。
ワークフロー系
35.ローカルで実機ビルドできる環境の整備
自分のPCで実機ビルドできる環境を作っておくのは、プロダクトのプロファイルをする上で大事です。 Xcodeを利用したプロファイルは強力なのでiOSの実機ビルドはできるようにしておくと良いです。
36.アプリ、アセットバンドルのビルド環境整備
アプリ、アセットバンドルのビルド環境は必ず必要になります。 ジェンキンスを使ったビルドジョブを作り、終了したらSlackに通知するようにしています。
アプリはビルドされたら、AppCenter(旧:HockeyApp)などのサービスから開発メンバーに配信できるようにすると良いです。
ビルドされたアプリはビルド番号をアプリ名に入れておくと便利です。
イメージこのような感じです。
37.サウンドアセット作成環境構築
Unity標準サウンド以外を使用する事が最初から決まっている場合は、サウンドアセット作成のワークフロー構築に早めに取り組みたいです。 今回はCRIサウンドを使用すると仮定します。 ※Unity標準サウンドを使用する場合は不要
CRIサウンドを利用する場合は、オーディオファイルをCRI専用ファイルに変換しなければなりません。
変換処理にAtomCraftを利用する場合、そのワークフローを事前に検証、環境構築しておくと良いです。 また、大人数でAtomCraftを使用する場合、どのファイルをgitignoreに入れなければならないかなど、時間に比較的余裕のある開発序盤に潰しておくと時間を効率に使えます。
38.動画アセット作成環境構築
37.サウンドアセット作成環境構築の動画版となりますが、動画はエンコードの仕方で大きくクオリティが変わってくるので、検証できる環境を用意しておくと良いと思います。
39.環境切り替えツールの導入
ゲームを開発する上で最低限以下の2種類は切り替えます。
- 開発環境
- リリース環境
その他にもステージング環境、サンドボックス課金環境などプロジェクトによっては様々な環境が必要になります。
環境を切り替えた時には以下の情報を切り替えています。
- 接続先URL
- アプリ名
- アプリID
- 使用シンボル
public string ApiBaseUrl => #if DEV "http://dev.sample.com/api/"; #elif RELEASE "http://sample.com/api/"; #elif LOCAL "http://localhost:8080/api/"; #else "http://dev.sample.com/api/"; #endif
※環境毎のAPI接続先切り替えの例です。
これらの情報を切り替えられるようなツールを作っておくと後々楽です。
40.クラッシュレポートの導入
クラッシュレポートは開発序盤から入れるようにしています。 開発序盤はバグも多いためクラッシュの理由をいち早く特定するための材料としてクラッシュレポートは助かります。
僕が今関わっているプロジェクトではSmartBeatを導入し、レポートの概要はSlackに通知するようにして常にウォッチしている状態です。
41.UIパーツのレギュレーションまとめページを作っておく
UIパーツの命名ルールはエンジニアリングを知っている人が決めるべきだと僕は思っています。いわゆるTA的な人です。 僕はTA的な動き方をする事もあるので、皆が見れる場所にUIパーツのレギュレーションページを作っておきます。 今のプロジェクトの場合はConfluenceを利用しています。
最初にひな形を作っておくとメンバーは迷わないので、事前にワークフローを構築しておく事は大事です。
- 画像サイズ
- ファイル命名ルール ※例 :
キャラID_icon.png
- 格納フォルダパス
- サンプル画像
以上の情報を1ページにまとめ、変更が入る度に更新しています。 特に開発序盤は更新頻度が多いので、ある程度落ち着いてからでも問題は無いと思います。
繰り返しになりますが、大事なのはワークフローを事前に作っておく事です。
その他
42.SerializeFieldをつけた時に発生するWarningの除去
最後はとても細かい内容ですが、SerializeFieldアトリビュートをつけるだけで、以下のようなWarningが出てきます。
is never assigned to, and will always have its default value null
Unityを使う身としてはSerializeFieldアトリビュートは必ず使うのでこのWarningは不要です。
初期値を入れれば解決しますが僕は一括で消し去りたいので、以下Assetsフォルダ直下にcsc.rsp
ファイルを作成します。
-nowarn:0649
すると、該当のWarningは消えます。
最後に
前編、後編と長々と書いてきましたがいかがだったでしょうか。
汎用性の高いものだけを紹介しましたが、関わるプロダクトの種類によってはまだまだやる事はあると思います。
仕様がなくてもUnityエンジニアは忙しいということですね(笑)。
今回紹介した大小含めた42のTipsが皆様の開発のお役に立てれば幸いです。
明日は@chrno001さんの記事です。