0

現在、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 はコピー元配列のディープ コピーを作成し、それをコピー先配列に格納すると考えました。実際に浅いコピーを作成している場合、問題がどこから来ているのか理解できます。

4

4 に答える 4

1

メソッドを介してオブジェクトへの参照を渡しているため、Init()実際にはオブジェクトを「コピー」しているのではなく、メモリ内の同じ基礎となるオブジェクトへの参照を維持しているだけです。

players同じ基になるオブジェクトへの同じ参照を持つ複数のオブジェクトがある場合、プレーヤー 1 が行った変更は、プレーヤー 2 が使用しているオブジェクトに影響します。

そうは言っても、実際にはメソッド内のオブジェクトを破棄しているわけではありませんDestory。ローカル インスタンス参照を Null に設定するだけで、StatusEffects の他のインスタンスには影響しません。他の何かがオブジェクトを破棄していないこと、または他のインスタンスを適切に初期化していないことを確信していますか?

渡されたオブジェクトの完全なコピーを取得したい場合は、ICloneable インターフェイスを見てください。オブジェクトのコピーを各プレーヤーに渡したいようです。

public class Character : ICloneable
{
    // Character data

    //Implement Clone Method
}

public class StatusEffect
{
    private Character target;
    private Character originator;

    public void Init(Character _Target, Character _Originator)
    {
        target = _Target.Clone()
        originator = _Originator.Clone();
    }
于 2013-09-26T13:29:55.523 に答える
0

Keith Payne のおかげで、どこに問題があるのか​​がわかりました。CElementTag のディープ コピーを作成していましたが、ar_statusEffects 配列のコピーは作成していませんでした。Array.Copy が実際には作成されていないのに、配列のディープ コピーを作成していると誤って想定していました。

CStatusEffect の IClonable インターフェイスを実装し、Clone() メソッドを使用して静的配列の各メンバーの真のディープ コピーを作成し、それを新しいタグ ar_statusEffects 配列に追加します。このようにして、同じ静的効果への参照ではなく、効果の個別のインスタンスがあります。

みんな、特にキース・ペインの助けとサポートに感謝します!

于 2013-09-27T06:43:59.040 に答える