2016年11月25日

うにばな  Unityのため50のヒントとベストプラクティス(2016年版):意訳

”Unityのため50のヒントとベストプラクティス”の2016年改訂版の意訳をしてみました。
誤訳また記述ミス、その他があるかもしれませんが、それらはコメント欄のほうにお願いします。
 

 

■ Unityのため50のヒントとベストプラクティス(2016年版)

Gamasutra:ハーマンTullekenのブログ - Unityのため50のヒントとベストプラクティス(2016年版)


約4年前、私はオリジナルである 50 Tips for working with Unity公開しました

オリジナルバージョンとは多くの関連がありますが、その後に多数の変更があります:

  • Unityは良くなりました。たとえば、私は現在、FPSカウンタを信頼しています。 PropertyDrawersを使用できるため、カスタム・エディターを書く必要が少なくなります。 プレハブ作業を行う方法は、明示的にネストされたプレハブを必要とし ScriptableObjectは相性がいいです。
  • Visual Studioの統合性が向上しました。デバッグが非常に簡単になり、ゴリラデバッグが不要になりました。
  • サードパーティ製のツールとライブラリが良くなりました。Asset Storeには、視覚的なデバッグやより良いロギングなどの処理を行う多くのアセットが用意されています。。私たち独自の(無料)拡張機能は、プラグインは、元の記事に記載された元の記事に記載されているコードの多くが含まれています。(それの多くは、ここでも説明します)。
  • バージョン管理は良くなりました。(あるいは、今、私はそれを効果的に使用する方法を知っています)。例えば、複数のコピーやプレハブのバックアップコピーを持つ必要はありません。
  • 私はより多くの経験を得ました。私はこの4年間で、数多くのゲームのプロトタイプFather.IOのようなプロダクションゲーム および当社の主力Unityアセットグリッド。などの数多くのUnity・プロジェクトに取り組みました。

    この記事は、上記をすべて考慮したオリジナルの改訂版です。




Tipsのディスカッションを続ける前に、私たちはここに次の免責条項を発行します(初期バージョンと基本的に同じです):

これらのヒントは、すべてのUnityプロジェクトに適用されるものではありません。

  • これらは、3?20人からの小さなチームのプロジェクトでの私の経験に基づいています。
  • チームのサイズ、プロジェクトの規模、およびプロジェクトの目標により その代価が支払われるべきかどうかを決定します 。構造、再利用性、透明性、などの評価値があります 例えば あなたはゲームジャム中にすべてのこれらを使用することはありません、。
  • 多くのヒントは好みの問題です(ここに記載されているTipsに匹敵する技術があるかもしれません)。

以下の記事にもいくつかのUnityのベストプラクティス(パフォーマンスの観点からのものが、ほとんどですが)が存在します:


ワークフロー開発プロセス)

1.最初に決定した規模ですべてのプロトタイプを構築します。

そうしないと(たとえば、あなたが常に正しくアニメーションをスケーリングすることはできません)、その後のアセットをやり直す必要があるかもしれません。1Unity・ユニットを使用して3Dゲームの場合、= 1メートル、通常は最高です。照明や物理学を使用していない2Dゲームのために、(「デザイン」の解像度で)1Unity単位= 1ピクセルは通常は良いです。UI(および2Dゲーム)について、すべての資産がその解像度にスケーリングするために設計解像度(私たちはHDまたは2xHDを使用)とデザインを選択します。

2. すべてのシーンが実行可能にしてください。

これによりシーンを切り替えてゲームを実行する必要がなくなり、より速くテストできるようになります。すべてのシーンで必要なシーン読み込みの間にオブジェクトが残っている場合、これは難しいことです。これを実行する1つの方法は、彼らがシーンに存在しないときに自分自身をロードする永続オブジェクトのシングルトンを作る使用することです。シングルトンは別のTipsでより詳細に記載されています。

3. ソースコード管理と効果的にそれを使用する方法を学びます。

  • アセットをテキストとしてシリアル化します。

    シーンとプレハブをより合併させることは実際にはありませんが、変更されたものをより見やすくします。

  • シーンやプレハブ共有戦略を採用します。

    一般的には、複数の人がシングルトンは別のチップでより詳細に記載されています。同じシーンやプレハブでは動作しないようにしてください。小さな制作チームのために、誰もがシーンやプレハブを作ることができないことを保証するために作業を開始する直前に。所有権のシーンを代表する取引所の物理的なマークは、(デスクトップ上のシーンがある場合は、あなただけの1シーンで作業することができます)有用である可能性があります。

  • ブックマークなどのタグを使用してください。
  • 分岐戦略を決めて固執する。 シーンとプレハブをスムーズにマージすることはできないので、分岐はやや複雑です。 あなたがブランチを使うことに決めた場合は、あなたのシーンとプレパブの共有戦略にそって動作しなければなりません。
  • サブモジュールを使用する場合は注意してください。

    サブモジュールは、再利用可能なコードを維持するための素晴らしい方法ですが、いくつかの注意事項があります。

  • メタファイルは、一般的に複数のプロジェクトにわたって一貫していません。非Monobehaviourまたは非Scriptableオブジェクトコードでは一般的に問題ではありませんが、サブモジュールを使用するMonoBehavioursおよびScriptableオブジェクトでは、コードが失われる可能性があります。
  • 多くのプロジェクト(サブモジュールで1つ以上を含む)で作業している場合、すべてのプロジェクトでコードを安定化させるために、さまざまなプロジェクトをプル・マージ・コミット・プッシュする必要がある場合には、アップデート・アバランシェを得ることがあります これが起こっている間に他の誰かが変更を加えている場合、それは持続的な雪崩に変わる可能性があります)。この影響を最小限に抑える1つの方法は、専用のプロジェクトからサブモジュールを常に変更することです。このように、サブモジュールを使用するプロジェクトは、常にプルする必要があります。それらはプッシュバックする必要はありません。

4.テストシーンやコード別々に保管してください

リポジトリに一時的なアセットやスクリプトをコミットし 編集しているプロジェクトから削除します。

5.ツール(特にUnity)をアップグレードする場合は同時にインストールしてください。

Unityは、以前とは異なるバージョンのプロジェクトを開いたときにリンクを保持することに大変優れていますが、各メンバーが異なるバージョンで作業しているときにリンクが失われることがあります。

6. サードパーティ製のアセットはクリーンなプロジェクトにインポートし独自に使用するための新しいリソースパッケージをエクスポートしましょう。

プロジェクトに直接リソースをインポートすると、それらは時々問題を引き起こす可能性があります:

  • 競合(ファイルまたは名前)、特に、プラグインフォルダのルートにファイルがあるか、サンプルの標準アセットのアセットを使用するアセットが存在する可能性があります。
  • あなた自身のプロジェクト全体にファイルを置くときに組織化されていないかもしれません。。これは特に、使用しないで削除する場合に問題になります。

より安全にアセットをインポートするには、以下の手順に従ってください:

  1. 新しいプロジェクトを作成し アセットをインポートします。
  2. examples を実行し それらが動作することを確認してください。
  3. アセットは、より適切なディレクトリ構造に配置されています。 (私は通常、独自のディレクトリ構造のリソースの強制的なアライメントないんだけど、私は、任意のプロジェクトのないディレクトリ内のすべてのファイルは、既存のファイルは重要な位置に存在する上書きされる可能性がありますことを確認しています。)
  4. examplesを実行して動作確認をします。(リソースを移動したときに時々 アセットの損傷を引き起こす可能性がありますが、一般的に問題になることはありません)。
  5. 必要のないものを全て削除します(たとえばexamplesのような)。
  6. アセットがコンパイルされているか そしてプレハブが全てリンクされているか確認します。なにか残っているかを実行してテストします
  7. すべてのアセットを選択して、パッケージをエクスポートします。
  8. プロジェクトにインポートします。

7. ビルドプロセスの自動化。

これは小規模なプロジェクトでも役に立ちますが、以下の場合に特に便利です:

  • 多くの異なったゲームのバージョンでビルドが必要な場合。
  • チーム内の技術知識の差異があるメンバーでビルドの必要がある場合。
  • プロジェクトのビルド前に微調整をする必要がある場合。

Unity Builds Scripting: Basic and advanced possibilities 詳しくはリンク先を参照してください。


8. ドキュメントセットアップ。

ほとんどのドキュメントはコード内になければなりませんが、特定のことは、コードの外にドキュメント化する必要があります。設計者がセットアップ用のコードを取捨選択することは時間の浪費です。(ドキュメントが最新のものである場合)ドキュメント化されたセットアップの効率を改善しました。

