在Unity中从不同场景访问变量

问题描述 投票:1回答:2

我对Unity和C#相当陌生,遇到了一些麻烦。我正在设计一个2D游戏,它有多个关卡。每个关卡都包含一个LevelManager,它存储关卡是否已经完成。它们中也有DontDestroyOnLoad命令。我想访问我的游戏中的所有LevelManager gameObjects,然后将它们存储在关卡选择场景中。我想使用winlose bool来确定关卡是否已经完成,这样我就可以解锁下一个关卡。明确地说,我想要一种方法来访问我整个游戏中的所有LevelManagers,然后将它们作为一个数组存储在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");
        }

    }
}
unity3d
2个回答
0
投票

首先,DontDestroyOnLoad不应该这样做--它的主要用途是实现像unity-singleton(简单易用的模板). 这意味着,你将有一个levelManager实例,而不是每个关卡一个。

要用你的方式,你需要加载所有的场景加法,并得到所有的LevelManager实例,或者你可以逐一加载所有的场景,从当前的LevelManager中得到关卡名称和它的isPassed值。或者你可以逐一加载所有的场景,从当前的LevelManager中获取关卡名称和它的isPassed值。这可能需要一段时间,而且是一个错误的方法。

正确的方法是将场景间的数据存储在 可脚本对象 模型。它就像一个MonoBehaviour,但与场景无关--它只是放在项目的某个地方。

所以,在你的情况下,你可以创建一个叫LevelProgress的ScriptableObject。它将存储所有通过的关卡的列表。它将有一个公共方法LevelPassed(string levelName),作为参数你可以使用任何东西--关卡名称,build中的关卡索引,场景本身,等等。每次,当玩家通过任何一个关卡时,你的LevelManager(从它那里删除DontDestoryOnLoad),它有一个对LevelProgress可脚本对象的引用,将调用LevelProgress.LevelPasses(currentLevel)。然后,如果你需要知道,某个关卡是否通过了,你可以直接检查,LevelProgress.P PassedLevels列表是否包含这个关卡。

另外,你需要为这个可脚本对象实现会话间的持久化。我认为,最好的方法是使用 JSONUtility 将ScriptableObject转换为JSON字符串,然后写入到 播放器refs.


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会造成内存泄漏,有时会影响游戏性能。


0
投票

我似乎已经想通了。我创建了一个包含GameManagement脚本的gameObject,其中有DontDestroyOnLoad一行。我还添加了一个特定的标签。然后我在每个关卡中搜索该对象,并将我的值更新为该对象。GameManagement脚本有一个用于关卡完成和关卡解锁的Bools数组。每个关卡管理器都决定了该关卡是否胜利,并更新了该值。利用这个,我决定了什么关卡被解锁。不过我确实需要使用火王的觉醒命令。它可以确保游戏中没有其他副本的脚本。解决了我的问题。

© www.soinside.com 2019 - 2024. All rights reserved.