2016-08-31 4 views
0

私は答えを探しましたが、ここでも紛失しています。私はいくつかのキャラクタージェネレーターを持っています。それはプレーヤーキャラクター変数を作成します。それらは、オブジェクトCharacterDataのコンポーネントである新しいスクリプト "PlayerCharacterData"に保存されます。このオブジェクトにはDontDestroyOnLoadがあるため、他のシーンにも適用されます。文字を生成した後、ゲームは文字データをそのスクリプトにロードし、シーンを切り替えると、データはバイナリファイルから適切にロードされます(先にシリアライズされます)。C#、Unity - シーン間で値を永続化/アクセス/変更する最も便利で高速な方法は?

しかし、私はこのコアを持っているので、 "PlayerCharacterData"クラスからデータを読み込むためにUIオブジェクトを多数必要とするので、UIはすべてのフィールドを埋め込むことができます。その後、ゲーム全体は "PlayerCharacterData"の変数値に依存します。

これは私のPlayerCharacterDataスクリプトの一部です:

public class PlayerCharacterData : MonoBehaviour { 

    void Awake(){ 
     DontDestroyOnLoad (this); // keeps the object when changing scenes 
    } 

    // below we have data stored in variables. It was loaded by another script.  
    public string characterName; 
    public int characterSpeed; 
    public List<Weapon>characterInvWeapon; 
    // etc, more variables x50-70 
    } 

は今ここに私のpublicクラスUIPlayerCharacterDataの例です。

public class UIPlayerCharacterData : PlayerCharacterData { 

    public void NameToCharacterDataUI() { 
     // this would be required to make it work: PlayerCharacterData playerCharacterData = GameObject.FindGameObjectWithTag("CharacterData").GetComponent<PlayerCharacterData>(); 
     Text_ch_sh_char_name_string.text = playerCharacterData.characterName; 
    } 
    // etc, more functions like that x50-70  

    void Awake() { 
     PlayerCharacterData playerCharacterData = GameObject.FindGameObjectWithTag("CharacterData").GetComponent<PlayerCharacterData>(); 
     NameToCharacterDataUI(); 
     // etc, calling more functions x50-70 
    } 
} 

問題があり、これらのクラスは、異なるオブジェクト上にある:私はより速く「検索」を作るためにユニティ・エディタではCharacterDataオブジェクトにタグを追加しました。最初は1番目のシーンから継承するCharacterDataオブジェクトのコンポーネント、2番目は2番目のシーンの主要なUIパネルのコンポーネントです。 2番目のクラスには、UIフィールドがたくさんあります。それぞれは、ファーストクラス(CharacterDataオブジェクトのコンポーネント)からデータを受け取ります。つまり、UIのデータを引き出すための50〜70の変数があります。

ゲーム全体でPlayerCharacterDataスクリプトのデータを取得して変更する必要があるため、これは単なるスタートです。

ここで、playerCharacterSheet変数に参照がないため、NameToCharacterData()関数が機能していないことがわかりました。「オブジェクト参照がオブジェクトのインスタンスに設定されていません」。それがAwake()で整理されたと思った - 私は間違っていた。 設定する必要があると思われるので、UIのフィールドに50-70の関数を埋め込むたびに、 PlayerCharacterData playerCharacterData = GameObject.Find("CharacterData").GetComponent<PlayerCharacterData>();

...を設定する必要があります。 ゲーム内の他のすべてのシステムについて言及する必要はありません。 さらに、毎回のプレイヤーはUIを開き、UIフィールドを再生成する必要があります。

もっと速く、より便利な方法がありますか? 私はすべてのPlayerCharacterData変数を静的にすることを考えていました - これは雷として速くなりますが、私たちのエンジンをマルチプレイヤー(1人から200人まで)のために準備したいと思っています。だから確かではない。

また、Monobehaviourを使わずに文字データ変数をスクリプトに保存する方がいいですから、gameObjectに接続しないと、PlayerCharacterData playerCharacterData = new PlayerCharacterData();を使ってクラスから値を取得できますか?本当に速く、どこからでもアクセス可能な値が必要です。

