如何将得分值从一个场景传递到另一个场景?
我尝试过以下方法:
场景一:
void Start () {
score = 0;
updateScoreView ();
StartCoroutine (DelayLoadlevel(20));
}
public void updateScoreView(){
score_text.text = "The Score: "+ score;
}
public void AddNewScore(int NewscoreValue){
score = score + NewscoreValue;
updateScoreView ();
}
IEnumerator DelayLoadlevel(float seconds){
yield return new WaitForSeconds(10);
secondsLeft = seconds;
loadingStart = true;
do {
yield return new WaitForSeconds(1);
} while(--secondsLeft >0);
// here I should store my last score before move to level two
PlayerPrefs.SetInt ("player_score", score);
Application.LoadLevel (2);
}
场景二:
public Text score_text;
private int old_score;
// Use this for initialization
void Start () {
old_score = PlayerPrefs.GetInt ("player_score");
score_text.text = "new score" + old_score.ToString ();
}
但屏幕上没有显示,也没有错误。
这是传递数据的正确方法吗?
我正在使用Unity 5免费版,为Gear VR开发游戏(意味着游戏将在Android设备上运行)。
有什么建议吗?
答案 0 :(得分:28)
有 3 方法可以做到这一点,但解决方法取决于您希望在场景之间传递的数据类型。加载新场景时甚至在标记为static
时,组件/脚本和游戏对象都会被销毁。
<强> 1。使用static
关键字。
如果要传递给下一个场景的变量不是某个组件,请使用此方法,不是否继承自MonoBehaviour
并且不是GameObject,然后将变量设为{{1 }}。
内置原始数据类型,例如static
,int
,bool
,string
,float
。所有这些变量都可以成为double
变量。
可以标记为静态的内置基元数据类型的示例:
static
这些应该没有问题。
可以标记为静态的对象示例:
static int counter = 0;
static bool enableAudio = 0;
static float timer = 100;
然后
public class MyTestScriptNoMonoBehaviour
{
}
请注意,该类不会从MonoBehaviour
继承。这应该有用。
无法标记为静态的对象示例:
从Object
,Component
或GameObject
继承的任何内容都
1A 。任何继承自MonoBehaviour
static MyTestScriptNoMonoBehaviour testScriptNoMono;
void Start()
{
testScriptNoMono = new MyTestScriptNoMonoBehaviour();
}
然后
public class MyTestScript : MonoBehaviour
{
}
这将不有效,因为它继承自MonoBehaviour
。
1B 。所有GameObject
:
static MyTestScript testScript;
void Start()
{
testScript = gameObject.AddComponent<MyTestScript>();
}
这将不工作,因为它是GameObject
而GameObject
继承自Object
。
Unity即使使用static GameObject obj;
void Start()
{
obj = new GameObject("My Object");
}
关键字声明它们也会销毁其Object
。
有关解决方法,请参阅#2 。
2.使用DontDestroyOnLoad
功能。
如果要保留或传递到下一个场景的数据继承自Object
,Component
或GameObject
,则只需使用此项。这解决了 1A 和 1B 中描述的问题。
当场景卸载时,您可以使用它来使这个GameObject不被破坏:
static
您甚至可以将其与 1A 和 1B 中的void Awake()
{
DontDestroyOnLoad(transform.gameObject);
}
关键字解决问题一起使用:
static
然后
public class MyTestScript : MonoBehaviour
{
}
现在,当加载新场景时,static MyTestScript testScript;
void Awake()
{
DontDestroyOnLoad(transform.gameObject);
}
void Start()
{
testScript = gameObject.AddComponent<MyTestScript>();
}
变量将被保留。
3.保存到本地存储,然后在下一个场景中加载。
当这是游戏关闭并重新打开时必须保留的游戏数据时,应使用此方法。这样的例子是玩家高分,游戏设置如音乐量,对象位置,操纵杆简档数据等。
Thare是两种保存方式:
3A 。使用PlayerPrefs
API。
如果只保存少量变量,请使用。让我们说球员得分:
testScript
我们想要保存playerScore:
将分数保存在int playerScore = 80;
功能
OnDisable
将其加载到void OnDisable()
{
PlayerPrefs.SetInt("score", playerScore);
}
函数
OnEnable
3B 。将数据转换为json,xml或binaray形式,然后使用其中一个C#文件API(例如File.WriteAllBytes
和File.ReadAllBytes
进行保存,以保存和加载文件。
如果要保存许多变量,请使用此方法。
一般情况下,您需要创建一个不从MonoBehaviour
继承的类。您应该使用此类来保存游戏数据,以便可以轻松地序列化或反序列化。
要保存的数据示例:
void OnEnable()
{
playerScore = PlayerPrefs.GetInt("score");
}
抓住[Serializable]
public class PlayerInfo
{
public List<int> ID = new List<int>();
public List<int> Amounts = new List<int>();
public int life = 0;
public float highScore = 0;
}
类,它是File.WriteAllBytes
和File.ReadAllBytes
的包装,可以使this帖子更容易保存数据。
创建新实例:
DataSaver
将数据从PlayerInfo保存到名为“players”的文件:
PlayerInfo saveData = new PlayerInfo();
saveData.life = 99;
saveData.highScore = 40;
从名为“players”的文件中加载数据:
DataSaver.saveData(saveData, "players");
答案 1 :(得分:23)
还有另一种方式:
ScriptableObject
ScriptableObject
基本上是数据容器,但也可以实现自己的逻辑。它们仅在Assets
中像预制件一样“存在”。它们不能不能用于永久存储,但是可以在一个会话期间存储数据,因此可以用于在场景之间共享数据。 ..和-我也经常需要的-在场景和AnimatorController
之间!
首先,您需要一个类似于MonoBehaviour
的脚本。 ScriptableObject
的一个简单示例可能看起来像
// fileName is the default name when creating a new Instance
// menuName is where to find it in the context menu of Create
[CreateAssetMenu(fileName = "Data", menuName = "Examples/ExamoleScriptableObject")]
public class ExampleScriptableObject : ScriptableObject
{
public string someStringValue = "";
public CustomDataClass someCustomData = null;
// Could also implement some methods to set/read data,
// do stuff with the data like parsing between types, fileIO etc
// Especially ScriptableObjects also implement OnEnable and Awake
// so you could still fill them with permanent data via FileIO at the beginning of your app and store the data via FileIO in OnDestroy !!
}
// If you want the data to be stored permanently in the editor
// and e.g. set it via the Inspector
// your types need to be Serializable!
//
// I intentionally used a non-serializable class here to show that also
// non Serializable types can be passed between scenes
public class CustomDataClass
{
public int example;
public Vector3 custom;
public Dictionary<int, byte[]> data;
}
您可以通过脚本创建ScriptableObject
的实例
var scriptableObject = ScriptableObject.CreateInstance<ExampleScriptableObject>();
或使用上面的示例中的[CreateAssetMenu]
使事情变得更容易。
由于此创建的ScriptabeObject
实例位于Assets
中,因此它没有绑定到场景,因此可以在任何地方引用!
当您要在两个场景之间共享数据或例如场景和AnimatorController
所要做的就是在两个实例中都引用此ScriptableObject
实例。
我经常使用一个填充数据的组件,例如
public class ExampleWriter : MonoBehaviour
{
// Here you drag in the ScriptableObject instance via the Inspector in Unity
[SerializeField] private ExampleScriptableObject example;
public void StoreData(string someString, int someInt, Vector3 someVector, List<byte[]> someDatas)
{
example.someStringValue = someString;
example.someCustomData = new CustomDataClass
{
example = someInt;
custom = someVector;
data = new Dictionary<int, byte[]>();
};
for(var i = 0; i < someDatas.Count; i++)
{
example.someCustomData.data.Add(i, someDatas[i]);
}
}
}
因此,在将所需的数据写入并存储到此ExampleScriptableObject
实例中之后,任何场景或AnimatorController
或其他ScriptableObject
中的每个其他类都可以在相同的位置上读取此数据方式:
public class ExmpleConsumer : MonoBehaviour
{
// Here you drag in the same ScriptableObject instance via the Inspector in Unity
[SerializeField] private ExampleScriptableObject example;
public void ExampleLog()
{
Debug.Log($"string: {example.someString}", this);
Debug.Log($"int: {example.someCustomData.example}", this);
Debug.Log($"vector: {example.someCustomData.custom}", this);
Debug.Log($"data: There are {example.someCustomData.data.Count} entries in data.", this);
}
}
如前所述,ScriptableObject
本身的更改仅在Unity编辑器中真正持久存在。
在构建中,它们仅在同一会话中保持不变。
因此,我经常需要将会话持久性与一些FileIO结合使用,以便在会话开始时(或在需要时)从硬盘驱动器加载和反序列化一次值,并在会话结束时将它们序列化并存储到文件中({{1 }})或需要的时候。
答案 2 :(得分:8)
除了playerPrefs之外,另一种脏方法是在级别加载期间通过调用DontDestroyOnLoad来保留对象。
DontDestroyOnLoad (transform.gameObject);
附加到游戏对象的任何脚本都将存活,脚本中的变量也将存活。 DontDestroyOnLoad函数通常用于保存整个GameObject,包括附加到它的组件,以及它在层次结构中的任何子对象。
您可以创建一个空GameObject,并只放置包含您想要保留的变量的脚本。
答案 3 :(得分:2)
我使用一种称为无状态场景的功能方法。
using UnityEngine;
public class MySceneBehaviour: MonoBehaviour {
private static MySceneParams loadSceneRegister = null;
public MySceneParams sceneParams;
public static void loadMyScene(MySceneParams sceneParams, System.Action<MySceneOutcome> callback) {
MySceneBehaviour.loadSceneRegister = sceneParams;
sceneParams.callback = callback;
UnityEngine.SceneManagement.SceneManager.LoadScene("MyScene");
}
public void Awake() {
if (loadSceneRegister != null) sceneParams = loadSceneRegister;
loadSceneRegister = null; // the register has served its purpose, clear the state
}
public void endScene (MySceneOutcome outcome) {
if (sceneParams.callback != null) sceneParams.callback(outcome);
sceneParams.callback = null; // Protect against double calling;
}
}
[System.Serializable]
public class MySceneParams {
public System.Action<MySceneOutcome> callback;
// + inputs of the scene
}
public class MySceneOutcome {
// + outputs of the scene
}
您可以将全局状态保持在调用方的范围内,因此可以最小化场景的输入和输出状态(使测试变得容易)。要使用它,您可以使用匿名函数:-
MyBigGameServices services ...
MyBigGameState bigState ...
Splash.loadScene(bigState.player.name, () => {
FirstLevel.loadScene(bigState.player, (firstLevelResult) => {
// do something else
services.savePlayer(firstLevelResult);
})
)}
更多信息,位于https://corepox.net/devlog/unity-pattern:-stateless-scenes
答案 4 :(得分:1)
有各种各样的方法,但是假设您只需要传递一些基本数据,则可以创建GameController的singelton实例并使用该类来存储数据。
,当然,DontDestroyOnLoad是必须的!
public class GameControl : MonoBehaviour
{
//Static reference
public static GameControl control;
//Data to persist
public float health;
public float experience;
void Awake()
{
//Let the gameobject persist over the scenes
DontDestroyOnLoad(gameObject);
//Check if the control instance is null
if (control == null)
{
//This instance becomes the single instance available
control = this;
}
//Otherwise check if the control instance is not this one
else if (control != this)
{
//In case there is a different instance destroy this one.
Destroy(gameObject);
}
}
这里是full tutorial,还有其他示例。