好的,抱歉刚到家。 cmets 中已经解决了一些问题。我看到的一个问题是在Update() 函数中不断使用PlayerPrefs.SetInt("Player Score", scoreValue);。除非绝对需要,否则我不会使用任何类型的 Update() 方法,因为某些调用的方法在计算上会变得相当繁重。对于一个较小的项目,它可以忽略不计,但经过几个月的项目工作后,这样的事情可能会成为您项目的瓶颈。
除了在Update() 中没有这种方法的挑剔原因之外,这很可能是数据没有重置的原因,因为它每帧都设置它。我做了一些重写来帮助你。我将使用Singleton Pattern,有些人认为这很糟糕,但我不同意。我发现它们在适当的情况下使用并且没有过度使用时非常有用。为了简要和一般地解释单例模式,它实际上是一个全局类的静态实例,因此所有其他脚本都可以通过访问它的实例来访问它。把它想象成一个只存在一次的类,其他所有脚本都可以使用它。
首先,创建一个名为Singleton.cs 的新脚本并复制以下代码。我剥离了 Unity 实现,并评论说它不那么令人困惑。
// from the Unity wiki
// http://wiki.unity3d.com/index.php/Singleton
// base class of a singelton pattern
using UnityEngine;
/// <summary>
/// Inherit from this base class to create a singleton.
/// e.g. public class MyClassName : Singleton<MyClassName> {}
/// </summary>
public class Singleton<T> : MonoBehaviour where T : MonoBehaviour
{
// Check to see if we're about to be destroyed.
private static object m_Lock = new object();
private static T m_Instance;
/// <summary>
/// Determines if this instance exists
/// </summary>
/// <returns></returns>
public static bool HasInstance() { return m_Instance != null; }
/// <summary>
/// Access singleton instance through this propriety.
/// </summary>
public static T Instance
{
get
{
lock (m_Lock)
{
if (m_Instance == null)
{
// Search for existing instance.
m_Instance = (T)FindObjectOfType(typeof(T));
// Create new instance if one doesn't already exist.
if (m_Instance == null)
{
// Need to create a new GameObject to attach the singleton to.
var singletonObject = new GameObject();
m_Instance = singletonObject.AddComponent<T>();
singletonObject.name = typeof(T).ToString() + " (Singleton)";
// Make instance persistent.
DontDestroyOnLoad(singletonObject);
}
}
return m_Instance;
}
}
}
}
现在为分数carry over script。我改变了一点,成为一名得分经理。这意味着什么取决于您,但管理器的一般概念是它保存与其管理的任何内容相关的所有数据。在您的情况下,它将管理、保存、操作和显示分数。其他需要访问分数、更改分数等的脚本调用它。
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.SceneManagement;
/// <summary>
/// Scoremanager that handles all score related functionality
/// </summary>
public class ScoreManager : Singleton<ScoreManager>
{
[SerializeField] private Text score;
private int scoreValue = 0;
private void OnEnable()
{
SceneManager.sceneLoaded += NewSceneLoaded;
}
private void OnDisable()
{
SceneManager.sceneLoaded -= NewSceneLoaded;
}
/// <summary>
/// Listen for when a new scene is loaded - set our score to the current player value
/// </summary>
/// <param name="scene"></param>
/// <param name="mode"></param>
public void NewSceneLoaded(Scene scene, LoadSceneMode mode)
{
// again just reusing the same function for less code rewriting
AddScore(PlayerPrefs.GetInt("Player Score"));
}
/// <summary>
/// Adds any given amount to our score, saves it and displays it to the user
/// </summary>
/// <param name="amount"></param>
public void AddScore(int amount)
{
scoreValue += amount;
UpdateScoreUI();
PlayerPrefs.SetInt("Player Score", scoreValue);
}
public void ResetScore()
{
// nice trick to reset the score to 0 by negating the entire score
// re-uses all of the other code as well to update UI and save the score to Player Prefs
AddScore(-scoreValue);
}
/// <summary>
/// Updates the UI of our score
/// </summary>
private void UpdateScoreUI()
{
score.text = "Score: " + scoreValue;
}
}
如您所见,它继承自Singleton 类,这意味着只要对象存在于场景中,就可以从项目中的任何位置调用它。确保在每个场景中放置ScoreManager。另一个更改是我将Text 对象设为私有字段,并将Serialized 设为检查器中的对象。这只是意味着其他类无法访问它,但 Unity 会在检查器中显示它,因此您可以将 Text 对象拖到脚本中以获取参考集。我要说明的另一件事是,我订阅了 sceneLoaded 的 SceneManager 委托事件,该事件在场景加载完成时被调用。我正在使用它来初始设置 UI 并正确初始化您的分数。
接下来是一个示例脚本,它调用ScoreManager 来增加分数。它有一个 int 值,可以将分数增加我在检查器中设置的某个值。您可以将其附加到任何更改分数的对象上,或者您可以复制重要的一行。
使用 UnityEngine;
/// <summary>
/// Example object that increases our score
/// </summary>
public class IncreaseScore : MonoBehaviour
{
// the amount of score that this object gives when its IncScore is called
[SerializeField] private int PointsWorth = 0;
/// <summary>
/// Increaes our score after some event - this example I am using a button to increase it
/// whenever you click an enemy or object, etc. use the same function
/// </summary>
public void IncScore()
{
ScoreManager.Instance.AddScore(PointsWorth);
}
}
重申一下,改变当前分数的一行是这一行:ScoreManager.Instance.AddScore(PointsWorth);。将 PointsWorth 更改为您想要更改的任何值。
最后,我稍微修改了您的ReplayLevel,以确保在调用ResetLevel 函数时分数重置为0。
using UnityEngine;
using UnityEngine.SceneManagement;
public class ReplayLevel : MonoBehaviour
{
public void Awake()
{
Time.timeScale = 1;
}
public void RestartLevel()
{
// reset our score as we are resetting the level
PlayerPrefs.SetInt("Player Score", 0);
SceneManager.LoadScene(SceneManager.GetActiveScene().name);
}
public void NewLevel()
{
SceneManager.LoadScene(SceneManager.GetActiveScene().name);
}
}
如果您有任何问题,请告诉我,因为这非常多。我自己对此进行了测试,因为它涉及更多,并且我让它在一个场景中本地工作,其中有一堆按钮只是调用函数。