以下のようなことをドキュメント化してください:

  • 使用しているタグ。
  • レイヤー用途(コリジョン、カリングおよびレイキャスト 本質的には、各レイヤに対応する使用)
  • レイヤーのためのGUIの深さ(各レイヤーに対応する表示)
  • シーンのセットアップ設定。
  • 複雑なプレハブのプレハブ構造。
  • 共通言語設定。
  • ビルドのセットアップ設定。

一般的なコーディング

9.すべてのコードを名前空間(ネームスペース)内に格納する。

これにより、独自のライブラリとサードパーティのコード間のコードの衝突を回避できます。しかし重要なクラスとの衝突を避けるために、名前空間に依存していません 別の名前空間を使用するとしてもクラス名として、"Object"、 "Action" 、 "Event" は使用しないでください。

10. アサーションの使用

アサーションは、コード内の不変量をテストし論理バグをフラッシュするのに役立ちます。アサーションで利用可能なのはUnity.Assertions.Assertのクラスです。これらですべての条件をテストし 条件が満たされない場合、コンソールにエラーメッセージを書き込みます。アサーションをどのように活用できるかを熟知していない場合はリンク先を参照アサーションを使用したプログラミングの利点を(別名文をアサート)

11. 表示されるテキスト以外の文字列は使用しないでください。

特に、オブジェクトやプレハブを識別するために文字列を使用しないでください。例外があります(Unityで名前でしかアクセスできないものがいくつかあります)。このような場合には、そのようなAnimationNamesやAudioModuleNamesなどのファイルの定数としてこれらの文字列を定義します。これらのクラスは管理不能になった場合はあなたのような何かを言うことができるように、入れ子になったクラスを使用しますAnimationNames.Player.Run。

12. Invoke とSendMessageを使用しないでください。

これらのMonoBehaviourのメソッドは名前で他のメソッドを呼び出します。コード内で名前で呼び出す方法は、追跡するのが困難です(あなたは用途を見つけることが出来ません。そしてSendMessageのスコープは範囲が広いため追跡することはより困難です)

コルーチンとC#アクションを使用して独自のInvokeを簡単にロールアウト(運用)することができます:

public static Coroutine Invoke(this MonoBehaviour monoBehaviour, Action action, float time) 
{ 
return monoBehaviour.StartCoroutine(InvokeImpl(action, time)); 
} 

private static IEnumerator InvokeImpl(Action action, float time) 
{ 
yield return new WaitForSeconds(time); 

action(); 
}

次に、あなたのmonoBehaviourでこのようにこれを使用することができます:

this.Invoke(ShootEnemy); //ShootEnemy は引数を取らないvoidメソッドです.

独自のベースMonoBehaviourを実装する場合は、独自のInvokeを追加することができます。

別の安全なSendMessageを実装するのはより困難です。そのかわりに、私はたいてい 親オブジェクト、カレントのゲームオブジェクトや子オブジェクトの現在のコンポーネントを取得し 各種のGetComponent 変数を使用して直接呼び出しを実行します。

(編集:誰かがUnityのEvent Systemの一部であるExecuteEventを代わりに提案しました、これまではまだ分かりませんでしたが、それ以上調べる価値はあります)

13. ゲームが実行中に階層を混乱させるオブジェクトの生成をしないでください。

シーンオブジェクトに親を設定すると、ゲームが実行されているときに簡単に見つけられるようになります。 空のゲームオブジェクト、またはシングルトン(この記事の後半参照)を使用して、コードから簡単にアクセスできるようにすることもできます。 このオブジェクトをDynamicObjectsと呼びます。

14. 正規な値としてnullを使用することについては具体的に記述し 可能な限り避けてください。

nullは不正なコードを検出するのに役立ちます。 しかしnullを暗黙的に渡す習慣があれば、間違ったコードがうまく実行されてしまいずっと後までそのバグに気付かないでしょう。 さらに各層がnull変数の上を通過するときにコードの深部に現れることがあります。 私は合法的な値としてnullを使うのをやめようとします。

私の好みのイディオムは、nullチェックを行わずコードが問題のある場所で失敗するようにすることです。 より深いレベルへのインタフェースとして機能するメソッドでは、nullの変数をチェックし失敗する可能性のある他のメソッドに渡す代わりに例外をスローします。

場合によっては、値が正当にnullになる可能性があり、別の方法で処理する必要があります。このような場合、何がいつ そしてなぜnullになるのかを説明するコメントを追加してください。

一般的にインスペクタで設定された値によく使用されます。ユーザーは値を指定できますが、値が指定されていない場合はデフォルト値が使用されます。これを行うより良い方法は、Tの値をラップする、クラスOptional です(これはNullable のようなものです)。特別なプロパティレンダラーを使用してチェックボックスをレンダリングし チェックボックスがオンの場合にのみ値ボックスを表示することができます。

(残念なことに、ジェネリッククラスを直接使用することはできません.Tの特定の値のクラスを拡張する必要があります。)

[Serializable]
public class Optional
{
public bool useCustomValue;
public T value;
}
[Serializable]
public class Optional
{
public bool useCustomValue;
public T value;
}

コードでは、このように記述できます:

health = healthMax.useCustomValue ? healthMax.Value : DefaultHealthMax;

編集:多くの人から構造体を使用する方が良いと指摘されました(ガベージを生成せず、nullにすることはできません)。 ただし 非ジェネリッククラスのベースクラスとしては使用できないため、インスペクタで実際に使用できるフィールドには使用できません。


15. コルーチンの効果的な使用方法を学びましょう

コルーチンは、多くの問題を解決する強力な方法です。 しかしデバッグが難しく誰も理解できない、あなた自信でさえ理解することが出来ません。

知っておくべきこと:

  • コルーチンを並行して実行する方法。。
  • コルーチンを順番に実行する方法。
  • 既存のものから新しくコルーチンを作成する方法。
  • CustomYieldInstructionを使用するcustom coroutines(カスタムコルーチン)を作成する方法。


//これ自身がコルーチンです 
IEnumerator RunInSequence() 
{ 
yield return StartCoroutine(Coroutine1()); 
yield return StartCoroutine(Coroutine2()); 
} 

public void RunInParallel() 
{ 
StartCoroutine(Coroutine1()); 
StartCoroutine(Coroutine1()); 
} 

Coroutine WaitASecond() 
{ 
return new WaitForSeconds(1); 
}


16. インターフェイスを共有するコンポーネントを操作するには、拡張メソッドを使用します。

編集:どうやらGetComponentなどはインターフェイス用に機能し、このチップを冗長にしています)。 特定のインタフェースを実装するコンポーネントを取得したり、そのようなコンポーネントを持つオブジェクトを見つけると便利なことがあります.

次の例では、代わりにこれらの関数のtypeofのジェネリック版を使用しています。ジェネリック版はインターフェイスで動作しますが、typeofをしないでください。以下の方法は、一般的な方法できちんとこれをラップします。

public static TInterface GetInterfaceComponent(this Component thisComponent) 
where TInterface : class 
{ 
return thisComponent.GetComponent(typeof(TInterface)) as TInterface; 
}

 

17. 拡張メソッドの構文をより便利にする。

例えば:

public static class TransformExtensions  
{ 
public static void SetX(this Transform transform, float x) 
{ 
Vector3 newPosition =  
new Vector3(x, transform.position.y, transform.position.z); 

transform.position = newPosition; 
} 
... 
}

 

18. 守備型のGetComponent代替を使用する

場合によって他のクラスのGetComponentを呼び出すときに、RequiredComponentを介してコンポーネントの依存関係を強制することが常に可能であるとは限りません。 そして、たとえあなたがRequiredComponentを使用しているとしても、それがどこにあるかを期待するコンポーネントをどこで取得し そうでない場合にはセットアップエラーをコード内に示すことは有用です。 これを行うには、エラーメッセージを出力する拡張メソッドを使用するか、見つからないときに、以下のようなより有用な例外をスローします:

 

public static T GetRequiredComponent(this GameObject obj) where T : MonoBehaviour 
{ 
T component = obj.GetComponent(); 

if(component == null) 
{ 
Debug.LogError("Expected to find component of type " 
+ typeof(T) + " but found none", obj); 
} 

return component; 
}

 

19. 同じことをするために異なる慣用イディオムを使用しないでください。

多くの場合、物事を行うために複数の慣習的な方法があります。 そのような場合は、プロジェクト全体で使用するものを1つ選択します。

理由は次のように:

1)いくつかのイディオムはうまく機能しません。 1つのイディオムを使用すると別のイディオムには適さない一方向のデザインになります。

2)同じイディオムを使用すると、チームメンバーは何が起こっているのかを簡単に理解することができます。 構造とコードを理解しやすくし 間違いにくくなります。

