現在、Unity3D エンジンを使用して C# を使用していますが、次の問題が発生しました。
アクセスする必要がある別のクラスのインスタンスへの 2 つのプライベート参照を持つクラスを作成しました。クラスの複数のインスタンスを作成して参照を設定すると、すべてのインスタンスが同じ変数を使用していることがわかりました。インスタンスを破棄していて、その直前に、参照を保持する 2 つの変数を null に設定したときにこれに気付きました。それを行った直後に、他のすべてのインスタンスはまだ参照にアクセスしようとしていたため、NullReferenceExceptions をスローしていました。参照されたオブジェクトは問題なく、他のスクリプトは引き続きそれらにアクセスできます。
構造を示す擬似コードを次に示します。
public class Character
{
// Character data
}
public class StatusEffect
{
private Character target;
private Character originator;
public void Init(Character _Target, Character _Originator)
{
target = _Target;
originator = _Originator;
}
public void Destroy()
{
target = null;
originator = null;
}
}
プログラムでは、次のように呼び出されます。
StatusEffect effect = new StatusEffect();
effect.Init(player1, player2);
// Time goes by
effect.Destroy();
Destroy() を呼び出した後、すべての StatusEffect の 2 つの参照は null になります。
これは、StatusEffects を破棄するときだけでなく、新しいものを作成するときの問題でもあります。新しいインスタンス内からの参照に触れるとすぐに、すべての StatusEffects は、新しい StatusEffect によって指定された 2 つの Character を参照します。
この問題を解決できる理由や方法がわかりません。誰かがこの問題について私に教えてもらえますか?
乾杯、バルタロス
編集:
要求された実際のコードは次のとおりです。いくつかの StatusEffects を保持するコンテナー クラスがあります。起動するとすぐに、それらすべてが初期化されます。
public class CElementTag
{
// ..Other data..
public float f_Duration; // Set in the editor
private CGladiator gl_target;
private CGladiator gl_originator;
private float f_currentDuration;
public CStatusEffect[] ar_statusEffects;
// Starts the effect of the element tag
public void StartEffect(CGladiator _Originator, CGladiator _Target)
{
gl_originator = _Originator;
gl_target = _Target;
f_currentDuration = f_Duration;
for(int i = 0; i < ar_statusEffects.Length; i++)
ar_statusEffects[i].Initialize(gl_originator, gl_target);
}
// Ends the effect of the element tag
public void EndEffect()
{
for(int i = 0; i < ar_statusEffects.Length; i++)
{
if(ar_statusEffects[i] != null)
ar_statusEffects[i].Destroy();
}
}
// Called every update, returns true if the tag can be destroyed
public bool ActivateEffect()
{
f_currentDuration -= Time.deltaTime;
if(f_currentDuration <= 0.0f)
{
EndEffect();
return true;
}
for(int i = 0; i < ar_statusEffects.Length; i++)
{
if(ar_statusEffects[i] != null && ar_statusEffects[i].Update())
RemoveStatusEffect(i);
}
return false;
}
// Removes expired status effects
private void RemoveStatusEffect(int _Index)
{
// Call destroy method
ar_statusEffects[_Index].Destroy();
// Remove effect from array
for(int i = _Index; i < ar_statusEffects.Length - 1; i++)
ar_statusEffects[i] = ar_statusEffects[i+1];
ar_statusEffects[ar_statusEffects.Length - 1] = null;
}
}
実際の StatusEffect クラスは、2 つの参照と、動作に必要なその他のデータを保持しています。それを継承するいくつかのクラスがあるため、仮想メソッドがあります。
public class CStatusEffect
{
// ..Necessary data..
// References
protected CGladiator gl_target;
protected CGladiator gl_originator;
virtual public void Initialize(CGladiator _Target, CGladiator _Originator)
{
gl_target = _Target;
gl_originator = _Originator;
// ..Initialize other necessary stuff..
}
virtual public void Destroy()
{
gl_target = null;
gl_originator = null;
// ..Tidy up other data..
}
virtual public bool Update()
{
// ..Modifying data of gl_target and gl_originator..
// Returns true as soon as the effect is supposed to end.
}
}
これは、この問題に関連するすべてのコードである必要があります。
EDIT2
@KeithPayneエディタで定義され、xmlに保存されたElementTagsの静的配列があります。プログラムの開始時に、静的配列は xml をロードし、すべての要素タグを格納します。使用する新しい要素タグを作成するときは、次のコンストラクターを利用します。
// Receives a static tag as parameter
public CElementTag(CElementTag _Tag)
{
i_ID = _Tag.i_ID;
str_Name = _Tag.str_Name;
enum_Type = _Tag.enum_Type;
f_Duration = _Tag.f_Duration;
ar_statusEffects = new CStatusEffect[_Tag.ar_statusEffects.Length];
Array.Copy(_Tag.ar_statusEffects, ar_statusEffects, _Tag.ar_statusEffects.Length);
}
配列を新しいタグにコピーするには、別の方法を使用する必要がありますか? Array.Copy はコピー元配列のディープ コピーを作成し、それをコピー先配列に格納すると考えました。実際に浅いコピーを作成している場合、問題がどこから来ているのか理解できます。