Jump to content
Unity Insider Forum

Assign zur Laufzeit


nordseekrabe

Recommended Posts

vor 9 Minuten schrieb nordseekrabe:

Die Display(City city) gibt für die Zeile "PictureParts[i].sprite = city.sprites[i]; eine Fehlermeldung: "Object reference not set to an instance". In der o.g. Form fehlt doch auch die Auswahl der darzustellenden Stadt, also in unserem Fall die listIndex, oder verstehe ich da was völlig falsch ?

PictureParts sollte public sein bzw. das Attribut [SerializeField] haben. Wenn du das hast, taucht das Feld im Inspektor auf, wenn du das GameObject selektierst, das dein Script drauf hat. Dort musst du deine SpriteRenderer reinziehen.

Damit das, was du im Editor zugewiesen hast, auch zur Laufzeit verfügbar bleibt, darfst du in deinem Code das Array nicht überschreiben.

Link zu diesem Kommentar
Auf anderen Seiten teilen

Korrektur von oben, habe natürlich auch die Schleife, nur die Zeile verändert.

Aber nochmal was ganz anderes. Wenn mein Gedanke völlig falsch ist, bitte habe Nachsicht. Warum klappt unser "RemoveAt" nicht ? Ist es nicht so, dass RemoveAt eine Indexzahl entfernt? Wenn ich 9 Elemente habe (0 bis 8) und RemoveAt  entfernt jetzt z.B Index 6, dann habe ich zwar nur noch 0 bis 7, aber Random kann erneut 6 wählen, da die Indexfolge der List<> angepasst wird (0 bis 7). Wünschenswert wäre doch,  dass die gewählte Indexzahl gelöscht wird und somit in unserem Fall dann nur noch 0,1,2,3,4,5,7,8 vorhanden sind ? Wenn es so wäre, sollte man dann nicht die Liste anhand ihrer Namen (cities.name) randomisieren ? Wenn dann Aachen gelöscht (removed) wird, kann es in einem nächsten Durchlauf nicht mehr ausgewählt werden. Geht das überhaupt (Random.Range(0, cities.name.Count)  ??

Mir kam dieser Gedanke, weil im Deinem vorgestellten Zahlensack auch wieder nur die Formulierung "numbers.RemoveAt(index)" erscheint, die wir doch 2x  anwenden.

Peter  

Link zu diesem Kommentar
Auf anderen Seiten teilen

Die Zuweisung sieht schonmal gut aus. Schau wegen der NullReferenceException in deinem Code nochmal, ob das Array zur Laufzeit irgendwie verändert oder gar überschrieben wird.

vor einer Stunde schrieb nordseekrabe:

Ist es nicht so, dass RemoveAt eine Indexzahl entfernt?

Nein, RemoveAt entfernt das n-te Element, egal welchen Wert das Element hat. Kann leider bei Listen von Zahlen schnell verwirrend werden. Daher mit Buchstaben:

Wenn du eine Liste a,b,c,d,e hast und RemoveAt(1) aufrufst, dann hast du danach a,c,d,e. Das 'b' an Position 1 ist weg.

Es gibt noch Remove(), um ein bestimmtes Element unabhängig von dessen Position in der Liste zu entfernen, z.B. Remove('c').

Wenn du also eine Liste von Indizes hast, und es sind jetzt zum einfacheren Verständnis schon die meisten Zahlen raus genommen, dann hast du z.B. noch 10,11,12. Dann wählen wir zufällig eine davon aus, indem wir den Index würfeln, also 0, 1 oder 2. Das geht mit Random.Range(0, 3), wobei du statt der 3 natürlich list.Count schreibst.
Dann kriegst du z.B. 0, und das Element mit dem Index 0 ist die 10. Also wird mit RemoveAt(0) die 10 entfernt und du hast noch 11,12. Die 10 wird dann zurück gegeben und als Index für die Liste aller Städte benutzt. Die Stadt mit dem Index 10 in dieser Liste kann nur dieses eine Mal ausgewählt werden, weil sie nach dem auswählen nicht mehr in der Liste der verfügbaren Indizes ist - sie enthält jetzt nur noch die Zahlen 11 und 12.

Deshalb stimmt diese Aussage nicht:

vor einer Stunde schrieb nordseekrabe:

Wenn ich 9 Elemente habe (0 bis 😎 und RemoveAt  entfernt jetzt z.B Index 6, dann habe ich zwar nur noch 0 bis 7

Wenn du mit RemoveAt Index 6 entfernst, dann hast du noch 0,1,2,3,4,5,7,8. Die 6 ist weg.

Wenn du jetzt nochmal mit Random.Range eine 6 kriegst, dann hast du 0,1,2,3,4,5,8. Die 7, die eben an Stelle 6 stand, wurde jetzt entfernt.

Also genau das, was du willst:

vor einer Stunde schrieb nordseekrabe:

Wünschenswert wäre doch,  dass die gewählte Indexzahl gelöscht wird und somit in unserem Fall dann nur noch 0,1,2,3,4,5,7,8 vorhanden sind ?

Als nächstes:

vor einer Stunde schrieb nordseekrabe:

Wenn es so wäre, sollte man dann nicht die Liste anhand ihrer Namen (cities.name) randomisieren ?

Brauchst du halt nicht, weil die Reihenfolge, in der du die Indizes aus deiner Liste ziehst, bereits randomisiert ist.

Stell dir vor, du hast eine Gameshow. An der Wand stehen durchnummeriert (!) 10 Städte. Der Showmaster hat einen Beutel in der Hand und lässt einen Kandidaten daraus einen Tischtennisball ziehen. Statt eines Stadtnamens steht auf dem Ball aber eine Zahl zwischen 0 und 9 (inklusive). Alle Anwesenden sehen: Es wurde eine 3 gezogen! Sie schauen auf die Wand, finden die 3 und sehen, dass daneben "Aachen" steht.

Dieser Unterschied zwischen "der Name der Stadt steht auf den Bällen im Sack" und "es steht eine Zahl auf dem Ball, der zu einer Stadt gehört" - um den geht es hier.

Wären die Städtenamen im Sack, dann müsstest du in deinem Code die Städte mischen, wobei du mit "wähle zufällig eine und streiche sie aus der Liste, damit sie nicht nochmal gezogen wird" schon den besten Ansatz gefunden hattest. Meine Lösung packt die Städtenamen allerdings auf die Wand, damit wir die Liste der Städte nicht anfassen müssen. Das präferiere ich immer, wenn es um Daten geht, die im Editor eingestellt werden. Hat was mit Konsistenz und ScriptableObjects zu tun.
Damit die Städtenamen-Liste nicht angefasst werden muss, packe ich stattdessen die Indizes als Bälle in den Sack. Ansonsten funktioniert mein Code genau wie dein ursprünglicher: Es wird zufällig ein Ball gezogen, und er wird dann auch aus dem Sack entfernt, damit er nicht nochmal gezogen wird.

vor einer Stunde schrieb nordseekrabe:

Mir kam dieser Gedanke, weil im Deinem vorgestellten Zahlensack auch wieder nur die Formulierung "numbers.RemoveAt(index)" erscheint, die wir doch 2x  anwenden.

Meine Lösung sieht vor, dass diese Methode außerhalb des Zahlensacks nicht mehr aufgerufen wird. Die Wand der Gameshow wird ja während der gesamten Sendung nicht verändert, da brauchen wir also nichts zu entfernen.

Link zu diesem Kommentar
Auf anderen Seiten teilen

Danke für die ausführliche Erläuterung, die ich soweit verstanden haben sollte. Bleibt für mich allerdings weiterhin unklar, warum es zur Mehrfachauswahl von PictureParts kommt wenn wir sie entfernt haben.

                        collectedOne = UnityEngine.Random.Range.Range(0, PictureParts.Count);

                        Debug.Log("Teilbild: " + collectedOne);  // es wird tatsächlich immer eine Zahl zwischen 0 und8 angezeigt, aber eben auch mehrfach diesselbe

                        PictureParts[collectedOne].sprite = cities[listIndex].sprites[collectedOne;

                        PictureParts[collectedOne].gameObject.SetActive(true);

                        PictureParts.RemoveAt(collectedOne);

Screenshot 2023-11-08 170831.png

Link zu diesem Kommentar
Auf anderen Seiten teilen

Wir haben hier zwei Ebenen mit jeweils einer Liste drin: Die Liste der Städte, und jede Stadt enthält die Liste der Sprites.

Bei der Auswahl der Stadt benutzen wir Random.Range, aber bei den Sprites soll es ja gar keinen Zufall geben. Da sollen einfach nur 9 Sprites dem jeweils dazugehörigen SpriteRenderer zugewiesen werden.

Die Zeile

collectedOne = UnityEngine.Random.Range.Range(0, PictureParts.Count); 

ist damit schon direkt falsch.

Stattdessen gehst du alle 9 SpriteRenderer und ihre dazugehörigen Sprites durch. Alle neun, jeden ein Mal.

Genau das macht die Schleife, auf die ich hiermit erneut verweise :)

private void Display(City city)
{
  for (var i = 0; i < pictureParts.Length; i++)
  {
    pictureParts[i].sprite = city.sprites[i];
    pictureParts[i].gameObject.SetActive(true);
  }
}

Wie du siehst: Kein Random.

Link zu diesem Kommentar
Auf anderen Seiten teilen

Entschuldige, dass ich widerspreche. Genau das will ich nämlich auch: neben der Zufallsauswahl aus 100 Orten soll auch bei der Darstellung eines Teilbildes aus den 9 verschiedenen eines zufällig gewählt werden. Hat u.a. den Sinn, dass das Raten hierdurch etwas komplizierter wird. Also noch mal die Spiellogik: nach dem Start ruft der Spieler mit dem Button "Teilbild zeigen" das 1.Teilstück aus dem Gesamtbild des durch Zufall aus 100 Orten ausgewählten Ortes auf. Ist ihm die Stadt bekannt, kann er im Inputfield seine Vermutung eintragen. Stimmt diese, erscheint das komplette Bild; zusätzlich erhält der Spieler eine Punktebewertung im Score. Ist die Eingabe im Inputfield aber falsch, tut sich nichts und der Spieler ruft mit dem Button "Teilbild zeigen" das 2.Teilbild auf. Auch das natürlich wieder per Random aus den noch verbliebenen 8 Teilbildern. etc.etc. Zusätzlich kann man mit dem Button "Beschreibung" eine textliche Erläuterung zu dem dargestellten Objekt aufrufen; dabei wird natürlich auch das Gesamtbild vervollständigt. Das funktioniert im Prinzip auch alles. Lediglich die korrekte Plazierung der Teilbilder ist nicht möglich und die Teilbilder treten mehrfach auf.

Ich hoffe, dass ich nicht nerve !

Peter

Link zu diesem Kommentar
Auf anderen Seiten teilen

Nein, absolut nicht die Reihenfolge. Wie oben von Dir selbst zitiert: korrekt aufgebaut, damit , wenn 9 Teilbilder plaziert sind, ein korrektes Bild zu sehen ist. In welcher Reihenfolge, also ob erst PicturePart0 oder PicturePart8 ist wurscht. Nicht wurscht ist, wo PicturePart0 landet (nämlich links oben) und das die Wahl, welches Teilbild nun drankommt, nicht der Reihenfolge 0 bis 8 gezollt ist, sondern einem Zufallsgenerator. Also zufällig macht PictionPart0 den Anfang, wird zufällig von PicturePart5 gefolgt, etc. aber immer an der richtigen Position im Bild. Deswegen habe ich ja von Anfang das Problem des Transform.position der SpriteRenderer/PictureParts angesprochen. Und wie schon schon mal angedacht, ob man mit z.B. struct Picture { public Transform picturePos; public SpriteRenderer sprite} und dann einer List<Picture> PictureParts weiter kommen könnte?

Aber Moin, moin sollte nicht vergessen werden

Gruß Peter  

Link zu diesem Kommentar
Auf anderen Seiten teilen

Habe das mal ausgeführt mit [serializable] private struct Picture {public Transform picturePos; public SpriteRenderer sprite;} und dann eine List<Picture> PictureParts; erstellt. Konnte damit im Inspector sehr gut jedem einzelnen Element sowohl Transform als auch SpriteRenderer zuweisen. Leider konnte ich die Sinnhaftigkeit nicht testen. Erhalte eine Fehlermeldung:

"der Rückgabewert von List<Picture>.this[int] ist keine Variable und kann daher nicht geändert werden"   für die Zeile PictureParts[collectedOne].sprite =  cities[listIndex].sprites[collectedOne]; der Fehler bezieht sich auf die linke Hälfte der Zeile. Was habe ich denn da falsch gemacht ? Ein kleiner Tipp wäre hilfreich, auch wenn die Codeidee ein grundsätzlicher Strukturfehler sein sollte.

Peter

Link zu diesem Kommentar
Auf anderen Seiten teilen

Okay. In dem Fall ergibt es keinen Sinn, beim Zuweisen der Sprites an die SpriteRenderer irgendetwas zufällig zu machen.

Mach es mit der Schleife, die ich hier nochmal verlinke. PictureParts[0] ist immer der SpriteRenderer oben links, und sprites[0] ist immer der Sprite oben links, der dazu gehört. Das Aufdecken, das dann zufällig ist, ist eine ganz andere Angelegenheit.

vor 18 Minuten schrieb nordseekrabe:

Erhalte eine Fehlermeldung:

Magst du einmal die gesamte aktuelle Klasse posten? Ich nehme an, dein Code ist gerade sehr "gewachsen". Da dürfte viel durcheinander sein. Wenn ich das gesamte Ding einmal sehen kann, sehe ich bestimmt was.

Link zu diesem Kommentar
Auf anderen Seiten teilen

Besten Dank. Nebenbei gesagt, glaube erkannt zu haben, warum es beim Random zu Doppelten kommt: mit jedem Aufruf von ShowPicturePart() (bei Buttonklick) wird jeweils stets die OriginalList von PictureParts genommen und nicht die durch RemoveAt veränderte. Oder sehe ich das falsch ?

Peter   

GameProcedure.cs Spielstart.cs

Link zu diesem Kommentar
Auf anderen Seiten teilen

Aaalso...

  1. Das Picture-struct kann weg. Ernsthaft, du brauchst die Transform-Komponente nicht, also mach dir direkt eine Liste mit SpriteRenderern.
  2. Die Liste "availableCityIndices" kann durch einen BagOfNumbers ersetzen, macht die Verwendung einfacher.
  3. "PickRandomCity" sollte nicht in ein Feld ("listIndex") hinein schreiben, sondern DisplayCity aufrufen, mit besagter Schleife drin.
    Du änderst aber noch SetActive(true) auf SetActive(false).
    Dann werden alle Sprites richtig zugewiesen, aber alle Bildteile werden ausgeblendet. Du machst sie dann in ShowPicturePart() mit SetActive(true) wieder an.
vor 1 Stunde schrieb nordseekrabe:

mit jedem Aufruf von ShowPicturePart() (bei Buttonklick) wird jeweils stets die OriginalList von PictureParts genommen und nicht die durch RemoveAt veränderte. Oder sehe ich das falsch ?

Nein, es gibt da nur die eine Liste. Beim Entfernen eines Elements wird keine Kopie erstellt oder so.

Ich hab dir mal den relevanten Teil (alles andere rausgekürzt) so geschrieben, wie ich das meine:

GameProcedure.cs

Link zu diesem Kommentar
Auf anderen Seiten teilen

Donnerwetter, echt unglaublich, alle Probleme aufgelöst im Nichts. Alle Logik klappt, das Ratespiel kann ausgeführt werden. Habe intensiv studiert, um alle Abläufe zu verstehen und die Neuerungen richtig einzubauen. Besonders erstaunlich finde ich, dass nun plötzlich die Teilbilder richtig platziert sind, was wohl darin begründet sein könnte, dass das Programm die Teilbilder 0 bis 8 automatisch auf dem Display richtig verteilt, wenn man sie alle mit Schleife aufruft und, wie in Deiner Lösung, dann auf Buttonklick einzeln aktiviert (SetActive(true)). Das ist in dieser Form natürlich alles noch zig Nummern zu hoch,  um selbständig zu solchen Lösungen zu kommen.

Dennoch zeigt sich an diesem Dialog mit Sascha, wie wichtig so ein Forum und die damit aktiven Helfer, für einen Anfänger sind. Ohne dies wäre ich frustriert in der Sackgasse hängen geblieben und hätte resignierend abgebrochen. Habe so dabei nicht nur ein Projekt beenden können, sondern auch sehr viel gelernt, was bei nächsten Versuchen Anwendung finden kann.

Also, habe tausend Dank, Sascha, für Geduld und Ausdauer, mit mir hierbei zu einem guten Ende gekommen zu sein. Bis zu einer nächsten Gelegenheit

Peter

Link zu diesem Kommentar
Auf anderen Seiten teilen

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Gast
Auf dieses Thema antworten...

×   Du hast formatierten Text eingefügt.   Formatierung jetzt entfernen

  Only 75 emoji are allowed.

×   Dein Link wurde automatisch eingebettet.   Einbetten rückgängig machen und als Link darstellen

×   Dein vorheriger Inhalt wurde wiederhergestellt.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Lädt...
×
×
  • Neu erstellen...