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



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

下面是关卡管理器脚本,它声明了关卡是否已赢得。

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

您可以使用 PlayerPrefs 创建一个ScoreManager脚本来存储每个关卡的分数,并将此脚本附加到第一个场景中的游戏对象。

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 一起使用会导致内存泄漏,有时会影响游戏性能。

首先,DontDestroyOnLoad 不应该这样做 - 这样做的主要用途是实现像 unity-singleton(易于使用的模板(这样的东西。这意味着,您将有一个 levelManager 实例,而不是每个级别一个。

要以自己的方式执行此操作,您需要累加加载所有场景,并获取所有 LevelManager 实例。或者你可以逐个加载所有场景,从当前关卡管理器获取关卡名称及其 isPass 值。这可能需要一段时间,而且这是一种错误的方式。

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

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

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

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

最新更新