2

私は最近、一度に複数のゲーム オブジェクトのインスタンス化を高速化するために、Unity でのオブジェクト プーリングを試しています。

ただし、これらはかなり複雑なオブジェクトであるため、プールに戻ったときにリセットする必要があります。

簡単にリセットできるようにデフォルト値を保存するには、ScriptableObject を使用するのが良い方法かもしれないと読みました。しかし、そのためには、実行時に新しい ScriptableObject をロードして、オブジェクトの実際の値を格納する必要があります。

したがって、疑似コードでは、とのクラスがpublic MyScriptableData dataありますpublic MyScriptableData defaults

1) デフォルトの ScriptableObject でプールされた新しいオブジェクトを作成しますdata = defaults;

2) オブジェクトの存続期間中にスクリプト可能なオブジェクトの値を変更することを行う

3) 非アクティブ化してから、プールされたオブジェクトをプールに戻し、scriptableObject をデフォルトにリセットします (data = defaults;再度)。

主な質問が 3 つあります。

A) 実際にこれを実装する方法がわかりません。ステップ2で、デフォルト値が変更されるようです。したがって、デフォルトにリセットしても何も起こりません。を使用して、スクリプト可能なオブジェクトの新しいインスタンスを作成することを考えました

data = ScriptableObject.CreateInstance<MyScriptableData>();

defaultsしかし、デフォルト値を からコピーして、デフォルト値を決して変更しないようにするにはどうすればよいでしょうか? Unity エディターでデフォルトをアセットとして編集できるようにしたいと考えています。

B) CreateInstance を使用すると、パフォーマンスが低下しますか? このオブジェクト プーリングを行っている全体的な理由は、オブジェクトのインスタンス化のパフォーマンス コストを削減することです。スクリプト可能なオブジェクトをインスタンス化して、遅いコードを再導入するのは嫌いです。

C) このアプローチは大丈夫ですか? または、プールに戻る前にオブジェクトをリセットするより良い方法はありますか?

いくつかの回答に基づいて編集:私はすでにフィールドの長いリストを持ち、これらのフィールドのデフォルト値を辞書に保存するセットアップを持っています。しかし、フィールドを追加/変更/削除するたびに、いくつかの場所でコードを変更する必要があることがわかりました

試みられた解決策 (ただし、間違っています。以下を参照してください): ScriptableObject の拡張メソッドを作成しました。

using UnityEngine;
using System.Reflection;

public static class ScriptableObjectExtension {

    public static T ShallowCopy<T> (this T orig) where T : ScriptableObject {
        T copiedObject = ScriptableObject.CreateInstance<T> ();
        FieldInfo[] myObjectFields = orig.GetType ().GetFields (
                                         BindingFlags.NonPublic | BindingFlags.Public |
                                         BindingFlags.Instance);

        foreach (FieldInfo fi in myObjectFields) {
            fi.SetValue (copiedObject, fi.GetValue (orig));
        }
        return copiedObject;
    }
}

最終的解決:

上記のスクリプトは、スクリプト可能なオブジェクトを複製するために機能しましたが、そのソリューションでは間違った道をたどっていたようです。

以下の何人かの人々は、プーリングはほとんどのアプリケーションの単一性においてそれほど重要ではないことを指摘しています。プロファイラーによるフレームレートが約 30 ~ 15 fps だったので、最初はプーリングを入れようとしましたが、プーリングがそれを改善するのに役立つと思いました。

コメントに基づいて、もう少し深く掘り下げたところ、LogStringToConsole というプロセスがあることがわかりました。これは、私の Debug.Log ステートメントが速度を落とすのと同じくらい簡単なことではないでしょうか!? それらを削除したところ、大きなスパイクはなくなりました。どうやら Debug.Log は大きなパフォーマンスの問題を引き起こします。今は60fpsを大きく超えています。このため、これらのオブジェクトをプールしないことにしました (ただし、1 秒間に数回生成される別のシナリオでは、より単純なオブジェクトにプールを使用しています)。これは、ここではスクリプト可能なオブジェクトについてまったく心配する必要がないことを意味します。私は今、プレハブと Init メソッドをロードして物事をセットアップし、それらが使い果たされるとオブジェクトが破棄されるインスタンスを作成しました。

インスタンス化/破棄の使用に戻ったとき、パフォーマンスに大きな変化は見られませんでした。すべての応答に感謝します!

4

3 に答える 3

4

非アクティブ化するたびにオブジェクトの値をリセットするだけでよい場合は、次のように単純に使用できませんでした:

OnEnable()
{
    default = data;
}

OnDisable()
{
    data = default;
}

これにより、アクティブ化されたときにデフォルトデータを保存/割り当て、非アクティブ化されたときにデータをデフォルト値にリセットできます。

于 2016-06-16T09:20:09.807 に答える
2

Awake()オブジェクトを作成したとき、または必要Start()なデフォルト値を変数の束に保存するか、辞書に保存するときはどうですか?

その後、値をリセットするには、名前が呼び出されたメソッドを作成し、Reset()すべての変数に以前に保存したデフォルト値を割り当てます。

例えば

// method 1
Dictionary<string, object> defaultValues = new Dictionary<string, object>();
int speed = 10;
List<float> scores = new List<float>() {1.5f, 3.4f};

// method 2
SomeClass something = new SomeClass();
SomeClass defaultSomething = new SomeClass();

// and if the type can use const
string sth = "abc";
const string defaultSth = "abc";

void Awake()
{
    defaultValues.Add("speed", speed); 
    defaultValues.Add("scores", new List<float>(scores)); // new list because it is reference type, 
    //and you dont want to store reference to the list because it will be edited during runtime  

    defaultSomething = something.Clone(); // edit, but you need to implement clone by yourself for that class or do something that will make other instance of the class with same value
}

void Reset()
{
    speed = (int) defaultValues["speed"];
    scores = (List<float>) defaultValues["scores"];

    something = defaultSomething.Clone();
    sth = defaultSth;
}

欠点は、すべてのインスタンスがメモリを占有する独自のデフォルト変数を格納することです。後で必要に応じてそれらを static または const に変更できます。

もう 1 つの方法は、デフォルト値を格納するためだけに使用される 1 つのインスタンスを作成し (実行時にこれを変更しないでください)、C# リフレクションを使用してすべてのメンバー値をコピーすることです。

C# リフレクションを使用して基本クラスのプロパティをコピーする

お役に立てれば

于 2016-06-16T01:07:46.857 に答える
-9

ノート!!

2014年頃から、原則としてUnityにプールする必要はありません。Unity はパフォーマンスを大幅に改善したため、通常のゲーム シナリオでは必要ありません。

OPは、ハンドプーリングの試みを削除するだけで問題を解決したことに注意してください。

近年、Unity は大幅に改善されました。

  • ガベージコレクション

  • メモリ処理

  • ヒューリスティックをトリガーするプールのような処理

  • プレハブとインスタンス化のプロセス

ビデオ ゲームでのオブジェクトの作成は、最新のハードウェアではまったく簡単です。「弾丸」のような典型的なシナリオは、おそらく 1 秒間に 12 個程度のアイテムしかありません。Unity の初期の頃は、弾丸などの典型的なゲームの複数のオブジェクト要件を達成するために、手動でプーリングを記述する必要がありました。Unity の努力のおかげで、これは典型的なゲーム シナリオ (弾丸、複数の NPC など) ではまったく不要になりました。

何らかの理由で必要に応じてプールすることはできますが、典型的なビデオ ゲームのニーズでは、パフォーマンスの観点からはまったく不要です。2D または 3D。

于 2016-06-16T16:58:38.447 に答える