現時点では静的変数の使用を検討していますが、より良い解決策がある場合や、マルチプレイヤーゲームの静的変数を作成するより良い方法がある場合はお知らせください。

+0

重複しません。私はすでにそこを見て、私が上で提案した解決策を提供します。多くの異なるオブジェクトからのデータへのアクセスをより良く実装する方法を探しています。本当に多くのオブジェクトを高速に処理します。 –

+0

注目。クローズされた投票が取り下げられ – Serlite

+0

ここで会話を続けましょう。私はあなたのnullエラーの問題がなくなったと思います。 'PlayerCharacterData playerCharacterData = GetPlayerCharacterData();'警告は、宣言後に 'playerCharacterData'変数を使用していないためです。 'playerCharacterData.accessOtherVariable = 5;'または 'playerCharacterData.callOtherFunction ...'を実行する必要があります。 – Programmer

答えて

1

PlayerCharacterData参照をどこに格納しようとしているのかはわかりませんが、Awake()内でローカルに宣言しているので、そのメソッドの外では使用できません。 Awake()がこの参照を格納するのに十分ではないことも私にはっきりしていませんが、その場合でもnull参照を避けるためにすべてのアクセスを「安全な」方法でラップすることができます。多分このような何か?Singletonパターンで

public class UIPlayerCharacterData : PlayerCharacterData { 

    PlayerCharacterData playerCharacterData; 

    public void NameToCharacterDataUI() { 
     Text_ch_sh_char_name_string.text = GetPlayerCharacterData().characterName; 
    } 
    // etc, more functions like that x50-70  

    void Awake() { 
     PlayerCharacterData playerCharacterData = GetCharacterData(); 
     NameToCharacterDataUI(); 
     // etc, calling more functions x50-70 
    } 

    PlayerCharacterData GetPlayerCharacterData() { 
     if (playerCharacterData == null) { 
      playerCharacterData = GameObject.FindGameObjectWithTag("CharacterData").GetComponent<PlayerCharacterData>(); 
     } 
     return playerCharacterData; 
    } 
} 
+0

ありがとう、私はそれをチェックします。私は公開のPlayerCharacterData playerCharacterDataを宣言しました。そのスクリプトでは、Start()またはAwake()は他の関数の参照をまったく設定していませんでした。 NameToCharacterData()のようなすべての特定の関数にこのような参照を作成するまで、すべての変数は空です。なぜ私はもっとグローバル化できないのか分かりません。 –

+0

ps。あなたは私がそれをローカルで宣言したと言った。それをグローバルに宣言する方法は?何らかの理由で私たちのことを理解することはできません。私は、そのクラスにデータを使ってグローバルかつ広大にアクセスする必要があります。クラス内のすべての関数ではなくクラス全体で行うのが良い方法です。 –

+0

あなたのやり方は実際にはうまくいった。しかし、なぜ私はそのplayerCharacterData照会グローバル、公共を作ることができないのですか?デフォルトではすべての関数の中でnullになっているようです... UIを開くたびに100回参照することは過度のことです。 //うわー、これはplayerCharacterData.characterName o.Oでも動作しますので、今はnullではありません。奇妙な。 なぜ私の方法がうまくいかなかったのか分かりませんが、あなたのことは分かりません。 –

1

は、ウィッヒは、一つのオブジェクトにクラスのインスタンス生成を制限デザインパターン、あなたは唯一のグローバルインスタンスを作成し、diferentスクリプトから、それをaccsesこの方法です。

MyClassのスクリプト:

public class MyClass : MonoBehaviour { 
    void Awake() { 
     Debug.Log(Manager.Instance.myGlobalVar); 
    } 
} 

グローバルなスクリプト:

public class Manager : Singleton<Manager> { 
    protected Manager() {} // guarantee this will be always a singleton only - can't use the constructor! 

    public string myGlobalVar = "whatever"; 
} 

SingletonImplementation:

using UnityEngine; 

/// <summary> 
/// Be aware this will not prevent a non singleton constructor 
/// such as `T myT = new T();` 
/// To prevent that, add `protected T() {}` to your singleton class. 
/// 
/// As a note, this is made as MonoBehaviour because we need Coroutines. 
/// </summary> 
public class Singleton<T> : MonoBehaviour where T : MonoBehaviour 
{ 
    private static T _instance; 