イディオム基の例:

  • ステートマシンvsコルーチン。
  • ネストされたプレハブvsリンクプレハブvsgodプレハブ。
  • データ分離戦略。
  • 2Dのゲームで状態ステートのスプライトを使用する方法。
  • プレハブ構造。
  • スポーン戦略。
  • タイプによって参照(「リンク」)配列vsレイヤーvsタグvsネームvs:オブジェクトを検索する方法。
  • オブジェクトをグループ化する方法:レイヤー参照vs配列(「リンク」)vsタグvsネームvsタイプによって。
  • 他のコンポーネントのメソッドを呼び出すための方法。
  • 自己登録vsオブジェクトのグループの検索。
  • 実行順序の制御(Unityのexecution order設定vsyield logic vsAwake / Start and Update / Late Update 更新の信頼vs手動による方法と任意順序アーキテクチャの使用)
  • ゲーム中のマウスでオブジェクト/ポジション/ターゲットを選択する:選択マネージャとローカル自己管理。
  • シーンの変化との間でデータを保持:を通じて  PlayerPrefs新しいシーンがロードされるときに破壊されない、またはオブジェクト。
  • (アディングおよびレイヤリング、ブレンディング)アニメーションの組み合わせの方法。
  • 入力処理(ローカルvs集中管理)

20. 簡単に一時停止するために、あなた自身の時間のクラスを維持してくださいTime.DeltaTimeとTime.TimeSinceLevelLoadをラップすると、一時停止と時間スケールが考慮されます。 それを使用するには調整が必要ですが異なるクロック(インターフェース・アニメーションやゲーム・アニメーションなど)を実行する場合は、作業が非常に簡単になります。

編集:UnityはunscaledTimeとunscaledDeltaTimeをサポートしています。これにより、多くの状況で独自のTimeクラスが冗長化されます。 グローバル時間をスケーリングすると、あなたが望ましくない方法で書き込まなかったコンポーネントに影響を与える場合には、これは依然として有効です。

21. 更新が必要なカスタムクラスは、グローバル静的時間にアクセスすべきではありません。 代わりに、Updateメソッドのパラメータとしてデルタ時間を取る必要があります。これにより、上で説明したように一時停止システムを実装するときや、カスタムクラスの動作を高速化または低速化したいときに、これらのクラスを使用できるようになります。

 

22. WWWを呼び出す一般的な構造を用います。

サーバー通信する多くのゲームでは、一般的に数十のWWWコールを含みます。あなたはUnityの標準のWWWクラスまたはプラグインを使用して定型文として上に薄い層を書くことで恩恵を得ることができます。

私は通常、すなわちCallImplのコルーチンとMakeHandler(GETとPOSTのために、それぞれ)メソッドの呼び出しを定義します。基本的に、スーパーハンドラを構築するために、プロセッサのパーサ、成功と失敗からMakeHandlerメソッドを使用してメソッドを呼び出します。また、それは、スーパーハンドラを呼び出し 完了するまで待って、URLを作成し CallImplのコルーチンを呼び出します。

これは おおよそ次のとおりです。:

public void Call(string call, Func parser, Action onSuccess, Action onFailure)
{
var handler = MakeHandler(parser, onSuccess, onFailure);
StartCoroutine(CallImpl(call, handler));
}
public IEnumerator CallImpl(string call, Func handler)
{
var www = new WWW(call);
yield return www; handler(www);
}
public Func MakeHandler(Func parser, Action onSuccess, Action onFailure)
{
if (NoError(www))
{
var parsedResult = parser(www.text);
onSuccess(parsedResult);
}
else { onFailure("error text");
}
}

 

これには、いくつかの利点があります。

  • 定型コードをたくさん書く必要がなくなります。
  • 特定の処理(UIコンポーネントのロードの表示や特定の一般的なエラーの処理など)を中枢部分で処理できます。

 

23. あなたが大量のテキストを持っている場合は、同じファイル内に配置します。

インスペクタで編集のためにフィールドに入力しないでください。 Unityのエディタを開くことなく変更することが容易になり、特にシーンを保存する必要はありません。

 

24. ローカライズする予定がある場合は、すべての文字列を1つの場所に分けてください。

これを行うには多くの方法があります。 1つの方法は、文字列ごとにパブリックの文字列フィールドを持つTextクラスを定義することです(デフォルトは英語に設定されています)。 他の言語ではこれをサブクラス化して、対応する言語でフィールドを再初期化します。

より洗練されたテクニック(テキストの本文が大きく、言語の数が多い場合に適しています)は、スプレッドシートで読み込まれ、選択された言語に基づいて正しい文字列を選択するロジックを提供します。

 


 

クラス設計

25.inspectable フィールドの実装方法を決定し それを標準にします

フィールドをパブリックにするか、プライベートにして[SerializeField]としてマークします。 後者は「より正確」ですが、あまり便利ではありません(もちろん、Unity自身が普及している方法ではありません)。 どの方法を選んでも、チームの開発者が公開フィールドをどのように解釈するかを知ることができるように標準にしてください。

  • inspectable なフィールドは公開されている場合。このシナリオでは、publicとは、「実行時に変数が設計者によって変更されるのは安全です。コード内に値を設定しないでください」という意味です。
  • inspectable なフィールドはプライベートでマークされたシリアライズ可能なフィールドの場合。 このシナリオでは、publicは「コード内でこの変数を変更することは安全です」(したがって、あまり多くは見られず、MonoBehavioursおよびScriptableObjectsにはパブリックフィールドは存在しないはずです)。

26.コンポーネントの場合、インスペクタで調整しないでください。  

それ以外の場合は、デザイナーが微調整します。 いくつかのまれなケースでは避けられないものがあります(たとえば、エディタスクリプトによってはそれを保持する必要がある場合など)。 その場合、HideInInspector属性を使用してインスペクタで非表示にすることができます。 

27.PropertyDrawersを使用して、フィールドをよりユーザーフレンドリーにする

PropertyDrawersは、インスペクター内のコントロールをカスタマイズするために使用できます。これにより、データの性質に適したコントロールを作成し 特定のセーフガードを適所に置くことができます(変数の範囲の制限など)。 ヘッダー属性を使用してフィールドを整理し ツールチップ属性を使用してデザイナーに余分なドキュメントを提供します。

28. カスタムエディタに比べてPropertyDrawersの使用が好ましい。

PropertyDrawersは、フィールド・タイプごとに実装されるため、実装する作業がはるかに少なくなります。 それらはまた より再利用可能です - 一度型のために実装され、それらはどのクラスでもその型のために使用することができます。 カスタムエディタはMonoBehaviourごとに実装されているため、再利用性と作業性が低下します。

29. 既定ではMonoBehavioursはシールされています。

一般に、UnityのMonoBehavioursはあまり継承されていません:

  • UnityがStartやUpdateのようなメッセージメソッドを呼び出す方法は、サブクラスでこれらのメソッドを扱うのが難しいです。気をつけなければ、間違った箇所が呼び出されるか、基本メソッドを呼び出すことを忘れてしまいます。
  • カスタムエディタを使用する場合は、通常、エディタの継承階層を複製する必要があります。あなたのクラスを拡張したい人は、自分のエディタを用意するか、またはあなたが提供したもので何でもしなければなりません

継承が必要な場合は、それを避けることができれば、Unityメッセージメソッドを提供しないでください。 そうした場合、それらを仮想化しないでください。 必要に応じて、メッセージメソッドメソッドから呼び出される空の仮想関数を定義して、子クラスがオーバーライドして追加の作業を実行することができます。

public class MyBaseClass
{
public sealed void Update()
{
CustomUpdate();
... //このクラスの更新 
   } 
 //このクラスは独自のアップデートを行う前に呼び出されます
//独自の更新コードにフックするオーバーライドします。
   virtual public void CustomUpdate(){};
} 

public class Child : MyBaseClass
{
override public void CustomUpdate()
{
//カスタム操作を実行
   }
}

これは、誤ったコードの上書きからクラスを防ぎ、それでもそれをUnityのメッセージにフックすることができます。私がこのパターンが好きではない一つの理由は、物事の順序が問題になることです。上の例では、クラスが独自の更新を行った直後に子プロセスが直接操作を行いたい場合があります。

30.インターフェイスをゲームロジックから分離する。

インタフェースコンポーネントは、一般にそれらが使用されているゲームについて何も知らないはずです。ビジュアル化するために必要なデータを提供し イベントに登録して、ユーザーがそれらとやり取りするときを見つけます。インターフェイスコンポーネントはゲームロジックをおこなう必要がありません 。それが有効であることを確認するために入力をフィルタリングすることができますが、メインルールの処理は他の場所で行われるべきです。多くのパズルゲームでは、作品はインターフェイスの拡張であり、ルールを含むべきではありません。(例えば、チェスの駒はそれ自体のルール上の動きを計算すべきではないです)

