首页 > 解决方案 > Unity 在重播时将分数重置为 0

问题描述

问候!我有一个工作脚本,可以将我的游戏分数从一个级别传递到另一个级别。问题是我的重播级别脚本没有重置为 0,而是保留了重置之前的分数。关于如何在不干扰分数结转脚本的情况下让重播脚本将分数重置为 0 的建议将不胜感激

分数结转脚本

{
  
public static int scoreValue = 0;
Text score;

void Start()
{
    score = GetComponent<Text>();
    scoreValue = PlayerPrefs.GetInt("Player Score");
}

void Update()
{
    score.text = " " + scoreValue;
    PlayerPrefs.SetInt("Player Score", scoreValue);
}

}

重播关卡脚本

{

public static int scoreValue = 0;
Text score;

public void RestartLevel()
{
    SceneManager.LoadScene(SceneManager.GetActiveScene().name);
    score = GetComponent<Text>();
    score.text = " " + 0;
    
}


public void Awake()
{
    Time.timeScale = 1;
    
}

标签: c#unity3d

解决方案


好吧抱歉刚到家。评论中已经解决了一些问题。我看到的一个问题是PlayerPrefs.SetInt("Player Score", scoreValue);Update()函数中的不断使用。除非绝对需要,否则我不会使用任何类型的Update()方法,因为某些调用的方法在计算上会变得相当繁重。对于一个较小的项目,它可以忽略不计,但经过几个月的项目工作后,这样的事情可能会成为您项目的瓶颈。

除了没有这种方法的挑剔原因之外Update(),这很可能是数据没有重置的原因,因为它每帧都设置它。我做了一些重写来帮助你。我将使用一些人认为不好的单例模式,但我不同意。我发现它们在适当的情况下使用并且没有过度使用时非常有用。为了简要和一般地解释单例模式,它实际上是一个全局类的静态实例,因此所有其他脚本都可以通过访问它的实例来访问它。把它想象成一个只存在一次的类,其他所有脚本都可以使用它。

首先,创建一个名为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对象拖到脚本中以获取参考集。我要说明的另一点是,我正在订阅 SceneManager 委托事件,sceneLoaded当场景完成加载时会调用该委托事件。我正在使用它来初始设置 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);
    }
}

如果您有任何问题,请告诉我,因为这非常多。我自己对此进行了测试,因为它涉及更多,并且我让它在一个场景中本地工作,其中有一堆按钮只是调用函数。


推荐阅读