Hermetes Geschrieben 3. April 2019 Melden Share Geschrieben 3. April 2019 Ich mache meine erste Schritte im Thema Save überhaupt. Habe Variablen in einer "Serializable Class <SaveTheme>. Diese <SaveTheme> Klasse wird bei jedem Click auf einem Button erzeugt. Und landet in ein er Liste : saveThemeList. Bei Ausführen von Save() bekomme ich diese Exception. NullReferenceException: Object reference not set to an instance of an object SaveManager.Save () (.../SaveManager.cs:154) Zeile 154: PlayerPrefs.SetString("theme_name" +i , saveTheme.themeName); public void Save() { PlayerPrefs.SetInt("Themes", saveThemeList.Count); for (int i = 0; i < saveThemeList.Count; i++) { PlayerPrefs.SetString("theme_name" +i , saveTheme[i].themeName); PlayerPrefs.SetInt( "theme_index" +i, saveTheme[i].themeIndex); } PlayerPrefs.Save(); Debug.Log("Saved"); } public void SaveThemeInfo(ThemeBox box) { SaveTheme theme = new SaveTheme(); theme.themeIndex = box.themeID; theme.themeName = box.themeName; theme.themeColor = box.themeColor; theme.themeHolder = themeBoxHolder; theme.themePrefab = themeboxPrefab; saveThemeList.Add(theme); } public class SaveManager : MonoBehaviour { public static SaveManager instance; public int ThemesCount; public GameObject themeboxPrefab; public Transform themeBoxHolder; private SaveTheme[] saveTheme; public List<SaveTheme> saveThemeList = new List<SaveTheme>(); Link zu diesem Kommentar Auf anderen Seiten teilen More sharing options...
Sascha Geschrieben 3. April 2019 Melden Share Geschrieben 3. April 2019 Du hast Elemente in saveThemeList, greifst aber auf saveTheme zu. Link zu diesem Kommentar Auf anderen Seiten teilen More sharing options...
Hermetes Geschrieben 4. April 2019 Autor Melden Share Geschrieben 4. April 2019 Ich kann nicht mal sagen das es ein Versehen war, habe das tatsächlich auch als Liste gesehen ^^. Nachdem Load habe ich keine Werte in den Variablen. Wo steckt denn da der Wurm? for (int i = 0; i < ThemesCount; i++) { //saveThemeList[i].themeName = PlayerPrefs.GetString("theme_name"); saveThemeList[i].themeIndex = PlayerPrefs.GetInt("theme_index"); saveThemeList[i].themeName = PlayerPrefs.GetString("theme_name"); } Link zu diesem Kommentar Auf anderen Seiten teilen More sharing options...
Mr 3d Geschrieben 4. April 2019 Melden Share Geschrieben 4. April 2019 Du speicherst die werte unter "theme_name"+i lädst dann aber nur "theme_name" /edit Und 'Themes Count' war oben im screenshot 0, wenn du das noch nicht geändert hast.. Link zu diesem Kommentar Auf anderen Seiten teilen More sharing options...
Hermetes Geschrieben 4. April 2019 Autor Melden Share Geschrieben 4. April 2019 Ich weiss nicht wo ich das + i beim laden unterbringen soll. Oft klappt das ja zufällig bei mir ^^. ThemesCount habe ich in die Start gelegt. Das funktioniert. Link zu diesem Kommentar Auf anderen Seiten teilen More sharing options...
Sascha Geschrieben 4. April 2019 Melden Share Geschrieben 4. April 2019 Genau wie beim Speichern. Du speichert das erste Element als ""theme_name0", also musst du auch "theme_name0" laden. saveThemeList[i].themeName = PlayerPrefs.GetString("theme_name" + i); Link zu diesem Kommentar Auf anderen Seiten teilen More sharing options...
Hermetes Geschrieben 4. April 2019 Autor Melden Share Geschrieben 4. April 2019 Das hatte ich auch so gemacht Sascha.Jetzt habe ich allerdings keine Einträge wenn ich Load ausführe. Ich kann ja keinen List.Count speichern darum habe ich das über die int ThemeCounts gemacht. Aber ich sehe hier gerade was komplett nicht. ThemesCount = PlayerPrefs.GetInt("Themes"); for (int i = 0; i < ThemesCount; i++) { saveThemeList[i].themeName = PlayerPrefs.GetString("theme_name" + i); saveThemeList[i].themeIndex = PlayerPrefs.GetInt("theme_index" + i); } Debug.Log("Loaded"); } Link zu diesem Kommentar Auf anderen Seiten teilen More sharing options...
Hermetes Geschrieben 4. April 2019 Autor Melden Share Geschrieben 4. April 2019 public void Load() { ThemesCount = PlayerPrefs.GetInt("Themes"); for (int i = 0; i < ThemesCount; i++) { saveThemeList[i].themeName = PlayerPrefs.GetString("theme_name" + i); saveThemeList[i].themeIndex = PlayerPrefs.GetInt("theme_index" + i); } Debug.Log("Loaded"); } Link zu diesem Kommentar Auf anderen Seiten teilen More sharing options...
Sascha Geschrieben 4. April 2019 Melden Share Geschrieben 4. April 2019 Ich merke gerade, dass du Sachen über Index in eine Collection packst, die eventuell gar nicht so groß ist. Wenn du in ein Array laden würdest, müsstest du das hier machen: ThemesCount = PlayerPrefs.GetInt("Themes"); saveTheme = new SaveTheme[ThemesCount]; Da du aber deine Daten in eine Liste packst, solltest du nicht über Index hinzufügen, sondern einfach mit Add. Statt saveThemeList[i].themeName = PlayerPrefs.GetString("theme_name" + i); saveThemeList[i].themeIndex = PlayerPrefs.GetInt("theme_index" + i); also var themeName = PlayerPrefs.GetString("theme_name" + i); var themeIndex = PlayerPrefs.GetInt("theme_index" + i); saveThemeList.Add(new SaveTheme(themeName, themeIndex)); Diese Schreibweise benötigt natürlich einen Konstruktor im SaveTheme-Typ. Link zu diesem Kommentar Auf anderen Seiten teilen More sharing options...
Hermetes Geschrieben 4. April 2019 Autor Melden Share Geschrieben 4. April 2019 Hab´s leider nicht verstanden. Wüsste jetzt nicht wie der Konstruktor aussehen sollte. Also die Collection wird schon relativ groß. Geplant sind 11 Variablen + noch eine Liste (von der ich noch nicht weiss wie realisieren) Link zu diesem Kommentar Auf anderen Seiten teilen More sharing options...
Sascha Geschrieben 4. April 2019 Melden Share Geschrieben 4. April 2019 Ein Konstruktor ist eine Methode ohne Rückgabetyp in der Signatur, die genauso heißt wie der Typ selbst: public struct Person { public string name; public int age; public Person(string name, int age) { this.name = name; this.age = age; } } Wenn du dann eine neue Person erstellst, kannst du das über diesen Konstruktor tun: new Person("Klaus", 42) Wie jede andere Methode auch kann der Konstruktor kann beliebige Dinge tun, er ist nicht darauf beschränkt, Feldern Parameterwerte zuzuweisen. Bei structs ist allerdings Regel, dass ein Konstruktor jedes Feld initialisieren muss. Es gibt halt noch die andere Schreibweise, mit der du Werte von structs oder Objekten direkt initialisierst: new Person { name = "Klaus", age = 42 } Bemerke die geschweiften anstatt der normalen Klammern. Ganz alternativ kannst du natürlich auch alles in einzelne Zeilen schreiben, ob das so hübsch ist, sei jedem selbst überlassen. var person = new Person(); person.name = "Klaus"; person.age = 42; Am Ende hast du jedenfalls immer eine Person, die du dann mit Add in die Liste hinzufügen kannst. Link zu diesem Kommentar Auf anderen Seiten teilen More sharing options...
Hermetes Geschrieben 4. April 2019 Autor Melden Share Geschrieben 4. April 2019 Und ist das für meine Collections dann eine (stabelbare) struct oder kann ich das frei wählen ? Und so ein Konstruktor ist doch eigentlich immer in der selben Klasse oder? (Weil die Variablen sich ja da befinden ,oder?) Habe das jetzt mal ohne struct gemacht......wann ist ein struct ein struct? using UnityEngine; using System; [Serializable] public class SaveTheme { public int themeIndex; public string themeName; public Color themeColor; public Transform themeHolder; public GameObject themePrefab; public SaveTheme (int index, string name) { this.themeIndex = index; this.themeName = name; } } using System.Collections; using System.Collections.Generic; using UnityEngine; public class SaveManager : MonoBehaviour { public static SaveManager instance; public int ThemesCount; public GameObject themeboxPrefab; public Transform themeBoxHolder; private SaveTheme[] saveTheme; public List<SaveTheme> saveThemeList = new List<SaveTheme>(); //public List<SaveTheme> ThemeList = new SaveTheme(); void Awake() { instance = this; //themeboxPrefab = (GameObject)(Resources.Load("ThemeBox")); themeBoxHolder = GameObject.FindGameObjectWithTag("ThemeHolder").transform; } void Start() { ThemesCount = PlayerPrefs.GetInt("Themes"); } public void SaveThemeInfo(ThemeBox box) { SaveTheme theme = new SaveTheme(box.themeID, box.themeName); theme.themeIndex = box.themeID; theme.themeName = box.themeName; theme.themeColor = box.themeColor; theme.themeHolder = themeBoxHolder; theme.themePrefab = themeboxPrefab; saveThemeList.Add(theme); } public void Save() { PlayerPrefs.SetInt("Themes", saveThemeList.Count); for (int i = 0; i < saveThemeList.Count; i++) { PlayerPrefs.SetString("theme_name" +i , saveThemeList[i].themeName); PlayerPrefs.SetInt( "theme_index" +i, saveThemeList[i].themeIndex); } PlayerPrefs.Save(); Debug.Log("Saved"); } public void Load() { ThemesCount = PlayerPrefs.GetInt("Themes"); for (int i = 0; i < ThemesCount; i++) { saveThemeList[i].themeName = PlayerPrefs.GetString("theme_name" + i); saveThemeList[i].themeIndex = PlayerPrefs.GetInt("theme_index" + i); } Debug.Log("Loaded"); } public void DeleteAllThemes() { //savesT_Title.Clear(); //savesT_ID.Clear(); //savesThemeBox.Clear(); // ThemesCount = PlayerPrefs.GetInt("themeCount"); PlayerPrefs.DeleteAll(); } } Link zu diesem Kommentar Auf anderen Seiten teilen More sharing options...
Sascha Geschrieben 4. April 2019 Melden Share Geschrieben 4. April 2019 vor 57 Minuten schrieb Hermetes: Und ist das für meine Collections dann eine (stabelbare) struct oder kann ich das frei wählen Die Frage verstehe ich nicht so ganz, aber ich nehme an, die Antwort "du kannst alles in deine Liste reinstecken was dich glücklich macht" wird schon passen vor 57 Minuten schrieb Hermetes: Und so ein Konstruktor ist doch eigentlich immer in der selben Klasse oder? Richtig. vor 58 Minuten schrieb Hermetes: Habe das jetzt mal ohne struct gemacht... Kannste erstmal machen. vor 58 Minuten schrieb Hermetes: wann ist ein struct ein struct? Ein struct hast du, wenn du statt "class" "struct" schreibst. Der Unterschied ist die fehlende Referenzsemantik. Bei Objekten einer Klasse redest du von Dingen mit einer Identität (z.B. deinen Vater). Zwei Objekte können gleich sein (z.B. sein Zwilling) und trotzdem nicht dasselbe. Und du redest von deinem Vater, aber deine Mutter von ihrem Mann - zwei reden mit unterschiedlichen Bezeichnungen von derselben Person. structs sind dagegen für Werte. Eine 5 ist immer eine 5, und wenn zwei Leute von einer 5 reden, reden sie von demselben Wert. Einen Unterschied zwischen "das gleiche" und "dasselbe" bei Werten. Wenn du deine 5 erhöhst hast du keine 5 mehr, sondern eine 6. Referenzsemantik spiegelt das wieder. Hast du eine Klasse "Auto", sieht das so aus: var meinAuto = new Auto(); var dasAuto = meinAuto; meinAuto.farbe = blau; Debug.Log(dasAuto.farbe); // blau In diesem Beispiel gibt es exakt ein Auto-Objekt, und zwei Variablen, die dieses Objekt referenzieren. Hast du allerdings ein Struct, wie z.B. Vector3: var meinePosition = new Vector3(1, 2, 3); var diePosition = meinePosition; diePosition.x = 3; Debug.Log(meinePosition); // (1, 2, 3) Hier wird bei der Zuweisung in der zweiten Zeile keine Referenz kopiert, sondern der Wert selbst. Deshalb steht in den beiden Variablen zwar derselbe Wert, aber es gibt keine Verbindung, wie ein gemeinsam referenziertes Objekt. Wenn du dann den Wert der einen Variable änderst, dann hast du eben einen neuen Wert, und änderst nicht den alten. So wie 5+1 nicht "erhöhte 5" ergibt, sondern eben 6. Link zu diesem Kommentar Auf anderen Seiten teilen More sharing options...
Hermetes Geschrieben 5. April 2019 Autor Melden Share Geschrieben 5. April 2019 Aber ich habe den Grund noch nicht so ganz verstanden. Legt man ein struct an um sicher zu stellen das alle Variablen bei einer neuen Initialisierung deklariert werden? Oder geht es eher darum, daß die fehlende Referenz verhindern soll das z.B. beim 3. Auto aus der Autofarbe plötzlich die Sitzpolster Farbe wird ? Ich glaube es sickert beim mir tatsächlich durch! Habe es zu struct geändert. Das hat wirklich Sinn. Kann ich jetzt mein Save() so stehen lassen? Und wie sieht´s dann bei Load() aus? using UnityEngine; using System; [Serializable] public struct SaveTheme { public int themeIndex; public string themeName; public Color themeColor; //public Transform themeHolder; // public GameObject themePrefab; public SaveTheme (int index, string name, Color color) { this.themeIndex = index; this.themeName = name; this.themeColor = color; } } Link zu diesem Kommentar Auf anderen Seiten teilen More sharing options...
Sascha Geschrieben 5. April 2019 Melden Share Geschrieben 5. April 2019 vor 6 Stunden schrieb Hermetes: Legt man ein struct an um sicher zu stellen das alle Variablen bei einer neuen Initialisierung deklariert werden? Nein, das kann man auch mit einem Konstruktor sicherstellen. vor 6 Stunden schrieb Hermetes: Oder geht es eher darum, daß die fehlende Referenz verhindern soll das z.B. beim 3. Auto aus der Autofarbe plötzlich die Sitzpolster Farbe wird ? Nein, sowas gibt's bei C# nicht ...solange du es nicht drauf anlegst Der Unterschied ist wirklich die Referenzsemantik. Nimm diesen Code, wie er bei Unity recht häufig vorkommt: transform.position = target.position; Transform.position ist ein Vector3, und das ist ein struct. Der Vektor, also die drei Zahlen, werden rüberkopiert. Wenn du nach dieser Zeile transform.position änderst, dann bewegt sich eben das Objekt, zu dem das Script gehört - das als "target" referenzierte Objekt aber natürlich nicht. Wäre Vector3 eine Klasse, dann wäre die position von "target" ein Objekt, und "target.position" würde dieses Objekt referenzieren. Die obige Zeile bewirkt, dass diese Referenz kopiert wird. Danach zeigen sowohl "transform.position" als auch "target.position" auf dieselben drei Zahlen im Speicher. Und wenn du diese drei Zahlen änderst z.B. durch: transform.position.x = 10; Dann würde die neue X-Position für beide Objekte gelten, da sie beide dieselbe (geänderte) Zahl benutzen. Tatsache ist, dass diese Zeile nicht mal kompiliert, und das hängt eben genau mit dieser potentiellen Verwirrung zusammen. Das ist eben der Unterschied zwischen Werten. Wenn ich durch ein Bonus-Item so viele Punkte erhalte, dass ich mit dir gleichauf bin, haben wir denselben Wert bei der Punktzahl. Kriege ich danach Punkte, heißt das nicht, dass du dann auch Punkte kriegst. Wenn wir aber zum Verkehrsamt laufen und du eintragen lässt, dass dein Auto jetzt auch mein Auto ist, dann kann ich danach nicht mein Auto blau färben, ohne dass dein Auto danach auch blau ist - weil's eben dasselbe Auto ist, und Autos Objekte sind und keine Werte. Link zu diesem Kommentar Auf anderen Seiten teilen More sharing options...
Hermetes Geschrieben 5. April 2019 Autor Melden Share Geschrieben 5. April 2019 Sascha , ich hab´s beinahe! Ich taste mich von einer Variablen zur nächsten. Mit dem Namen und der ID klappts! Und nun weiss ich nicht wie ich bei der Color vorgehen muss. Also, wie bringe ich das string zu Color zurück? saveThemeList.Add(new SaveTheme(themeIndex, themeName, themeColor, themeboxPrefab)); themePrefab.GetComponent<ThemeBox>().themeName = saveThemeList[i].themeName; themePrefab.GetComponent<ThemeBox>().themeID = saveThemeList[i].themeIndex; themePrefab.GetComponent<ThemeBox>().themeColor = saveThemeList[i].themeColor; Mein Struct: public SaveTheme (int index, string name, Color themecolor, GameObject prefab) { this.themeIndex = index; this.themeName = name; this.themeColor = themecolor; this.themePrefab = prefab; } Fehlermeldung: SaveManager.cs(99,65): error CS1503: Argument 3: cannot convert from 'string' to 'UnityEngine.Color' SaveManager.cs(103,60): error CS0029: Cannot implicitly convert type 'string' to 'UnityEngine.Color' Meine Umwandlung zum String var themeColor = PlayerPrefs.GetString("theme_color" + i); Das war ein gutes Beispiel mit der Vector 3! Irgendwie assoziere ich ein struct als ein neutraler Wert. Mag vielleicht wieder falsch sein, aber so funktioniert meine Denke. Link zu diesem Kommentar Auf anderen Seiten teilen More sharing options...
Sascha Geschrieben 5. April 2019 Melden Share Geschrieben 5. April 2019 Kleinigkeit vorweg: Du hast da recht viel sehr gleich aussehenden Code - so etwas ist immer zu vermeiden. Statt themePrefab.GetComponent<ThemeBox>().dies themePrefab.GetComponent<ThemeBox>().jenes schreibe var themeBox = themePrefab.GetComponent<ThemeBox>(); themeBox.dies themeBox.jenes Aber zum Eigentlichen: Alle Typen in C# haben die ToString-Methode, also lassen sich alle Dinge als String ausgeben. Das hat schon seine Gründe - Daten mit Playerprefs speichern ist keiner davon. Entsprechend gibt es keine erzwungene Konvertierungsmöglichkeit von string zurück zu allen beliebigen Typen, da musst du selber ran. ToString ist nicht groß optimiert, einen string zu bauen, den man dann wieder gut einlesen kann. Unter anderem deshalb empfiehlt es sich, auch das Konvertieren von BeliebigerTyp zu string noch einmal selber zu bauen. Ein Beispiel: private static string SerializeColor(Color c) { return c.r + "|" + c.g + "|" + c.b + "|" + c.a; } private static Color DeserializeColor(string s) { var c = s.Split('|'); try { return new Color(float.Parse(c[0]), float.Parse(c[1]), float.Parse(c[2]), float.Parse(c[3])); } catch { Debug.LogError("Could not deserialize color."); return new Color(0,0,0,0); } } Ein try-catch um alles herum ist evtl. nicht die sauberste Lösung, und einfach alle Werte mit Pipes zu trennen ist nur eine von vielen Möglichkeiten, aber so würde das erstmal funktionieren. Jedenfalls dann einfach: PlayerPrefs.SetString(key, SerializeColor(color)); color = DeserializeColor(PlayerPrefs.GetString(key)); Natürlich kann man das noch hübsch in Methoden verpacken: public static void SetColor(string key, Color c) { PlayerPrefs.SetString(key, SerializeColor(color)); } public static Color GetColor(string key) { return DeserializeColor(PlayerPrefs.GetString(key)); } und dann einfach GetColor genau wie GetString aufrufen, und so weiter. Link zu diesem Kommentar Auf anderen Seiten teilen More sharing options...
Hermetes Geschrieben 6. April 2019 Autor Melden Share Geschrieben 6. April 2019 Tausend Dank Dir Sascha!!! Der Compiler meinte das man den Type zu einer Variable ersetzen muss. Also machte ich SerializeColor(color)) zu : PlayerPrefs.SetString(key, SerializeColor(c)); Weiss nicht ob das richtig war Habe nämlich deinen Debug Error bekommen. Debug.LogError("Could not deserialize color."); Und so in Save() eingeschrieben: SetColor("theme_color + 1", saveThemeList[i].themeColor); In Load() var themeColor = GetColor("theme_color" + i); theme.themeColor = saveThemeList[i].themeColor; Playerprefs habe ich mir nicht unbedingt ausgesucht. Es erschien mir am leichtesten.😌 Zu was tendierst du denn ? Json oder XML ? Link zu diesem Kommentar Auf anderen Seiten teilen More sharing options...
Hermetes Geschrieben 6. April 2019 Autor Melden Share Geschrieben 6. April 2019 Oh mir fiel es gerade auf! "theme_color" + i muss es natürlich heissen. Die Themebox übernimmt die Sachen nun wieder nicht, es ist zu spät heute...mache Feierabend. Link zu diesem Kommentar Auf anderen Seiten teilen More sharing options...
Sascha Geschrieben 7. April 2019 Melden Share Geschrieben 7. April 2019 vor 11 Stunden schrieb Hermetes: Debug.LogError("Could not deserialize color."); Das Ding wird halt ausgelöst, wenn der String, der von den PlayerPrefs kommt, nicht exakt dem Format entspricht, das die Deserialize-Funktion erwartet, also "F|F|F|F", wobei "F" stellvertretend für ein float ist. vor 11 Stunden schrieb Hermetes: Weiss nicht ob das richtig war Naja, c muss halt die Variable mit deiner Farbe sein - ob die Varable jetzt c oder color oder myColor oder franz heißt, ist wurscht, solange du überall denselben Namen hinschreibst vor 11 Stunden schrieb Hermetes: var themeColor = GetColor("theme_color" + i); theme.themeColor = saveThemeList.themeColor; Du schreibst hier die gelandene Farbe in eine Variable namens "themeColor", liest dessen wert aber nirgendwo aus. Link zu diesem Kommentar Auf anderen Seiten teilen More sharing options...
Recommended Posts
Archiviert
Dieses Thema ist jetzt archiviert und für weitere Antworten gesperrt.