同様に、入力はその入力に作用するロジックから分離する必要があります。 あなたのアクターに動く意図を知らせる入力コントローラーを使用してください。 アクターは実際に移動するかどうかを処理します。

ここでは、ユーザーが選択肢のリストから武器を選択できるようにする、UIコンポーネントの抜粋された例を示します。これらのクラスがゲームについて知っている唯一のものは、武器クラスです(武器は、このコンテナが表示する必要があるデータの有用なソースであるためです)ゲームはコンテナについても何も知りません。それが関係しているすべてはOnWeaponSelectイベントに登録するだけです。

 

public WeaponSelector : MonoBehaviour
{
public event Action OnWeaponSelect {add; remove; }
   // GameManagerは、このイベントのために登録することができます

public void OnInit(List  weapons)
{
foreach(var weapon in weapons)
{ 

var button = ... //Instantiates a child button and add it to the hierarchy           

buttonOnInit(weapon, () => OnSelect(weapon));
//子ボタンは、オプションが表示され、 
//このコンポーネントへのクリックバックを送信
      }
}
public void OnSelect(Weapon weapon)
{
if(OnWepaonSelect != null) OnWeponSelect(weapon);
}
} 

public class WeaponButton : MonoBehaviour
{
private Action<> onClick; 

public void OnInit(Weapon weapon, Action onClick)
{
... //武器からスプライトとテキストを設定


this.onClick = onClick;
} 

public void OnClick() //リンクUI Buttonコンポーネントのクリック時などでこの方法
    {
Assert.IsTrue(onClick != null);  //は起こるべきではありません

onClick();
}
}


31. 個別のコンフィグレーション、状態ステート、ブックキーピングを分離する。

  • 設定変数(コンフィギュレーション)は、プロパティを使ってオブジェクトを定義するためにインスペクタで調整される変数です。 たとえば、maxHealthです。
  • 状態変数(ステート)は、オブジェクトの現在の状態を完全に決定する変数であり、ゲームが保存をサポートしている場合は保存する必要がある変数です。 たとえば、currentHealthです。
  • 簿記変数(ブックキーピング)は、速度、利便性、過渡的な状態に使用されます。 それらは常に状態変数から完全に決定することができます。 たとえば、previousHealthです。

これらのタイプの変数を分離することで、変更可能なもの、保存する必要のあるもの、ネットワークを介して送信/取得する必要があるもの、そしてこれをある程度強化することが容易になります。

 

ここでは、この設定の簡単な例を示します:

public class Player
{
   [Serializable]
   public class PlayerConfigurationData
   {
      public float maxHealth;
   }

   [Serializable]
   public class PlayerStateData
   {
      public float health;
   }

   public PlayerConfigurationData configuration;
   private PlayerState stateData;

   //book keeping
   private float previousHealth;

   public float Health
   {
      public get { return stateData.health; }
      private set { stateData.health = value; }
   }
}

 

32. パブリックインデックス結合配列の使用を避けてください

例えば、武器の配列、弾丸の配列、およびパーティクルの配列を定義しないでください。コードは次のようになります:

public void SelectWeapon(int index) 
{  
currentWeaponIndex = index; 
Player.SwitchWeapon(weapons[currentWeapon]); 
} 

public void Shoot() 
{ 
Fire(bullets[currentWeapon]); 
FireParticles(particles[currentWeapon]); 
}

これはコード内ではあまり問題になりませんが、きちんとインスペクタで設定してください。 いっそのこと3つの変数をカプセル化するクラスを定義し その配列を作成してください

[Serializable] 
public class Weapon 
{ 
public GameObject prefab; 
public ParticleSystem particles; 
public Bullet bullet; 
}

コードはきれいに見えますが、最も重要なことはインスペクタでデータを設定するのは間違いということです。

 

33. 配列以外の構造体には配列を使用しないでください。

例えば、プレイヤーは3種類の攻撃を受ける可能性があります。 それぞれは現在の武器を使用しますが、異なる弾と異なる動作を生成します。

3つの弾丸を配列にダンプして、この種のロジックを使用したくなるかもしれません:

public void FireAttack() 
{ 
/// behaviour 
Fire(bullets[0]); 
} 

public void IceAttack() 
{ 
/// behaviour 
Fire(bullets[1]); 
} 

public void WindAttack() 
{ 
/// behaviour 
Fire(bullets[2]); 
} 
Enums 列挙型はコード内で見栄えを良くすることができます... 
public void WindAttack() 
{ 
/// behaviour 
Fire(bullets[WeaponType.Wind]); 
}

別々の変数を使用して、どの名前を入れるべきかを示す名前を付ける方が良いでしょう。クラスを使ってきれいにします

[Serializable] 
public class Bullets 
{ 
public Bullet fireBullet; 
public Bullet iceBullet; 
public Bullet windBullet; 
}

これは、他に火災、氷、風のデータがないことを前提としています。

 

34. インスペクタで細かくするためのシリアライズ可能なクラスのデータをグループ化する。

いくつかのエンティティには数十の調整機能があるので インスペクタで正しい変数を見つけることは悪夢になることがあります。

作業を簡単にするには、以下の手順に従ってください:

  • 変数のグループに対して別々のクラスを定義します。 それらをパブリックかつシリアライズ可能にします。
  • 主要クラスでは、上記のように定義された各型のパブリック変数を定義します。
  • AwakeまたはStartでこれらの変数を初期化しないでください。 シリアライズ可能なので、Unityはそれを処理します。
  • 定義に値を割り当てることで、以前と同じようにデフォルトを指定することができます。

これにより、インスペクタ内の折りたたみ可能な単位で変数がグループ化され、管理が容易になります。


[Serializable] 
public class MovementProperties //Not a MonoBehaviour! 
{ 
public float movementSpeed; 
public float turnSpeed = 1; //default provided 
} 

public class HealthProperties //Not a MonoBehaviour! 
{ 
public float maxHealth; 
public float regenerationRate; 
} 

public class Player : MonoBehaviour 
{ 
public MovementProperties movementProeprties; 
public HealthPorperties healthProeprties; 
}


                    

 

35. 公開フィールドに使用されていない場合でも、MonoBehaviours Serializableではないクラスをシリアライズ可能にする

これにより、Inspectorがデバッグモードのときにインスペクタのクラスフィールドを表示することができます。 これはネストされたクラス(プライベートまたはパブリック)に対してもです。

36. インスペクタの変更をコードで修正することは避けてください。

インスペクタで調整可能な変数は構成変数であり、ランタイム定数として扱われ、状態変数の2倍ではありません。 このプラクティスに従うことで、コンポーネントの状態を初期状態にリセットするメソッドを簡単に記述でき、変数の動作をより明確にすることができます。

public class Actor : MonoBehaviour 
{ 
public float initialHealth = 100; 

private float currentHealth; 

public void Start() 
{ 
ResetState(); 
}    

private void Respawn() 
{ 
ResetState(); 
}  

private void ResetState() 
{ 
currentHealth = initialHealth; 
} 
}

   

 

パターン

 

パターンは、頻繁に発生する問題を標準的な方法で解決する方法です。 Bob Nystromの「Game Programming Patterns(無料のオンラインで読むことができます)http://gameprogrammingpatterns.com/」は、ゲームプログラミングで発生する問題にどのようにパターンが当てはまるかを見るのに役立つリソースです。 Unity自身は、これらのパターンを多く使用しています。Instantiateはプロトタイプパターンの例です。 MonoBehavioursはテンプレートパターンのバージョンに従い、UIとアニメーションはオブザーバーパターンを使用し 新しいアニメーションエンジンはステートマシンを使用しています。

これらのヒントは、具体的にはUnityとパターンの使用に関連します。

37. 利便性のためにシングルトン(Singleton)パターンを使用してください。

次のクラスは、それを継承するクラスを自動的にシングルトンにします:

public class Singleton : MonoBehaviour where T : MonoBehaviour 
{ 
protected static T instance; 

//Returns the instance of this singleton. 
public static T Instance 
{ 
get 
{ 
if(instance == null) 
{ 
instance = (T) FindObjectOfType(typeof(T)); 

if (instance == null) 
{ 
Debug.LogError("An instance of " + typeof(T) +  
" is needed in the scene, but there is none."); 
} 
} 

return instance; 
} 
} 
}

シングルトンは ParticleManager 、AudioManager または GUIManager。などの管理に有用です。 

