从Unity中的其他场景访问变量

时间:2020-06-03 00:33:04

标签: unity3d

我对Unity和C#相当陌生,遇到了一些麻烦。我正在设计一个具有多个级别的2D游戏。每个级别都包含一个LevelManager,用于存储该级别是否已完成。他们也有DontDestroyOnLoad命令。我想访问游戏中的所有LevelManager gameObjects,然后将它们存储在关卡选择场景中。我想用赢/输来确定关卡是否已经完成,以便解锁下一个关卡。明确地说,我想要一种方法来访问我的整个游戏中的所有LevelManager,然后将它们作为数组存储在GameManager脚本中。我该怎么办?

下面是LevelManager脚本,它声明是否赢得了关卡。

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

public class SceneController : MonoBehaviour
{
    private GameObject[] StartHouseCount;
    private GameObject[] StartDragonCount;
    private GameObject[] LiveDragonCount;
    private GameObject[] FinishedHouseCount;

    public int NumOfHouses;
    public int NumOfFinishedHouse;
    public int NumOfDragons;
    public int LiveNumOfDragons;
    public GameObject[] Players;
    public GameObject CurrentPlayer;

    [Header("Player")]
    public float RefuelRate;
    public float RepairRate;

    public GameObject canvas;
    public bool GameIsPaused = false;

    private GameObject MainPlayer;
    public bool Win = false;
    public int Level;
    // Start is called before the first frame update
    void Start()
    {
        CurrentPlayer = Players[0];
        StartHouseCount = GameObject.FindGameObjectsWithTag("House");
        StartDragonCount = GameObject.FindGameObjectsWithTag("Dragon");
        NumOfHouses = StartHouseCount.Length;
        NumOfDragons = StartDragonCount.Length;
        MainPlayer = Players[0];
    }

    // Update is called once per frame
    void Update()
    {
        GameIsPaused = canvas.GetComponent<PauseMenu>().GameIsPaused;

        LiveDragonCount = GameObject.FindGameObjectsWithTag("Dragon");
        LiveNumOfDragons = LiveDragonCount.Length;

        FinishedHouseCount = GameObject.FindGameObjectsWithTag("ThankYou");
        NumOfFinishedHouse = FinishedHouseCount.Length;

        if (Input.GetKeyDown(KeyCode.Alpha1))
        {
            CurrentPlayer = Players[0];
        }
        if (Input.GetKeyDown(KeyCode.Alpha2))
        {
            CurrentPlayer = Players[1];
        }


        if (NumOfFinishedHouse == NumOfHouses)
        {
            SceneManager.LoadScene("WinScene");
        }

        if (MainPlayer == null)
        {
            SceneManager.LoadScene("LoseScene");
        }
        if (MainPlayer.GetComponent<BasicHelicopterController>().CurrentFuel <= 0 || MainPlayer.GetComponent<BasicHelicopterController>().CurrentHealth <= 0)
        {
            SceneManager.LoadScene("LoseScene");
        }

    }
}

3 个答案:

答案 0 :(得分:0)

首先,DontDestroyOnLoad不应该这样做-主要用途是实现诸如unity-singleton(easy-to-use template)之类的事情。这意味着,您将只有一个levelManager实例,而不是每个级别一个。

要以您的方式进行操作,您需要添加所有场景,并获取所有LevelManager实例。或者您可以一张一张地加载所有场景,从当前LevelManager获取关卡名称及其isPassed值。这可能要花一些时间,而且方法错误。

正确的方法是将场景间数据存储在ScriptableObject模型中。就像MonoBehaviour,但与场景无关-它只是放置在项目中的某个地方。

因此,在您的情况下,您可以创建称为LevelProgress的ScriptableObject。它将存储所有通过级别的列表。它将具有公共方法LevelPassed(string levelName),您可以使用任何参数作为参数-级别名称,构建中的级别索引,场景本身等等。每当玩家通过任何关卡时,引用了LevelProgress可编写脚本对象的LevelManager(从其中删除DontDestoryOnLoad)都会调用LevelProgress.LevelPasses(currentLevel)。然后,如果您需要知道是否通过某个级别,则可以检查是否是LevelProgress。PassedLevels列表包含该级别。

此外,您需要为此可编写脚本的对象实现会话之间的持久性。 IMO,最好的方法是使用JSONUtility将ScriptableObject转换为JSON字符串,然后将其写入PlayerPrefs

答案 1 :(得分:0)

您可以创建一个ScoreManager脚本来使用PlayerPrefs存储每个级别的分数,并将此脚本附加到第一个场景中的GameObject。

public class ScoreManager: MonoBehavior {

    public static ScoreManager Instance;
    const string LEVEL_KEY = "SCORE_";
    const string MAX_LEVEL_KEY = "MAX_LEVEL";

    public static int MAX_LEVEL {
        get { return PlayerPrefs.GetInt(MAX_LEVEL_KEY, 1); }
        set {
            if(value > MAX_LEVEL) {
                PlayerPrefs.SetInt(MAX_LEVEL_KEY, value);
            }
        }
    }

    void Awake() {
        if(Instance != null) {
            Destroy(this.gameObject);
            return;
        }
        Instance = this;
        DontDestroyOnLoad(this);
    }

    public void SaveScore(int level, float score) {
        PlayerPrefs.SetFloat(LEVEL_KEY + level, score);
        MAX_LEVEL = level;
    }

    public float GetScore(int level) {
        return PlayerPrefs.GetFloat(LEVEL_KEY + level, 0);
    }

    public bool IsLevelUnlocked(int level) {
        // You can write your own level-unlock logic here.
        return level <= MAX_LEVEL;
    }
    ...
}

然后,您可以从其他脚本中调用其功能和属性。

    ...
    public void GameOver() {
        ScoreManager.Instance.SaveScore(level, score);
    }
    ...

将所有LevelManger脚本与DontDestroyOnLoad一起使用会导致内存泄漏,有时还会影响游戏性能。

答案 2 :(得分:0)

我似乎已经知道了。我创建了一个包含GameManagement脚本的gameObject,该脚本具有DontDestroyOnLoad行。我还添加了一个特定标签。然后,我在每个级别中搜索该对象并将其值更新为该对象。 GameManagement脚本具有一系列用于完成级别和解锁级别的布尔值。每个关卡经理决定是否赢得关卡并对其进行更新。使用该命令,我确定了解锁的级别。我确实需要使用Fire King的Awake Command。它确保游戏中没有其他脚本副本。解决我的问题。