2017-06-19 7 views
2

私は2つのクラスを持っています。 1つはGameManagerと呼ばれ、もう1つはと呼ばれます。 私はこのようなEneimesクラスからこれらの二つの変数にアクセスしようとしている2つの私はインスペクタcurrentLevel=1から変更されているGameManager内の変数とtotalEnemy=10.インスペクタの値はUnity3dの別のクラスからアクセスできません

// GameManager.cs 
    private static GameManager instance = new GameManager(); 
    public static GameManager get(){ return instance; } 

    public int currentLevel; 
    public int curLevel { get; set; } 
    public int totalEnemy; 
    public int totLevel { get; set; } 


    void Start() { 
     curLevel = currentLevel; 
     totLevel = totalEnemy; 
    } 

を持っています。しかし毎回それは私にcurLevel = 0を与えるが、私はcurLevel = 1を得ることを期待している。私は間違っているの?

// Enemies.cs 

    void Start() { 
     Debug.Log (GameManager.get().curLevel); // always output = 0 
    } 
+0

あなたは 'currentLevel'を変更したようですが、 'instance'を変更する必要があります –

答えて

4

private static GameManager instance = new GameManager();が問題です。

GameObjectにスクリプトが添付されている場合、そのスクリプトのタイプのインスタンスは、スクリプト内でthisと参照されます。つまり、同じスクリプトが複数のGameObjectに添付されている場合、同じタイプのインスタンスが複数存在する可能性があります。したがって

特定のインスタンスは、インスペクタで設定さcurLevel = 1を有するインスタンス特定GameObjectに取り付けられたタイプのあります。これは、スクリプト内でthisと呼ばれることを意味します。 より

あなたのコードのようGameManagerの新しいインスタンスを宣言するとstatic GameManager instanceは、別のインスタンスを指しているので、あなたは基本的にインスペクタのすべての値を無視しているが、インスタンスあなたはインスペクタでの値を設定します。

インスペクタを使用して宣言した特定のインスタンスを使用するには、次の操作を行う必要があります。私はAwake()Start()を変更

using System.Collections.Generic; 
using System.Collections; 
using UnityEngine; 

public class GameManager : MonoBehaviour 
{ 
    private static GameManager instance; 
    public static GameManager get() { return instance; } 

    public int currentLevel; 
    public int curLevel { get; set; } 
    public int totalEnemy; 
    public int totLevel { get; set; } 

    void Awake() 
    { 
     if (instance == null) 
     { 
      instance = this; 
     } 
     else 
     { 
      Debug.LogError(string.Format("GameManager.Awake(): More than one instances of this type {0} is being initialised but it's meant to be Singleton and should not be initialised twice. It is currently being initialised under the GameObject {1}.", this.GetType(), this.gameObject.name)); 
      Destroy(gameObject); 
     } 

     curLevel = currentLevel; 
     totLevel = totalEnemy; 
    } 
} 

注意。これは、他のスクリプトからこのメソッドで初期化された値を参照しているためで、実行時に異なるMonoBehavioursの間で最初に呼び出されるStart()を保証することができないためです。しかし、UnityはAwake()が常にStart()より早く呼び出されることを保証します。さらに、Awake()で自己初期化可能な変数を初期化し、この実行順序のために他のスクリプトに依存する変数をStart()に初期化するのはUnityのベストプラクティスです。

最後に、あなたのシーンにGameManagerという成分を持つ複数のGameObjectがある場合に問題が発生します。このようなオブジェクトが2つある場合を考えてみましょう。シーンがロードされると、各スクリプトはAwake()を呼び出し、両方ともprivate static GameManager instance;を2つのthisのそれぞれに設定します。その結果、あるものが別のものに上書きされます。

このスクリプトを慎重に使用し、にこのスクリプトが1つだけ含まれていることを確認してください。しかし、あなたのコードを知らない人が思考せずにそれを使うことができるように、コードを書くべきです。が簡単に検出される可能性があります。です。

EDIT:

OPさんのコメントに対応するため、このタイプは複数回のプロジェクトに比べて初期化されるとき、私が処理するコードを追加しました。 @Karduxの提案に加えて、プロジェクトをにサイレントにしたくないので、私はDebug.LogError()を追加しました。を解決しました。問題が発生した場合は、通知を受けたいと思います。

プロジェクトで頻繁にSingleton Sを使用している場合は、すべての子Singletonのためのプロセスをチェックし、このインスタンスを扱う親abstract class Singletonを持ちたい、とSingletonからGameManager継承している場合があります。

ただし、誤って使用すると悪い設計パターンとみなされますので、慎重にSingletonを使用してください。 (そして、私はそれを正しく使うことができないので、それを使用することはできません。)

+0

素晴らしい説明!!また、Awake()とStart()の使用について疑問を晴らしました。プロジェクト全体でこのスクリプトのインスタンスを1回だけ確実にすることについて、それをどのように実装するのかについての考え方は?私はDontDestroyOnLoad(this.gameObject)を使用します。この正しいアプローチです – jquery404

+2

インスタンスを使用するときの良い方法は、インスタンスがすでに存在するかどうかをチェックすることです(シングルトンアプローチとしてのみ必要な場合に備えて):if(instance == null){instance = this; } else {DestroyImmediate(gameオブジェクト);} } '。これにより、ゲームの他のシーンからコンポーネントを削除するのを忘れた場合の頭痛を軽減することができます:) – Kardux

+0

@ jquery404私はそれが助けてうれしいです。編集された答えをご覧ください。 – BrokenBacon

関連する問題