2016-08-31 13 views
0

我寻找答案,但仍然在这里失去。我有一些角色生成器,它会创建并保存玩家角色变量。它们被保存到一个新的脚本“PlayerCharacterData”中,它是对象CharacterData的一个组件。该对象具有DontDestroyOnLoad,因此它会持续到其他场景。在生成角色之后,游戏将角色数据加载到该脚本中,并且当我切换场景时,数据从二进制文件(先前序列化)中正确加载。C#,Unity - 最方便快捷的方式来持久/访问/修改场景之间的值?

但是现在我有了这个核心,我需要我的众多UI对象从“PlayerCharacterData”类加载数据,所以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 
    } 

现在,这里的例子我的公共类UIPlayerCharacterData的。我加了一个标签对象CharacterData在Unity编辑器进行“查找”更快:

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对象的组件,第二是第二场景中主UI面板的组件。第二类有很多UI字段需要填写,只是向你展示了其中的一个。每个都从第一类(CharacterData对象上的组件)获取数据。因此,UI需要50-70个变量才能从中提取数据。

这只是一个开始,因为整个游戏将需要获取和修改PlayerCharacterData脚本中的数据。

现在,我发现函数NameToCharacterData()不起作用,因为它没有在playerCharacterSheet变量中引用:“对象引用未设置为对象的实例”。认为它在Awake()中被整理出来 - 我错了。 因此,似乎我们需要设置 PlayerCharacterData playerCharacterData = GameObject.Find("CharacterData").GetComponent<PlayerCharacterData>();

...在填充UI中的字段的每个50-70函数中。 更不用说游戏中的其他系统也需要这样做。 另外,每次玩家打开UI时,需要重新生成这些UI字段。

有没有更快,更方便的方法呢? 我在考虑让所有的PlayerCharacterData变量都是静态的 - 这将会像雷电一样快,但我更愿意让我们的引擎为多人(1到200名玩家)做好准备。所以不确定。

另外,将字符数据变量保存到没有Monobehaviour的脚本中是不是更好,因此没有连接到一个gameObject,也许我们可以使用PlayerCharacterData playerCharacterData = new PlayerCharacterData();来获得该类的值?我只需要这些值可以非常快速地从任何地方访问。

在这一点上真的考虑使用静态变量,但如果有更好的解决方案,或者如果有更好的方法为多人游戏创建静态变量,请告诉我。

+0

不是重复。我已经看过,它提供了我上面提出的解决方案。研究如何更好地实现访问来自许多不同对象的数据,其中很多是快速的。 –

+0

已注意。收起投票。 – Serlite

+0

让我们继续在这里谈话。我猜你的空错误问题已经消失了。至于'PlayerCharacterData playerCharacterData = GetPlayerCharacterData();'警告,那是因为你在声明它后没有使用'playerCharacterData'变量。你必须做'playerCharacterData.accessOtherVariable = 5;'或'playerCharacterData.callOtherFunction ...' – Programmer

回答

1

我不清楚你在哪里试图存储你的PlayerCharacterData引用,但是因为你在Awake()中本地声明了它,所以在该方法之外将不可用。我也不清楚Awake()如何不足以存储这个引用,但即使在这种情况下,您也可以在“安全”方法中包含对它的所有访问,以避免引用空值。也许这样?

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引用全局化,公开?似乎它在每个功能默认为空...每次打开UI引用100次似乎矫枉过正。 //哇,似乎这种方式它甚至适用于playerCharacterData.characterName o.O所以现在它不是null。奇怪的。 所以我不明白为什么我的方式不起作用,但你的确如此。 –

1

随着Singleton模式,至极是一种设计模式,限制类的实例化一个对象,这样你只会让一个全局实例,并从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; 
    } 
} 

要求:MonoBehaviourExtended.cs(从GetOrAddComponent)

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“反模式”。

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); 
    } 
}