web-dev-qa-db-ja.com

Unityが非静的パブリックフィールドの初期化された値を無視するのはなぜですか?

InvokeRepeating() を使用して、ゲームのメソッドを呼び出しています。 GameObjectクラスの1つのInvokeRepeating()メソッドでStart()を呼び出します。 InvokeRepeating()repeatRateパラメーターを設定するには、secondsBetweenBombDropsというパブリックフィールドを渡します。

Unityは、コードでsecondsBetweenBombDropsに指定した値を無視し、代わりにsecondsBetweenBombDropsが静的修飾子なしで宣言されている場合、デフォルト値(つまり1)を使用します。

public float secondsBetweenBombDrops = 10f;
void Start() {
    InvokeRepeating("DropBomb", 1f, secondsBetweenBombDrops);
}

ただし、static修飾子をsecondsBetweenBombDropsに追加すると、コードは期待どおりに動作し、正しい値の10が使用されます。

public static float secondsBetweenBombDrops = 10f;
void Start() {
    InvokeRepeating("DropBomb", 1f, secondsBetweenBombDrops);
}

適切な値を使用するために、このフィールドにstatic修飾子が必要なのはなぜですか?

Unityインスペクターで、スクリプトコンポーネントはsecondsBetweenBombDropsが1であることを示します。このデフォルト値の1は、ゲームの開始時にプレハブをインスタンス化するか、ゲームの実行中にプレハブインスタンスを作成するかに関係なく存在します。

37
Yvette Colomb

シリアライゼーションの両刃の剣

Unityは、コーディングの知識が限られている人(初心者、デザイナー)を含め、すべての人にとって物事を簡単にしたいと考えています。

それらを助けるために、Unityはインスペクターにデータを表示します。これにより、コーダーはコードを作成し、デザイナーはMonoDevelop/an IDEを開かずに値を調整して設計できます。

インスペクターに値を表示するには、2つの方法があります。

public int myVar = 10;
[SerializeField] private int myOtherVar = 0; // Can also be protected

2つ目は、カプセル化の原則に準拠しているため、より優れています(変数はプライベート/保護され、メソッドまたはプロパティを介して変更されます)。

エディターで変数を表示すると、スクリプトで指定された値は、スクリプトをドラッグするときにのみ使用されます。 Unityはこれらの値をシリアル化し、スクリプトの変更を気にしません。例えば、myVarが事後のスクリプト内で20に設定されている場合、これは混乱につながる可能性があり、使用されません。シリアル化はシーンファイルに書き込まれます。

この例の2行は、まったく同じように機能します。

可能な解決策

スクリプトコンポーネントの設定ホイールで[リセット]を押すと、Unityにスクリプト内の新しい値を考慮させることができます。これにより、コンポーネントの他のすべての変数もリセットされるため、意図されている場合にのみこれを行ってください。

変数をプライベートにして属性[SerializeField]を省略すると、シリアル化プロセスが無効になるため、Unityはシーンファイル内で表示する値を探しません。代わりに、実行時にスクリプトによって値が作成されます。

コンポーネントをUnityに追加すると、コンポーネントのタイプの新しいオブジェクトが作成されます。表示される値は、そのオブジェクトのシリアル化された値です。このため、メンバー値のみを表示でき、静的変数はシリアル化できないため表示できません。 (これは.NET仕様であり、Unityに厳密には固有ではありません。) nityは静的フィールドをシリアル化しない であるため、static修飾子を追加することで問題が解決したようです。

OPの説明

OPケースでは、コメントに基づいて、エディターでパブリックフィールドに値1が表示されていました。この値は、最初に宣言したときに実際にフィールドに与えた可能性が最も高い値であるにもかかわらず、デフォルト値であると考えました。スクリプトをコンポーネントとして追加した後、値10を作成し、まだ値1を使用しているためバグがあると考えました。これで、設計どおりに正常に動作していることを理解できます。

Unityは何をシリアライズしますか?

デフォルトでは、Unityは値の型(int、float、enumなど)と文字列、配列、リスト、MonoBehaviourをシリアル化して表示します。 (エディタスクリプトを使用して外観を変更することは可能ですが、これはトピック外です。)

以下:

public class NonMonoBehaviourClass{
   public int myVar;
}

デフォルトではシリアル化されません。ここでも、これは.NET仕様です。 Unityは、エンジン要件の一部としてMonoBehaviourをデフォルトでシリアル化します(これにより、コンテンツがシーンファイルに保存されます)。エディターで「クラシック」クラスを表示する場合は、次のように言ってください。

[System.Serializable]
public class NonMonoBehaviourClass{
   public int myVar = 10;
}

明らかに、ゲームオブジェクトに追加できないため、MonoBehaviour内で使用する必要があります。

public class MyScript:MonoBehaviour{
     public NonMonoBehaviourClass obj = new NonMonoBehaviourClass();
}

これにより、インスペクターにオブジェクトが表示され、myVarのインスタンスのNonMonoBehaviourClass変数を変更できます。繰り返しますが、スクリプト内のmyVarへの変更は、値がシリアル化されてシーンに保存された後は考慮されません。

インスペクターでの表示に関する追加のヒント

最後に、インターフェイスには変数が含まれていないため、インスペクタにも表示されません。メソッドとプロパティだけです。デバッグモードでは、プロパティはデフォルトでは表示されません。このモードは、インスペクターの右上隅に3本の線があるボタンを使用して変更できます。最初の2つの設定はNormal/Debugです。最初のものはデフォルトのもので、2番目のものもプライベート変数を表示します。これは値を監視するのに便利ですが、エディターから変更することはできません。

したがって、表示するインターフェイスが必要な場合、抽象クラスは同様の機能を提供します(多重継承を除く)が、MonoBehaviourになる可能性があるため、抽象クラスを検討する必要があります。

参照:

http://docs.unity3d.com/ScriptReference/SerializeField.html

http://docs.unity3d.com/Manual/script-Serialization.html

https://www.youtube.com/watch?v=9gscwiS3xs

https://www.youtube.com/watch?v=MmUT0ljrHNc

38
Everts