(多くのプログラマーは、XManagerという名前のクラスに対して警告を出します。なぜならクラスの名前が不十分であるか、無関係なタスクが多すぎるように設計されているからです。 概ね私はこれに同意します。 しかし すべてのゲームには少数のマネージャーが存在しますし すべてのゲームで同じことをするので、実際にはこれらのクラスは慣用表現です。)

  • (プレーヤーなど)マネージャーではない固有のプレハブのインスタンスのためにシングルトンを使用しないでください。この原則を遵守しないと、継承階層が複雑になり、特定の種類の変更が困難になります。GameManager(または他の適切なGodクラス)でこれらの参照を維持してください。
  • クラス外から頻繁に使用されるパブリック変数やメソッドの静的プロパティとメソッドを定義します。これにより、GameManager.Instance.playerの代わりにGameManager.Playerを記述することができます。

他のヒントで説明したように、シングルトンは、グローバルデータを追跡するシーンロード間で持続するデフォルトのスポーンポイントとオブジェクトを作成する場合にも有用です。

 

38. ステートマシンを使用して、異なるステートで異なる動作を取得したり、ステート遷移でコードを実行したりします

軽量のステートマシンは、複数の状態を有し各状態は、指定または状態の実行操作の有無を入力して、アクションを更新することができます。これは、コードをよりクリーンにしてエラーが起こりにくくします。Updateメソッドに、それが何をするかを変更するifステートメントやswitchステートメント、またはhasShownGameOverMessageなどの変数の有無を記述することで状態マシンからの符号を得ることが出来ます。


public void Update() 
{ 
if(health <= 0) 
{ 
if(!hasShownGameOverMessage)  
{ 
ShowGameOverMessage(); 
hasShownGameOverMessage = true; //Respawning resets this to false 
} 
} 
else 
{ 
HandleInput(); 
}    
}

より多くの状態ステートがある場合 このタイプのコードは非常に乱雑になる可能性があります。 ステートマシンはそれを非常にクリーンにすることができます。

39. UnityEvent型のフィールドを使用して、インスペクタでObserver パターンを設定する

※Observer パターン(オブザーバ・パターン)とは、プログラム内のオブジェクトの状態を観察(observe)するようなプログラムで使われるデザインパターンです。

UnityEventのクラスでは、ボタン上のイベントと同じUIインターフェイスを使用して、インスペクタの4つのパラメータを取る方法にリンクすることができます。これは入力に対処するために特に有用です。

 

40.フィールド値の変化を検出するためにオブザーバーパターンを使用してください。

変数が変更された場合にのみコードを実行する問題は、ゲームで頻繁に発生します。 私たちは汎用クラスでこれを包括的に解決しました。これにより、値が変わるたびにイベントに登録することができます。 ここでは、healthの例での構築の方法です:

/*値の監視*/health = new ObservedValue(100);
health.OnValueChanged += () => { if(health.Value <= 0) Die(); };

これを確認する場所を確認することなく、どこでも変更できます。たとえば次のように。

if(hit) health.Value -= 10;

健康値healthが0を下回ると、Dieメソッドが呼び出されます。 さらなる議論と実装については、この記事ポストを参照してください。

41.プレハブにアクターパターンを使用してください。(これは、「標準」パターンではありません。基本的な考え方は、Kieran Lordのこのプレゼンテーションを参考にしてください)

プレハブの主なコンポーネントはアクターです。 通常はプレハブの「同一性」を提供するコンポーネントであり、上位の1つのコードは多くの場合相互作用します。 アクターは、他のコンポーネント(ヘルパー)を同じオブジェクト(および場合によっては子供)上で使用して作業を行います。

メニューからボタンオブジェクトを1つにまとめると、SpriteおよびButtonコンポーネント(およびTextコンポーネントを持つ子)を持つゲームオブジェクトが作成されます。 この場合、Buttonはアクターコンポーネントです。 同様に、メインカメラは、通常、付属のカメラコンポーネントに加えて、いくつかのコンポーネント(GUIレイヤ、フレアレイヤ、オーディオリスナ)を備えています。 カメラはアクターです。

アクタは、他のコンポーネントが正しく動作するように要求することがあります。 アクタコンポーネントで次の属性を使用すると、プレハブをより強固で便利にすることができます。

  • あなたのアクターが同じゲームオブジェクトに必要とするすべてのコンポーネントを示すには、RequiredComponentを使用します。 (あなたのアクターは、返された値がnullかどうかをチェックする必要はなく、常にGetComponentを安全に呼び出すことができます)。

  • DisallowMultipleComponentを使用して、同じコンポーネントの複数インスタンスをアタッチすることを防ぎます。 したがって、アクターは、複数のコンポーネントが接続されているときのビヘイビアーを心配することなく、常にGetComponentを呼び出すことができます。

  • アクタオブジェクトに子がある場合はSelectionBaseを使用します。 これにより、シーンビューでの選択が容易になります。

 

[RequiredComponent(typeof(HelperComponent))]

[DisallowMultipleComponent]

[SelectionBase]

public class Actor : MonoBehaviour {

...//

}

 

42. ジェネレータを用いたランダムパターンのデータストリーム。(これは標準モードではありませんが、それが非常に有用であることが判明しました)

ジェネレータは乱数ジェネレータと似ています。特定のタイプの新しいアイテムを取得するために呼び出すことができるNextメソッドを持つオブジェクトです。 ジェネレータは、さまざまなパターンやさまざまな種類の乱数を生成するために、その構築中に操作することができそれらは便利です。なぜなら新しい項目を生成するロジックをアイテムの必要な場所とは別にして、コードがはるかにクリーンになるようにするからです。

ここではいくつかの例を示します:

var generator = Generator
   .RamdomUniformInt(500)
   .Select(x => 2*x); //Generates random even numbers between 0 and 998

var generator = Generator
   .RandomUniformInt(1000)
   .Where(n => n % 2 == 0); //Same as above

var generator = Generator
    .Iterate(0, 0, (m, n) => m + n); //Fibonacci numbers

var generator = Generator
   .RandomUniformInt(2)
   .Select(n => 2*n - 1)
   .Aggregate((m, n) => m + n); //Random walk using steps of 1 or -1 one randomly

var generator = Generator
   .Iterate(0, Generator.RandomUniformInt(4), (m, n) => m + n - 1)
   .Where(n >= 0); //A random sequence that increases on average

私たちは、障害物の生成、背景色の変更、プロシージャルな音楽の作成、単語ゲームで単語を作るときに可能性の高い文字列の生成などに使用しています。ジェネレータはまた、コンストラクトを使って一定でない間隔で繰り返しコルーチンを制御するためにうまく機能します:

 

while (true) 
{ 
//Do stuff 

yield return new WaitForSeconds(timeIntervalGenerator.Next()); 
}

ジェネレータの議論についての詳細は、この投稿を参照してください

 

プレハブとScriptableObject参照

43.すべてにプレハブを使用してください。

プレハブ(またはプレハブの一部)ではないシーン内の唯一のゲームオブジェクトは、フォルダーでなければなりません。 一度だけ使用されるユニークなオブジェクトであっても、プレハブでなければなりません。 これにより、シーンを変更する必要のない変更を簡単に行うことができます。

44.プレハブをプレハブにリンクする。 インスタンスをインスタンスにリンクしない

プレハブへのリンクは、プレハブをシーンにドロップするときに維持されます。 インスタンスへのリンクはありません。 可能な限りプレハブにリンクするとシーン設定が減少し シーンを変更する必要性が減ります。

可能な限り、インスタンス間のリンクを自動的に確立します。 インスタンスをリンクする必要がある場合は、プログラムでリンクを確立します。 例えばプレイヤープレハブはゲームマネージャーが起動時にそれをGameManagerに登録することができます。または、GameManagerは、プレイヤープレハブインスタンスが開始時にそれを見つけることができます。

45.あなたが他のスクリプトを追加したい場合。プレハブのルートにメッシュを入れないでください 

メッシュからプレハブを作るときは、最初にメッシュを空のゲームオブジェクトの親とし それをルートにします。 メッシュノードではなく、ルート上にスクリプトを置く。 そうすれば、インスペクタで設定した値を失うことなく、メッシュを別のメッシュに置き換えるほうがずっと容易です。

46.プレハブの代わりに、共有コンフィギュレーションデータにScriptableObjectを使用します。

あなたがこれを行う場合:

  • シーンはより小さくなります
  • 誤って1つのシーン(プレハブインスタンス上)に変更を加えることはできません。

47. レベルのデータにScriptableObjectを使用する。