    private static object _lock = new object(); 

    public static T Instance 
    { 
     get 
     { 
      if (applicationIsQuitting) { 
       Debug.LogWarning("[Singleton] Instance '"+ typeof(T) + 
        "' already destroyed on application quit." + 
        " Won't create again - returning null."); 
       return null; 
      } 

      lock(_lock) 
      { 
       if (_instance == null) 
       { 
        _instance = (T) FindObjectOfType(typeof(T)); 

        if (FindObjectsOfType(typeof(T)).Length > 1) 
        { 
         Debug.LogError("[Singleton] Something went really wrong " + 
          " - there should never be more than 1 singleton!" + 
          " Reopening the scene might fix it."); 
         return _instance; 
        } 

        if (_instance == null) 
        { 
         GameObject singleton = new GameObject(); 
         _instance = singleton.AddComponent<T>(); 
         singleton.name = "(singleton) "+ typeof(T).ToString(); 

         DontDestroyOnLoad(singleton); 

         Debug.Log("[Singleton] An instance of " + typeof(T) + 
          " is needed in the scene, so '" + singleton + 
          "' was created with DontDestroyOnLoad."); 
        } else { 
         Debug.Log("[Singleton] Using instance already created: " + 
          _instance.gameObject.name); 
        } 
       } 

       return _instance; 
      } 
     } 
    } 

    private static bool applicationIsQuitting = false; 
    /// <summary> 
    /// When Unity quits, it destroys objects in a random order. 
    /// In principle, a Singleton is only destroyed when application quits. 
    /// If any script calls Instance after it have been destroyed, 
    /// it will create a buggy ghost object that will stay on the Editor scene 
    /// even after stopping playing the Application. Really bad! 
    /// So, this was made to be sure we're not creating that buggy ghost object. 
    /// </summary> 
    public void OnDestroy() { 
     applicationIsQuitting = true; 
    } 
} 

要件:(GetOrAddComponentから)MonoBehaviourExtended.cs

static public class MethodExtensionForMonoBehaviourTransform { 
    /// <summary> 
    /// Gets or add a component. Usage example: 
    /// BoxCollider boxCollider = transform.GetOrAddComponent<BoxCollider>(); 
    /// </summary> 
    static public T GetOrAddComponent<T> (this Component child) where T: Component { 
     T result = child.GetComponent<T>(); 
     if (result == null) { 
      result = child.gameObject.AddComponent<T>(); 
     } 
     return result; 
    } 
} 

Source

1

データを格納する静的クラスを使用します。可能ならば、私はSingletonの代わりに静的なクラス "anti-pattern"を使うことを好みます。

public static class Cache 
{ 
    /// <summary> 
    /// Actual cache storage for data. 
    /// </summary> 
    private static Dictionary<string, object> _cache = new Dictionary<string, object>(); 

    /// <summary> 
    /// Adds/changes an object to the cache. 
    /// </summary> 
    /// <param name="key">The identifier of stored object to access it.</param> 
    /// <param name="obj">The object to store</param> 
    public static void Set(string key, object obj) 
    { 
     if (string.IsNullOrEmpty(key)) 
      throw new Exception("key must be a non empty string"); 

     if (_cache.ContainsKey(key)) 
      _cache[key] = obj; 
     else 
      _cache.Add(key, obj); 
    } 

    /// <summary> 
    /// Get the cached object by key. 
    /// </summary> 
    /// <typeparam name="T"></typeparam> 
    /// <param name="key"></param> 
    /// <returns></returns> 
    public static T Get<T>(string key) 
    { 
     var obj = _cache.ContainsKey(key) ? _cache[key] : null; 
     return (T)obj; 
    } 

    /// <summary> 
    /// Removes an object. 
    /// </summary> 
    /// <param name="key">The identifier of stored object</param> 
    public static void Remove(string key) 
    { 
     if (string.IsNullOrEmpty(key)) 
      throw new Exception("key must be a non empty string"); 

     if (_cache.ContainsKey(key)) 
      _cache.Remove(key); 
    } 
} 
関連する問題