Jump to content
Unity Insider Forum

Sascha

Administrators
  • Gesamte Inhalte

    13.619
  • Benutzer seit

  • Letzter Besuch

  • Tagessiege

    780

Alle erstellten Inhalte von Sascha

  1. Naja, du hast ja zwei Arten von Objekten hier. Das eine ist nach deiner Beschreibung "statisch" (wie gesagt: voll okay, solche Begriffe zu nutzen, auch wenn sie anderswo schon eine feste Bedeutung haben, solange man es erklärt). Das andere Objekt wird instanziiert (oder auch nicht, je nach dem) und reagiert darauf, angeklickt zu werden. Ich sehe da auf jeden Fall zwei Scripts. In dem Fall: ItemPosition public class ItemPosition : MonoBehaviour { public Item item; public ItemRepresentation spawnedItemPrefab; private ItemRepresentation spawnedItem; private void SpawnItem() { // Wenn ich schon ein Item in der Szene habe, dann lass mal if (spawnedItem) return; spawnedItem = Instantiate(spawnedItemPrefab, ...); spawnedItem.item = item; } } ItemRepresentation public class ItemRepresentation : MonoBehaviour { public Item item; private void OnMouseDown() { Inventory.AddItem(item); Destroy(gameObject); } } Ist jetzt nur ein minimales Beispiel, wo sich ein Item immer ins Inventar packt und dann selbst aus der Szene löscht. Was aber auch geht: Du packst den Collider und damit die "Anklickbarkeit" auf das "statische" Objekt. Kann ich mir auch gut vorstellen. Dann brauchst du nur ein Script, weil die Prefab-Kopie nix macht außer hübsch auszusehen. ItemPosition würde sich dann um Inventory.AddItem und Destroy kümmern.
  2. Ja, keine Sorge - ist nix neues. Aber entsprechend muss ich das ansprechen, wenn die Kommunikation uns daran hindert, eine Lösung zu finden. Und vielleicht hast du ja auch auf Dauer was davon, mehr korrekte Begriffe zu kennen Entsprechend auch etwas Terminologie für das, was du geschrieben hast: Ein Prefab ist ein GameObject, das nicht in einer Szene existiert. Instantiate kopiert ein GameObject in die aktuell aktive Szene. (Du kannst damit in der Tat auch GameObjects kopieren, die schon in der Szene sind - es muss kein Prefab sein). Da es in der Tat möglich ist, ein Prefab zu ändern, ist es wichtig, zwischen Prefabs und ihren in der Szene existierenden Kopien zu unterscheiden. Wenn du nämlich "ein Prefab änderst", dann hat das je nach Kontext einige sehr... "interessante" Nebeneffekte. Man redet bei dem, was in der Szene landet, wenn man ein Prefab hinein zieht, daher von einer Prefab-Instanz. Bei dem, was Instantiate zur Laufzeit produziert, übrigens eher nicht. Der entscheidende Punkt, dass geänderte Eigenschaften im Prefab auch in dessen Instanzen übernommen werden, fehlt da nämlich. Dann etwas worüber ich jetzt schon mehrfach gestolpert bin: Es ist völlig okay, dass du auch Begriffe benutzt, die woanders eine feste Bedeutung haben. Aber du solltest hier nicht von "statischen Objekten" reden, ohne einmal kurz zu erklären, was das bedeutet. Meinst du damit einfach nur, dass das Objekt nicht zur Laufzeit erstellt oder zerstört wird, sondern immer vorhanden ist? Also verstehe ich das richtig, dass du ein Script hast, das ein Prefab instanziiert, und das resultierende Objekt hat wiederum ein Script, welches darauf reagiert, dass es angeklickt wurde? Du solltest dringend die Namen deiner Klassen überarbeiten. Es ist zwar schön einfach, die Dinger "IrgendwasManager" zu nennen; aber genau solche Zusammenhänge gehen durch solche Namen verloren. Wenn ich dir als Namen z.B. "ItemPosition" und "ItemRepresentation" vorschlage, dann weißt du sofort, welche Klasse welche wäre. Und so als Tipp: Deine eigene Denkweise kannst du damit auch beeinflussen. Wenn du deine Namen sorgfältig wählst, fällt es dir einfacher, über die Zusammenhänge nachzudenken, in denen sie vorkommen. Wenn deine Annahme jetzt jedenfalls korrekt ist und du den Grund für das Problem gefunden hast, habe ich hier die Lösung für dich: Instantiate gibt dir eine Referenz auf das Objekt zurück, das es erstellt hat. Du kannst also dein neues Objekt direkt bearbeiten. var itemInstance = Instantiate(itemPrefab); itemInstance.representedItem = myItem; Was ich hier übrigens mache, ist bei "itemPrefab" als Typ nicht GameObject, sondern den Typ der relevantesten Komponente zu nehmen: public ItemRepresentation itemPrefab; // oder wie auch immer das bei dir heißt. Da kann man dann alle GameObjects rein ziehen, die diese Komponente haben. Und Instantiate gibt direkt eine Referenz auf diese Komponente auf der erstellten Kopie zurück, sodass man sich ein GetComponent sparen kann.
  3. Liegt vielleicht daran, dass ich echt müde bin, aber hier verstehe ich nix. SOs erzeugst du nicht - zumindest nicht über Runtime-Code. Also... kannst du, aber... machst du nicht. Was heißt "übernommen"?
  4. Joa, da kann eine NRE definitiv her kommen Was denn für ein neues Script? Warum baust du deinen Code nicht einfach so, dass das Objekt nur dann verwendet wird, wenn es auch Relevanz hat? Du kannst doch wunderbar schreiben, dass etwas anderes (z.B. auch "gar nichts") passieren soll, wenn kein Item da ist: public class TreasureChest : MonoBehaviour { public Item loot; public void Open() { if (loot != null) { PlayerInventory.Add(loot); loot = null; } else { Debug.Log("Ich bin leer :("); } } } Wenn "Mein klickbares Objekt hat nix mit irgendeinem Item zu tun, weil es z.B. eine Tür ist" ein valider Fall in deinem Gamedesign ist, dann sollte dein Code diesen Fall auch unterstützen. Der Code wird nicht neu kompiliert. Das passiert nur, wenn sich etwas im Code ändert - und dann siehst du das unten rechts am sich drehenden Ladekreis, und evtl. auch am Dialog mit Fortschrittsbalken. Wenn du in den Play Mode gehst, wird die offene Szene einmal entladen, das Programm neu gestartet und die Szene neu deserialisiert (geladen). Dass da durch das Starten des Spiels ohne dein Zutun irgendwelche Referenzen verloren gehen, kannst du im Normalfall ausschließen.
  5. Dann ist es sehr unwahrscheinlich, dass das Item irgendwo gelöscht wurde - geht ja bei Assets nicht mal eben mit Destroy(). So oder so heißt das aber, dass da ein ObjectManager herumfliegt, der kein Item referenziert. Das müsstest du eigentlich über den Inspektor nachvollziehen können. Also einmal in die Szene navigieren und die ObjectManager-Instanzen durchgehen. Dann weißt du schonmal, welches Objekt da Probleme macht. Anschließend kannst du das Objekt außerhalb des Play Modes anschauen und sehen, ob das schon falsch im Editor eingestellt ist oder die Referenz zur Laufzeit flöten geht.
  6. Du solltest niemals einfach blind irgendwas rauskürzen, in der Hoffnung, das der Fehler verschwindet. Der Code, der dann übrig bleibt, wird mit allerhöchster Wahrscheinlichkeit nicht das tun, was du willst. Und das ist auch nicht wirklich mehr Wert als Code, der nicht kompiliert. "Item" ist also eine ScriptableObject-Klasse, richtig? Und deine Items existieren alle in den Assets? Und du ziehst sie dann in deine "ObjectManager"-Instanzen in der Szene?
  7. Genau - ein bool ist ein "Value Type", zu denen auch Zahlen, Chars und Structs (z.B. Vector3) gehören. Value Type-Variablen können nicht null als Wert annehmen und entsprechend nie diese Exception auslösen. Unabhängig von deinem spezifischen Problem und dessen Lösung ist es sinnvoll, dass du dir Werkzeuge aneignest, mit denen du sie sehen kannst. Debug.Log ist schon ganz nett, aber gerade bei noch nicht richtig lokalisierten Problemen etwas unpraktisch. Schau dir mal an, wie du mit deiner IDE den Debugger benutzt. Du setzt einen Breakpoint auf die von der Fehlermeldung genannten Zeile, verbindest den Debugger mit Unity "spielst" bis zum Problem-Moment. Dann kannst du dir schön den Zustand deines ObjectManager-Objekts und aller relevanten Variablen ansehen. Eine Variable, die null als Wert hat, sticht dann schnell heraus. Mit dem Debugger kannst du spätestens im nächsten Schritt auch früher ansetzen und schauen, wie es dazu kommt, dass da null steht.
  8. Moin! NullReferenceExceptions (NREs) sind nicht annähernd so undurchsichtig, wie man vielleicht initial glaubt. Erstmal wäre in der Konsole die Zeile zu finden, in der die Exception geflogen ist. Damit weißt du schonmal mehr. Dann kann eine NRE immer nur davon kommen, dass vor einem Punkt oder einer eckigen Klammer etwas den Wert null hat. Wenn du also z.B. irgendwas.Zeug().liste[5] hast, dann könnte "irgendwas" null sein, "Zeug()" null zurück geben, oder "liste" ist null. Damit solltest du das Problem schonmal stark eingrenzen können.
  9. Sascha

    Unity und Singletons

    Moin! Ich habe endlich die überarbeitete Version meines Unity-Singleton-Artikels fertig. Wer Singletons nicht kennt, sich fragt, was das eigentlich genau ist oder einfach nur potentiell seinen Horizont erweitern will, darf gerne Mal einen Blick riskieren. Und wer meint, dass man Singletons nicht benutzen sollte, erst recht http://blog.13pixels.de/2023/unity-and-the-mysterious-singleton/ Ich hatte 2019 schon einen Artikel darüber geschrieben, aber meine Ansichten zu dem Thema haben sich seitdem ein bisschen verändert. Der neue Artikel ist ein ganzes Stück objektiver und klarer in seinen Inhalten.
  10. Kannst ja nachschauen, was dein String ist: try { float.Parse(MengeEingabe.text); } catch { Debug.LogError(MeineEingabe.text); } und dann solltest du erkennen können, warum das nicht funktioniert.
  11. Ich glaub, du hast die angekündigte Fehlermeldung vergessen Meine Vermutung ist aber, dass du ein Komma als Trenner eingegeben hast und float.Parse vielleicht einen Punkt erwartet?
  12. Unity ist für AR schon ne ganz gute Sache. Kannst du das hier noch ein bisschen spezifizieren? Wenn du von AR redest, musst du da immer genauer sein. Was für ein Ort? Ein virtueller Ort oder ein echter? Was macht der QR-Code genau? Ist der irgendwo, und dann wird relativ zu dessen Position etwas virtuelles angezeigt? Jeweils nur ein einzelnes Bauteil? Denn wenn du das meinst, dann platzierst du ja schon den QR-Code - ist damit nicht der "von dir platzierte Ort" inbegriffen?
  13. Moin! Trivial ist das Projekt leider schon einmal nicht. XR anzugehen, ohne erstmal genügend Basiskenntnisse für ein simples eigenes Spiel zu sammeln, ist etwas heftig. Und ob du jetzt Zeit hast, dich ein paar Wochen bis Monate in Unity-Basics einzuarbeiten, musst du selber wissen. Die Pipeline "CAD-Programm -> Unity" ist zum Glück nicht problematisch. Unity benutzt selber FBX, versteht aber direkt oder indirekt mehrere Formate. Wenn du z.B. eine .blend-Datei importierst, dann haut Unity selbständig im Hintergrund Blender an, das einmal in FBX zu konvertieren. Die .blend-Datei bleibt in den Assets aber erhalten. Die dazu gehörende FBX-Datei wird dann immer da unsichtbar eingetauscht, wo die .blend-Datei eingetragen wird. Aus der Schnittmenge aus "kann mein CAD-Editor exportieren" und "kann Unity importieren" lässt sich daher sicherlich immer etwas finden. Was meinst du mit ?
  14. Moin! Die Simpelste wäre vermutlich, das Öffnen des Menüs um einen Frame zu verzögern, sodass der Button nicht mehr als frisch gedrückt gilt. Alternativ kannst du vielleicht das EventSystem erst einen Frame später aktivieren.
  15. Moin! Du willst es wirklich so weit wie nur irgendwie möglich vermeiden, das per Script zu machen. Was spricht denn gegen eine LayoutGroup-Komponente?
  16. Das Ding sollte da auf jeden Fall nicht sein, da ist eine Einstellung falsch. Schau mal hier: https://forum.unity.com/threads/theres-a-unity-logo-on-my-game-icon.1352450/
  17. Aaalso... Das Picture-struct kann weg. Ernsthaft, du brauchst die Transform-Komponente nicht, also mach dir direkt eine Liste mit SpriteRenderern. Die Liste "availableCityIndices" kann durch einen BagOfNumbers ersetzen, macht die Verwendung einfacher. "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. 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
  18. Moin! Unity erlaubt by default nicht, dass du ohne https Anfragen verschickst. Das kannst du zumindest zum Entwickeln in den Player Settings unterbinden, aber früher oder später solltest du für das Backend ein SSL-Zertifikat einrichten. So sieht die Option in 2023.x aus:
  19. 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. 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.
  20. Das ist doch genau, wonach ich gefragt habe, und du hast geantwortet 🤔 Aber kann es sein, das wir aneinander vorbei reden? Ich rede von der Position der Teilbilder, und du von der Reihenfolge, in der sie aufgedeckt werden?
  21. 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.
  22. 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. 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: 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: Als nächstes: 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. 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.
  23. 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.
  24. Das ist das, was mich verwirrt hatte. Du willst ja keinen Zufall beim Anzeigen der Teilbilder haben, deshalb sollte hier gar nicht von Zufall die Rede sein. Du hast neun Sprites und neun SpriteRenderer, und jeder SpriteRenderer kriegt den Sprite mit demselben Index, fertig. Siehe Link Wie gesagt, lass Transform da raus. Du ziehst deine SpriteRenderer in den Inspektor und hast sie dann im pictureParts-Array in genau der Reihenfolge, in der du sie reingezogen hast. Vermutlich ist 0 der Renderer oben links, 1 der mittlere oben usw. Und die Sprites, die du auch jeweils in einer Neunerliste drin hast, sind genauso geordnet. Daher bekommt pictureParts[0] auch sprites[0] zugewiesen, pictureParts[1] kriegt sprites[1]. Genau das macht die Schleife in diesem Post.
×
×
  • Neu erstellen...