レベルデータは、多くの場合、XMLまたはJSONに保存されているが、代わりにScriptableObjectを使用することで、いくつか利点があります。

  • ScriptableObjectはエディタで編集することができます。簡単にデータの検証が可能になることで技術面に暗いデザイナーにも親しみやすくなります。また編集をさらに容易にするためにカスタムエディタを使用することができます。
  • データの読み出し/書き込みと解析を心配する必要はありません。
  • 結果のアセットを分割してネストして管理する方が容易であり 大規模な構成ではなく、ビルディングブロックからレベルを作成する方が簡単になります。

48.ScriptableObjectを使用して、インスペクタで動作を設定する。ScriptableObjectは通常、データの設定に関連付けられますが、データとして「メソッド」を使用することもできます

敵のタイプがあり、各敵にスーパーパワーの束があるシナリオを考えてみましょう。 あなたはこれらの通常のクラスを作ってEnemyクラスのリストを持つことができますが、カスタムエディタを使わなければインスペクタ内に別のSuperPowerのリスト(それぞれが独自のプロパティを持つ)を設定することはできません。 しかし これらのSuperPowerアセットを(ScriptableObjectとして実装する)ことができればよいのです。

このように設定:

public class Enemy : MonoBehaviour 
{ 
public SuperPower superPowers; 

public UseRandomPower() 
{ 
superPowers.RandomItem().UsePower(this); 
} 
} 

public class BasePower : ScriptableObject 
{ 
virtual void UsePower(Enemy self) 
{ 
} 
} 

