Jak przekazywać dane między scenami w Unity
Jak mogę przekazać wartość wyniku z jednej sceny do drugiej?
Próbowałem:
Scena pierwsza:
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);
}
Scena druga:
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 ();
}
Ale nic nie wyświetla się na ekranie i nie ma błędu.
Czy to prawidłowy sposób przekazywania danych ?
Używam Unity 5 free edition, rozwijać grę dla Gear VR (co oznacza, że gra będzie działać na urządzeniach z Androidem).
Jakieś sugestie?5 answers
Poza playerPrefs innym brudnym sposobem jest zachowanie obiektu podczas ładowania poziomu przez wywołanie na nim DontDestroyOnLoad.
DontDestroyOnLoad (transform.gameObject);
Każdy skrypt dołączony do obiektu gry przetrwa, podobnie jak zmienne w skrypcie. Funkcja Dontdestroyonload jest zwykle używana do zachowania całego obiektu GameObject, łącznie z dołączonymi do niego komponentami i wszystkimi obiektami potomnymi, które posiada w hierarchii.
Można utworzyć pusty obiekt GameObject i umieścić tylko skrypt zawierający zmienne, które chcesz zachować na nim.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-08-31 09:57:02
Są 3 sposoby na to, ale rozwiązanie zależy od rodzaju danych, które chcesz przekazać między scenami. Komponenty / skrypty i Obiekty GameObjects są niszczone podczas ładowania nowej sceny, a nawet gdy są oznaczone jako static
.
1. Użyj słowa kluczowego static
.
Użyj tej metody, jeśli zmienna przekazywana do następnej sceny nie jest składową, nie Nie dziedziczy z MonoBehaviour
and is not a GameObject then make the variable to be static
.
Wbudowane prymitywne typy danych, takie jak int
, bool
, string
, float
, double
. Wszystkie te zmienne można utworzyć zmienną static
.
Przykład wbudowanych prymitywnych typów danych, które mogą być oznaczone jako statyczne:
static int counter = 0;
static bool enableAudio = 0;
static float timer = 100;
Powinny działać bez problemów.
Przykład obiektów, które mogą być oznaczone jako statyczne:
public class MyTestScriptNoMonoBehaviour
{
}
Then
static MyTestScriptNoMonoBehaviour testScriptNoMono;
void Start()
{
testScriptNoMono = new MyTestScriptNoMonoBehaviour();
}
Zauważ, że klasa nie dziedziczy z MonoBehaviour
. To powinno zadziałać.
Przykład obiektów, które nie mogą być oznaczone jako statyczne:
Wszystko, co dziedziczy z Object
, Component
lub GameObject
czy Nie zadziała.
1A .Wszystko, co dziedziczy po MonoBehaviour
public class MyTestScript : MonoBehaviour
{
}
Then
static MyTestScript testScript;
void Start()
{
testScript = gameObject.AddComponent<MyTestScript>();
}
To będzie a nie działać, ponieważ dziedziczy z MonoBehaviour
.
1B .Wszystkie GameObject
:
static GameObject obj;
void Start()
{
obj = new GameObject("My Object");
}
To będzie Nie też działać, ponieważ jest to GameObject
oraz GameObject
dziedziczenie z Object
.
Jedność zawsze zniszczy jej Object
nawet jeśli są zadeklarowane za pomocą słowa kluczowego static
.
Zobacz #2 na obejście.
2.Użyj DontDestroyOnLoad
function .
Musisz użyć tego tylko wtedy, gdy dane do przechowywania lub przekazywania do Następna scena dziedziczy z Object
, Component
lub jest GameObject
. Rozwiązuje to problem opisany w 1A i 1B .
void Awake()
{
DontDestroyOnLoad(transform.gameObject);
}
Możesz go nawet użyć za pomocą static
słowa kluczowego Rozwiąż problem z 1A i 1B :
public class MyTestScript : MonoBehaviour
{
}
Then
static MyTestScript testScript;
void Awake()
{
DontDestroyOnLoad(transform.gameObject);
}
void Start()
{
testScript = gameObject.AddComponent<MyTestScript>();
}
Zmienna testScript
zostanie zachowana, gdy nowa scena mnóstwo.
3.Zapisz w pamięci lokalnej, a następnie załaduj podczas następnej sceny.
Ta metoda powinna być używana, gdy są to DANE GRY, które muszą być zachowane, gdy gra jest zamknięta i ponownie otwarta. Przykładem tego jest wysoki wynik gracza, ustawienia gry, takie jak głośność muzyki, lokalizacje obiektów, dane profilu joysticka i tak dalej.
Thare są dwa sposoby, aby to zapisać:
3A .Użyj PlayerPrefs
API.
Użyj, jeśli masz tylko kilka zmienne do zapisania. Powiedzmy wynik gracza:
int playerScore = 80;
I chcemy zapisać wynik gracza:
Zapisz wynik w funkcji OnDisable
void OnDisable()
{
PlayerPrefs.SetInt("score", playerScore);
}
Załaduj go w funkcji OnEnable
void OnEnable()
{
playerScore = PlayerPrefs.GetInt("score");
}
3B .Serializuj dane do postaci json, xml lub binaray, a następnie zapisz za pomocą jednego z API plików C#, takich jak File.WriteAllBytes
oraz File.ReadAllBytes
aby zapisać i załadować pliki.
Użyj tej metody, jeśli istnieje wiele zmiennych do zapisania.
Generale, potrzebujesz aby utworzyć klasę, która nie dziedziczy z MonoBehaviour
. Ta klasa powinna być używana do przechowywania danych gry, aby można ją było łatwo serializować lub de-serializować.
Przykład danych do zapisania:
[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;
}
Grab the DataSaver
class which is a wrapper over File.WriteAllBytes
oraz File.ReadAllBytes
to ułatwia zapisywanie danych z tego posta .
Utwórz nową instancję:
PlayerInfo saveData = new PlayerInfo();
saveData.life = 99;
saveData.highScore = 40;
Zapisz dane z PlayerInfo do pliku o nazwie "gracze":
DataSaver.saveData(saveData, "players");
załaduj dane z pliku o nazwie "Odtwarzacze":
PlayerInfo loadedData = DataSaver.loadData<PlayerInfo>("players");
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-06-14 10:52:41
Jest inny sposób:
ScriptableObject
ScriptableObject
s są w zasadzie kontenerami danych, ale mogą również implementować własną logikę. "Żyją" tylko w Assets
Jak prefabrykaty. Mogą Nie być używane do przechowywania danych na stałe, ale przechowują dane podczas jednej sesji, więc mogą być używane do dzielenia danych i odniesień między scenami ... i-coś, czego często potrzebowałem-między scenami i AnimatorController
!
Skrypt
Pierwszy potrzebujesz skryptu podobnego do MonoBehaviour
s. prosty przykład ScriptableObject
może wyglądać jak
// 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;
public Transform someTransformReference = 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;
}
Tworzenie Instancji
Można tworzyć instancje ScriptableObject
za pomocą skryptu
var scriptableObject = ScriptableObject.CreateInstance<ExampleScriptableObject>();
Lub aby ułatwić korzystanie z [CreateAssetMenu]
Jak pokazano w powyższym przykładzie.
Ponieważ stworzona instancja ScriptabeObject
żyje w Assets
, nie jest powiązana ze sceną i dlatego może być odwołana wszędzie!
To, gdy chcesz udostępnić dane między dwoma scenami lub również np. scena i AnimatorController
wszystko, co musisz zrobić, to odwołać się do tej instancji ScriptableObject
w obu.
Wypełnij Dane
Często używam np. jednego komponentu do wypełniania danych jak
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]);
}
example.someTransformReference = transform;
}
}
Zużyć Dane
Więc po zapisaniu i zapisaniu wymaganych danych w tej instancji ExampleScriptableObject
każda inna klasa w dowolnej scenie lub AnimatorController
lub również inne ScriptableObject
może odczytać te dane w ten sam sposób:
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);
Debug.Log($"The data writer {example.someTransformReference.name} is at position {example.someTransformReference.position}", this);
}
}
Trwałość
Jak powiedział zmiany w samym ScriptableObject
są tylko w edytor Unity bardzo wytrwały.
W kompilacji są trwałe tylko podczas tej samej sesji.
Dlatego też często łączę trwałość sesji z jakimś plikiem do ładowania i deserializacji wartości po rozpoczęciu sesji (lub w razie potrzeby) z dysku twardego i serializuję je i zapisuję do pliku po zakończeniu sesji (OnApplicationQuit
) lub w razie potrzeby.
(to nie będzie działać z referencjami oczywiście.)
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-12-27 17:23:38
Używam podejścia funkcjonalnego, które nazywam scenami Bezpaństwowymi.
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
}
Możesz zachować stan Globalny w zakresie wywołującego , dzięki czemu Stany wejść i wyjść sceny mogą być zminimalizowane (ułatwia testowanie). Aby go użyć, możesz użyć funkcji anonimowych: -
MyBigGameServices services ...
MyBigGameState bigState ...
Splash.loadScene(bigState.player.name, () => {
FirstLevel.loadScene(bigState.player, (firstLevelResult) => {
// do something else
services.savePlayer(firstLevelResult);
})
)}
Więcej informacji na https://corepox.net/devlog/unity-pattern:-stateless-scenes
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-01-23 22:25:55
Istnieją różne sposoby, ale zakładając, że musisz przekazać tylko niektóre podstawowe dane, możesz utworzyć pojedynczą instancję kontrolera GameController i użyć tej klasy do przechowywania danych.
I oczywiście DontDestroyOnLoad jest obowiązkowy!
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);
}
}
Oto pełny samouczek z innym przykładem.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-06-30 18:35:52