[CreateAssetMenu("BlowFire", "Blow Fire") 
public class BlowFire : SuperPower 
{ 
public strength; 
override public void UsePower(Enemy self) 
{ 
///program blowing fire here 
} 
}

このパターンに従うとき、次の点に注意してください:

  • ScriptableObjectを確実に抽象化することはできません。 代わりに具象基底クラスを使用し 抽象型であるメソッドにNotImplementedExceptionsをスローします。 抽象属性を定義して、抽象クラスである必要があるクラスとメソッドをマークすることもできます。。
  • ジェネリックであるScriptableObjectはシリアライズできません。 ただし ジェネリックベースクラスを使用して、すべてのジェネリックを指定するサブクラスをシリアライズするだけで済みます。

49.ScriptableObjectを使用してプレハブを特殊化します。

2つのオブジェクトの構成が一部のプロパティでのみ異なる場合は、2つのインスタンスをシーンに配置し インスタンスのプロパティを調整するのが一般的です。 通常は、2つのタイプの間で異なるプロパティの別のクラスを別々のScriptableObjectクラスにする方が優れています。

これにより、柔軟性が向上します:

  • 特殊化クラスの継承を使用して、異なるタイプのオブジェクトにさらに固有のプロパティを与えることができます。
  • シーンの設定ははるかに安全です(単に目的のタイプのオブジェクトを作るためにすべてのプロパティを調整する代わりに、ScriptableObjectを選択するだけです)
  • コードの実行時にこれらのオブジェクトを操作する方が簡単です。
  • あなたは2タイプの複数のインスタンスがある場合は、変更を行うときはそのプロパティは常に一貫性があることを知っています。
  • コンフィギュレーション変数のセットを混在および一致させることができるセットに分割できます。

以下はセットアップの簡単な例です:

[CreateAssetMenu("HealthProperties.asset", "Health Properties")]
public class HealthProperties : ScriptableObject
{
public float maxHealth;
public float resotrationRate;
} 

public class Actor : MonoBehaviour
{
public HealthProperties healthProperties;
}

特殊化の数が多い場合は、特殊化を通常のクラスとして定義し それを保持できる適切な場所(GameManagerなど)にリンクされたScriptableObjectでこれらのリストを使用することができます。 それを安全に、より速く便利にするために必要な融合方法がもう少しあります。

最小限の例を以下に示します:

public enum ActorType 
{ 
Vampire, Wherewolf 
} 

[Serializable] 
public class HealthProperties 
{ 
public ActorType type; 
public float maxHealth; 
public float resotrationRate; 
} 

[CreateAssetMenu("ActorSpecialization.asset", "Actor Specialization")] 
public class ActorSpecialization : ScriptableObject 
{ 
public List healthProperties; 

public this[ActorType] 
{ 
get { return healthProperties.First(p => p.type == type); } //Unsafe version! 
} 
} 

public class GameManager : Singleton   
{ 
public ActorSpecialization actorSpecialization; 

... 
} 

public class Actor : MonoBehaviour 
{ 
public ActorType type; 
public float health; 

//Example usage 
public Regenerate() 
{ 
health  
+= GameManager.Instance.actorSpecialization[type].resotrationRate; 
} 
}

 

50. CreateAssetMenuを使用することで属性アトリビュートは自動的にScriptableObjectが作成したメニューが作成/アセットに追加されます。


 

デバッギング

51. 効果的なUnityのデバッグ機能を使用する方法を学びます。

  • Debug.Logステートメントにコンテキストオブジェクトを追加して、それらの生成場所を確認します。
  • Debug.Breakを使用してエディタでゲームを一時停止します(たとえば、エラー状態が発生し そのフレームのコンポーネントプロパティを調べたい場合など)。
  • ビジュアルデバッグのためにDebug.DrawRay関数とDebug.DrawLine関数を使用します(たとえば、レイキャストがヒットしなかった理由をデバッグする場合、DrawRayは非常に便利です)。
  • ビジュアルデバッグには、Gizmosを使用します。 また、DrawGizmo属性を使用してモノ動作以外のギズモレンダリングを提供することもできます。
  • デバッグインスペクタビューを使用して(インスペクタを使用してUnityで実行時にプライベートフィールドの値を確認してください)。

52. 効果的にIDEのデバッガを使用する方法を学びます。

Visual StudioでのUnityゲームのデバッグ。を参照してください。

53. 時間の経過とともに値をグラフに描画するビジュアルデバッガを使用する

これは物理学、アニメーション、その他の動的プロセス、特に散発的な障害をデバッグするのに非常に役立ちます。 グラフのゆらぎを見ることができ、同時にどの他の変数が変化するかを見ることができます。 目視検査では、あまりにも頻繁に変化する値や明白な原因なしにドリフトするような特定の種類の奇妙な行動も明確になります。 私たちはモニターコンポーネントを使用しますが、それにはいくつかの方法があります。

54. 改良されたコンソールログを使用します。

カテゴリに応じた出力の色分けを可能にするエディタ拡張を使用し それらのカテゴリに従って出力をフィルタリングすることができます。私たちは Editor Console Proを使用していますが、いくつかが利用可能です

55. Unityのテストツールを使用して、特にアルゴリズムと数学的コードをテストする。

チュートリアルを参照するか またはUnit testing at the speed of light with Unity Test Tools.の投稿、またUnity Test Tools を参照してください。

56. Unityのテストツールを使用して、「スクラッチパッド」のテストを実行します。

Unityのテストツールは、正式なテストには適していません。 シーンを実行することなくエディタで実行できる便利なスクラッチパッドテストにも利用できます。

57.スクリーンショットを撮影するためのショートカットを実装します。

多くのバグは視覚的であり、スナップショットを撮ることができるときに報告する方がずっと簡単です。 理想的なシステムでは、連続するスクリーンショットが上書きされないように、PlayerPrefsにカウンタを保持する必要があります。 スクリーンショットは、プロジェクトフォルダ外に保存して、誤ってリポジトリにコミットすることを避ける必要があります。

58. 重要な変数のスナップショットを印刷するためのショートカットを実装します。 

これにより、ゲーム中に何か予期せぬ事態が発生したときに、情報を簡単に記録することができます。 もちろん、どの変数がゲームに依存しているか。 あなたはゲームで発生する典型的なバグに導かれます。 例は、プレイヤーと敵の位置、またはAIアクター(「それを追いかけようとしているパス」などの「思考状態」)です。

59.簡単にテストさせるためのデバッグオプションを実装します。 

いくつかの例:

  • すべての項目のロックを解除します。
  • 敵を無効にします。
  • GUIを無効にします。
  • プレイヤーを無敵にします。
  • すべてのゲームプレイを無効にします。

誤ってデバッグオプションをコミットしないように注意してください。デバッグオプションを変更することでチーム内の他の開発者を混乱させる可能性があります。

60. デバッグのショートカットキーの定数を定義し 一か所に保存します。

デバッグ・キーは、ゲーム入力の残りの部分と同じように単一の場所で通常(または都合よく)処理されません。 ショートカットキーの衝突を回避するには、中枢の場所に定数を定義します。 代わりに、デバッグ機能であるかどうかにかかわらず、すべてのキーを1か所で処理することもできます。 (欠点は、このクラスがこれのためだけにオブジェクトへの余分な参照を必要とするかもしれないということです)。

61.プロシージャルなメッシュ生成を行う際に頂点に小さな球を描画するか生成します。

メッシュを表示するために三角形やUVを使い始める前に、頂点が正しい位置にあることを確認し メッシュが正しいサイズであることを確認するのに役立ちます。

 


 

パフォーマンス

62. パフォーマンス上の理由におけるデザインと設計に関する一般的なアドバイスには細心の注意を払ってください。

  • このようなアドバイスはしばしば作り話に基づいており、テストによって裏打ちされていません。
  • 時にはアドバイスは、テストに裏打ちされているが、テスト自体が破綻しています。
  • 時にはアドバイスは正しいテストに裏打ちされているが、それらは非現実的または異なる状況にあります。(例えば、それを使用して配列はジェネリックリストよりも高速であるかを示すのは簡単です。しかし 実際のゲームの文脈でこの差はほとんど常に無視できる程度です。同様に、テストはあなたのターゲットデバイスとは異なるハードウェアに適用されていた場合、その結果はあなたにとっては意味がありません。)
  • 時にはアドバイスは懸命ですが、古くなっている事があります。
  • 時には、アドバイスが適用されますが、トレードオフが存在します。スローゲームその船は時々ない高速のものよりも優れています。そして、頻繁に最適化されたゲームはシッピング(発送)を遅らせる厄介ななコードが含まれている可能性が高くなります。

パフォーマンスのアドバイスは、以下で概説するプロセスを使用して、実際の問題の原因を迅速に追跡するのに役立つことを覚えておくと便利です。

63. 初期の段階からターゲットデバイス上で定期的にテストをします。

デバイスは、非常に異なるパフォーマンス特性を持っていますが それを驚かないでください。早期から問題について知っていることでより効果的に対処することができます。

64.パフォーマンスの問題の原因を追跡するために効果的にプロファイラを使用する方法を学びます。

65. 必要に応じてより正確なプロファイリングを行うには、カスタムプロファイラを使用します。

Unityのプロファイラでは、何が起きているのかを明確に把握することはできません。プロファイルフレームが不足したり、プロファイリングが深すぎるとテストが意味をなさないほどゲームが遅くなることがあります。 私たちはこれに独自のカスタムプロファイラーを使用していますが、アセットストアで代替案を見つけることができるはずです。

66. パフォーマンス向上の影響を測定する

パフォーマンスを向上させるために変更を加えるときは、その変更が実際に改善されていることを確認するために測定します。 変更が測定可能または過失でない場合は、元に戻します。

67. より良いパフォーマンスのために読みにくいコードを書かないでください。

次のいずれかの状況がない限り:

  • プロファイラを使用して問題を抱えているソースを特定し変更後のパフォーマンスを測定しパフォーマンスの向上が保守性の損失に比べて十分に高い場合。

OR

  • あなたが自分は何をしているか解っている場合。

 


 

基本ネーミングおよびディレクトリ構造

68. ネーミングは、慣例やフォルダ構造に従います

一貫した命名およびフォルダ構造は、対象を見つけやすく、対象が何であるかを把握しやすくなります。

おそらく、独自の命名規則とフォルダ構造を作成したいと思うでしょう。 ここでは一例を示します。

命名の一般原則

  1. それが何であるかを示すような呼び方をします。鳥は鳥と呼ばれるべきです。
  2. 発音したり記憶したりできる名前を選択します。 あなたがマヤのゲームを製作したとして レベルをQuetzalcoatisReturn(ケツアルコアトルの復活)と名付けてはいけません。
  3. 一貫して。あなたが名前を選択すると、それに固執。別の内の1つの場所とbuttonContainerで何かbuttonHolderを呼び出さないでください。
  4. ComplicatedVerySpecificObject:このように、パスカルケースを使用してください。スペース、アンダースコア、またはハイフンを使用しないでください (例外的に同じものの異なる性質を命名を参照してください)。
  5. 進捗状況(WIP、 final)を示すために、バージョン番号、または単語を使用しないでください。
  6. 略語は使用しないでください:DVamp@ WはDarkVampire@Walkでなければなりません。
  7. 設計ドキュメント内の用語を使用してください:ドキュメントがdieのアニメーションを呼び出した場合、DarkVampire@DeathではなくDarkVampire@Dieとします。
  8. 最も具体的な記述子は、VampireDarkではなくDarkVampireのままにしておきます。 PauseButtonではなく、ButtonPausedです。 たとえば、すべてのボタンが「Button」という単語で始まらない場合、インスペクターで一時停止ボタンを見つける方が簡単です。 [多くの人がそれを好む傾向があります。なぜならグループ分けを視覚的により明白にするからです。 名前はグループ化するためのものではなく、フォルダもあります。 名前は同じタイプのオブジェクトを区別して、信頼性が高く高速に配置することができます。
  9. いくつかの名前は、配列を形成します。PathNode0、PathNode1、例えば、これらの名前の数字を使用してください。常に0ではなく1から開始します。
  10. シーケンスを形成しないもののために数字を使用しないでください。例えば、Bird0、Bird1、Bird2はフラミンゴ、イーグル、ツバメにする必要があります。
  11. 二重下線__Player_Backupとプレフィックス一時オブジェクト。

 

同じものを異なる性質(特徴)から命名

基本の名前をアンダーバーで区切って「性質(特徴)」を記述します。

例えば:

  • GUIボタンステート  EnterButton_Active、EnterButton_Inactive
  • テクスチャ  DarkVampire_Diffuse、DarkVampire_Normalmap
  • スカイ  JungleSky_Top、JungleSky_North
  • LODグループ  DarkVampire_LOD0、DarkVampire_LOD1

例えばRock_Small、Rock_LargeはSmallRock、LargeRockにする必要があり、アイテムの異なるタイプを区別するだけにこの規則を使用しないでください。

 


 

構造

シーン、プロジェクトフォルダ、およびスクリプトフォルダの構成は同様の形式に従ってください。ここでは、取り掛かりにいくつかの要約例を示します。

 

フォルダー構成


MyGame
   Helper
      Design 
      Scratchpad
   Materials
   Meshes
      Actors
         DarkVampire
         LightVampire
         ...
      Structures
         Buildings
         ...
      Props
         Plants
         ...
      ...
   Resources
      Actors
      Items
      ...
   Prefabs
      Actors
      Items
      ...
   Scenes
      Menus
      Levels
   Scripts
   Tests
   Textures
      UI
      Effects
      ...
   UI 
MyLibray
   ...
Plugins
SomeOtherAsset1
SomeOtherAsset2
...
                   

 

シーンの構造

Main 
Main
Debug
Managers 
Cameras
Lights
UI
   Canvas
      HUD
      PauseMenu
      ...
World
   Ground
   Props
   Structures
   ...
Gameplay
   Actors
   Items
   ...
Dynamic Objects

 

スクリプトのフォルダ構造

Debug 
Debug
Gameplay
   Actors
   Items
   ...
Framework
Graphics
UI
...



akinow at 16:46|PermalinkComments(0)TrackBack(0) Clip to Evernote Unity3d | シリーズ講座

2016年10月31日

ザーザード- ザーザード-(じゅもんがちがいます)


ご無沙汰してます 


 カイザード・アルザード・キ・スク・ハンセ・グロス・シルク
 灰塵と化せ冥界の賢者 七つの鍵を持て 開け地獄の門
 ハーロ・イーン 
 

ハロウィンの定番ネタですね あのコミックも自分が生きている間に完結することがないのかな。
ストーリーも忘れちゃったし。

 
ログインパスワードを忘れてしばらく締め出しをくらっていました いつものことですが
あんまり更新期間があいたので記事の書き方もだいぶ忘れてしまった気もします。

少し前に、すっごい長文記事を書いてブログを更新したら 「夢だった」ことがあって起きてから
言いようのない脱力感でぐったりしたことがありましたが 今回の更新は夢じゃないよね


最近はIT関係の作業が主なのですが まあしんどい

IT系の作業はなんというか 哲学テーマの「アキレスと亀」みたい

「アキレスが一歩進むと 亀も少し進むのでアキレスは亀を追い越せない」 というアレ 

「一歩完成に近づくと 仕様もちょっと変わるので永久に作業が終わらない」
 コレが現実。

でも 攻撃を回避する能力を身につけると以外に時間もとれる良い職種なのかもしれないです。
自分にはまだわからないですが。



そういえば おやすみ中コメントの書き込みがいくつかありました

「最近では開発に数十億かけるものまであって」 という文章に 

「そんなにかかるかバカ」(北海道から)

というコメントをいただきました。

北海道は行ったこと無いからわかりません 人件費はジャガイモで払ったりするかもしれませんし(´・ω・`)

と 冗談はさておき 以前の記事で書いたことがありましたが 開発会社の規模にもよりますけど

年収+(光熱費。管理費。開発機材のコスト。その他 = ほぼ年収)
+宣伝費用 +外部委託(声優さん、シナリオ、作曲、デザイン、プログラム)ざっくりこのぐらいで
どこまでを開発費として含めるかは会社により違います ゲームエンジンの開発費を計上する場合もあるので
そういうところは非常にお高くなると思います。
いまどきはリソース作成が最も重たい作業なのでデザインだけでも100人、200人を動かしたらどの程度のコストに
なるか想像してみてください。

ちなみに うちのサイトのIPブロックは
「記事と関係のない煽りコメント」や「某掲示板の該当スレからジャンプしてきた」
場合に発動するような気がします。今回のはセーフ

関係ないのにブロックされてしまったよという人は VPNやらプロキシで迂回すれば多分大丈夫
とここに書いても ブロックされてたら見れないのか


ということで いろいろ準備もあるので しばらく休んでからまた更新します。

それでは

akinow at 09:31|PermalinkComments(0)TrackBack(0) Clip to Evernote 日記 

2015年12月31日

年末のご挨拶です

 



 




「 私の部屋に薄い本などあるが 今は話したくない 」



 



 






じょしらく 時そば




今年も 年またぎは 『じょしらく ドラマcd』 を聞きながら過ごしてまいりましょう
大晦日らしく 演目は 『ときそば』をチョイスしてみます。 ドラマCD版のマリーさん CV:阿澄佳奈さんです

じょしらく動画 前回いつだっけ?
さいきん正月に『和尚が2(ツー)』って聞かされなくなった


2年前だったよ (´・ω・`) ナツカシネー


今年気づいたことは ブログ更新をサボると書き方を忘れる
ので 2016年は定期的に記事を書いていこうと思った所存 カタジケナイ



dat151230064148.jpg


今年も29から31日までの3日間 東京ビックサイトにおいてコミックマーケット89が開催されました


例の紐 フミナさま 夜のヤッターマン 一瞬で過ぎ去るブーム


おれたちは同人絵かき・・・
紙キレ同然 己の薄い本
萌えつきるのに
わずか数秒・・・

的な



生活かかってる人もいるから 確実に売れるネタをチョイスしないと大変なことになっていしまいます

応募締め切りが半年前だから 半年先のブームを読み切るすごい嗅覚だよね

ちなみにどうしてもネタがなくて困ったときは”ドラクエ”をネタにするんだとか聞きました

ドラクエのファンは年齢層が高くて懐に余裕があるのでドラクエネタであれば

手にとってくれるし複数買いしてくれるので安定株なんだそうです。

すごく売上げがあるわけではないけど次のコミケまで生活できるぐらいの儲けが期待できる鉄板ネタだとか




そういえば同人誌は発売されると即日翻訳されてアップロードされてしまうので

いちはやく翻訳版を読むためにロシア語や中国語をマスターする人がいるとか 前にどこかで見たよ

今は知らないけど

努力の方向がよくわからない




コミケの華は 同人誌 &たぶん物販とか ( てきとーだなおい)

検索してみると抱き枕は相変わらず需要が高いみたい

ヲタクイズビューティフル

「 3次は枕営業があるから2次最高! 」

っていうけど 2次嫁は作品が売れても売れなくても安定の枕営業です


no title

そういえば抱き枕の記事前に書いてたね
終わらない すごい 絵コンテ






ネット界隈で話題の 声優研究同人誌とか





no titleno titleno titleno titleno title

 


 


 


 


 


 


 



 



【画像】冬コミで発売されたこの同人誌頭おかしすぎるだろwwwww



声優ブームはいいけど声優さんもたいへんだよね 電話の種類特定されたり シャンプーの銘柄を調べあげられたり

写真のExifを削除しても 部屋の写真に写り込んだものやら窓の景色から住所特定とか

そりゃ声優さんも塩対応したくなるわ

塩対応はしょっぱい対応(つれない対応)という意味だそうだけど


塩対応と聞いて

ファン「私の不注意です、塩がなくなりますが手に入りませんか?」
声優さん「塩?塩か?」

みたいなオタク的戯れかと思ったのは秘密よ

いまどきノ子がファーストガンダムもないか



no title
あと前回C88 のこれはちょっと欲しくなったかな ちなみにパラメータふってあるけど これデュエルできるの?




同人ゲーム c89

同人ソフトを忘れていました がんばってるなー
好きなモノが作れる幸せ(重い言葉)






no title

ちなみに今年のコスプレ一等賞を選ぶならコレです
困ったような表情といいシチュエーションといい最高





ということで

今年はほとんど更新できなかったけど

2016年はなんとか更新頻度を上げていきたいので よろしくおねがいします





そういえば 掃除してたら5年前に描いた漫画が出てきた エロだけどw
古川さとしさん という成年小説?官能小説? を書かれているお友達のHPにプレゼントしたもので 同人誌ではない

詳しくはぐぐる





失望しました ぶぎょうさんのファン辞めます




no title


no title



おまけ1 今年話題になったものを無理やり動画で振り返る





■ スターウォーズ 新作エピソード公開




STAR WARS 子門真人

子門真人カバーの スターウォーズのテーマ。 日本サイドが勝手に歌詞をつけてレコードを製作したため ルーカス激おこで速攻回収されてしまいました 絶版のため貴重な音源みたいですよ。 ルーカスフィルムもディズニーの軍門に下ったことですし
ディズニーの偉い人新作エピソードでつかってみませんか?
宇宙からのメッセージ。。。 銀河大戦。。。 を見てルーカスが腹を抱えて笑ってたそんな時代



スタジオジブリ散開




風の谷のナウシカ

おなじみ安田成美の風の谷のナウシカ ちなみに当時映画館でこの曲は流れていません。
松本隆&細野晴臣タッグで楽曲としてはよろしかったのですが、当時 宮駿監督は「ヴォルガの舟唄」
のような荘厳な曲をイメージしていて
映画公開の直前にこの曲を聞き あわてて全国の映画館に曲差止めの通達が出されました。
・・ですが一部地方の映画館は差し替えが間に合わず 映画を台無しにされてしまったようです



no title
そういえば年末になって急に ナウシカ姐さん漫画がとりあげられてたんだが なにか起きているんだろうか? 田中圭一先生が 一周回って注目を集めだしているのか? 『おそまつさん』のエンド絵あたりがきっかけなんかな わからん





笑っていいとも終了




ネバーエンディングストーリー

ネバーエンディングストーリーBD化を記念して(いつのはなしだよ) 羽賀研二カバーの ネバーエンディングストーリーのテーマです。 いいとも青年隊からタレントや役者もこなし 『ストリートファイターII MOVIE』のケン役で声優としても高性能でしたが、サギ容疑で逮捕されてしまったのは記憶にあたらしいところ ウィキペディアの代表作『サギ師一平シリーズ』は狙い過ぎな気もします だが GJ。

「♪照れた男のジグザグをわかってくれ~」突っ込みどころ満載の歌詞ですが、 昭和の歌謡曲では「ジグザグ気取った~」「照れてジンジ~ン」「ギザギザハートの子守唄~」のように繰り返す濁点のはいったワードをつかうとヒットするというジンクスがあったのです 竹本孝之の『てれてzinzin』はヒットなの?。 ジブリ作品がヒット祈念でタイトルに「の」をつけるジンクスみたいなものです たぶん。。 『となりの山田くん』なんてなかった




おまけ2 2015年個人的ヒット商品




アリエール 洗濯洗剤 液体 パワージェルボール(アマゾンだと¥360くらい)
no title
洗濯物に一度ついた匂い(納豆みたいなくさったかおり)は絶対に落とせないはずだったのに それを取り去る快挙をアリエールがやってくれました 強力な殺菌効果で部屋干しなどで染み付いた匂いをカットする業界初の ありえなーい商品 こんなんでいいかな?
これは売上1位になるわけだわ
魔法みたい






北越 たまねぎおかき 12枚×12袋 (アマゾンだと2800円ぐらい)
no title
玉ねぎの香ばしい香りと さくさくの食感で ’開ければ最後ユーキャントストップ’ そんな禁断のお菓子
(このキャッチはプリングルスのCM)
Box12袋が数日で消費されます。 危険ですから食べないでください アマゾンの在庫がなくなると管理人が死にます。










akinow at 22:09|PermalinkComments(2)TrackBack(0) Clip to Evernote 日記