Jump to content
Unity Insider Forum

Frage zum RayCast und sich damit zu bewegenden Objekten


Der_Stefan_

Recommended Posts

Guten Abend ihr Lieben,

ich stocke mal wieder und seh' das Land nicht... Ich arbeite gerade an einer Art 3D-Umsetzung des Spieleklassikers Das verrückte Labyrinth. Soll im Grunde genauso funktionieren, sprich mit den sich bewegenden Wegstücken in einer Reihe, nur mit dem Unterschied, dass man im 3D-Umfeld durch`s Labyrinth tappst und sich die Wände von alleine bewegen.

Meine Idee zur Umsetzung dabei war, dass ein GameObject um`s Spielfeld herum per Zufallsprinzip auf Wegpunkten hält und die jeweilige Reihe per Raycast auswählt und bewegt. An genau dieser Stelle stocke ich aber an gleich zwei Stellen:

Zum einen wählt der RayCast nur das erste Teil in der Reihe aus. Bekomme ich es wohl irgendwie hin, dass der Strahl durch die ganze Reihe strahlt und dabei alle 7 Objekte gleichzeitig anwählt?

Und zum anderen scheint irgendwas mit der Bewegungssteuerung krude zu sein. Angegeben ist, dass das Objekt sich um 12 Zähler, also einmal um seine eigene Länge nach vorne schieben soll. Statt dessen sind es aber manchmal 3, mal 6 oder auch 18 Zähler. Seltsamerweise aber immer glatte und durch 3 teilbare Zahlen... (im Bild unten sind es auch 6 statt 12). Hat jemand eine Idee woran das liegen könnte?

Hier noch der Script-Code meines Rays. Die Wegpunkte habe ich noch nicht korrekt angesetzt, zum testen löse ich den RayCast noch manuell über die Space-Taste aus. Überhaupt bin ich noch am tüfteln, wie ich das ganze Konstrukt überhaupt wie gewünscht umgesetzt bekomme. Wesentlich mehr Code beinhaltet die Klasse daher auch nicht.

if (Input.GetKeyDown (KeyCode.Space))
        {
            rowmove = true;
        }

        Vector3 origin = transform.position;
        Vector3 direction = transform.forward;

        Debug.DrawRay (origin, direction *200f, Color.red);
        Ray ray = new Ray (origin, direction);

        if (Physics.Raycast(ray, out RaycastHit raycastHit) && rowmove)
        {
            raycastHit.collider.GetComponent<Rigidbody>().transform.position += new Vector3(12f, 0.0f, 0.0f);
            Debug.Log ("RayShoot-Info");
            rowmove = false;
        }

Besten Dank und Gruß

 

Der Stefan

Unbenannt.JPG

Unbenannt2.JPG

Link zu diesem Kommentar
Auf anderen Seiten teilen

vor einer Stunde schrieb Der_Stefan_:

Meine Idee zur Umsetzung dabei war

Auch wenn ich gerne auf die weiteren Fragen eingehe, muss ich direkt mal sagen, dass das nicht die beste Idee ist. Du hast ein klar definiertes Spielfeld, das sogar als Grid angeordnet ist. Bau dir eine passende Datenstruktur (vermutlich ein 2D-Array) und greife darauf zu. Das ist wesentlich eleganter, weniger fehleranfällig und viel weniger mit Kanonen auf Spatzen schießen als da die Physik-Engine für rauszukramen.

vor einer Stunde schrieb Der_Stefan_:

Bekomme ich es wohl irgendwie hin, dass der Strahl durch die ganze Reihe strahlt und dabei alle 7 Objekte gleichzeitig anwählt?

Dafür gibt es Physics.RaycastAll bzw. das empfehlenswertere Physics.RaycastNonAlloc, das weniger Garbage fabriziert.

vor einer Stunde schrieb Der_Stefan_:

Und zum anderen scheint irgendwas mit der Bewegungssteuerung krude zu sein.

Ist nicht mit Sicherheit zu sagen. Gerade die Regelmäßigkeit in den fehlerhaften Werten ist merkwürdig. Hätte sonst gesagt, dass die Dinger irgendwo mit kollidieren und dann woanders hinschnappen... passt aber nicht so ganz. Aber mal eine andere Frage, mit der sich das Problem evtl. von selbst lösen könnte: Warum überhaupt Rigidbodys? Sehe hier irgendwie den Nutzen nicht so ganz. Du brauchst weder Krafteinwirkung, noch Kollisionsevents für die Elemente.

Link zu diesem Kommentar
Auf anderen Seiten teilen

Guten Morgen Sascha,
danke dir für deine Tipps.

vor 8 Stunden schrieb Sascha:

Auch wenn ich gerne auf die weiteren Fragen eingehe, muss ich direkt mal sagen, dass das nicht die beste Idee ist. Du hast ein klar definiertes Spielfeld, das sogar als Grid angeordnet ist. Bau dir eine passende Datenstruktur (vermutlich ein 2D-Array) und greife darauf zu. Das ist wesentlich eleganter, weniger fehleranfällig und viel weniger mit Kanonen auf Spatzen schießen als da die Physik-Engine für rauszukramen.

Ich bin für jeden Tipp dankbar. Mit Grids hatte ich bisher noch nicht das Vergnügen und Arrays kenne ich bisher nur als lineare Liste. Sprich, "bitte tue xy mit allen Dingen, die in dieser Liste stehen". Mein Gedankengang dazu war, dass ich dann für jede Reihe und jede Spalte entsprechende und sich überschneidende Listen benötigen würde. Und da sich aber die "Inhalte", so nenne ich es jetzt mal ja ständig ändern, müsste ich bei jedem Schritt mehrere Objekte aus mehreren Listen austragen und in andere eintragen lassen. Gleichzeitig müssten sie aber in der sich bewegenden Reihe selbst bleiben. Und als dann noch Überlegenden zu dem hinten rausfallenden Teil und dem vorne zusätzlich eingesetzten Teil hinzukamen, schwirrte mir langsam schon beim Gedankenspiel der Kopf, so dass mir ein direktes Auswählen der Reihe ohne "Sammlung" deren Inhalte als wesentlich einfacher erschien. Daher die Idee mit dem RayCast. Wenn du aber sagst, mit Grids und Arrays zu arbeiten würde in diesem Fall doch wesentlich besser, einfacher und Fehler-unanfälliger funktionieren, werde ich da doch mal nach Suchen und mich entsprechend einlesen. 

Ich habe da jetzt natürlich noch nicht nach gesucht. Weißt du aus dem Stehgreif ob hier kürzlich schon mal jemand mit Grids zu tun hatte bzw. kannst du mir eine passende Hilfestellung empfehlen? Gibt dafür ja sicherlich auch diverse Themen hier oder Unity-Tuts. Wenn nicht, nicht schlimm. Ich find schon was :)

vor 9 Stunden schrieb Sascha:

Warum überhaupt Rigidbodys?

Nein, an sich brauche ich die RBs nicht, das ist korrekt. Aber ich habe die Bewegung der Segmente anders nicht ansteuern können. Der RayCast hat zwar erkannt dass da ein GameObject im Weg ist, aber nichts damit gemacht. Die Bewegung kam erst mit dem Ansprechen eines RB in's Spiel. Aber gut, wenn das mit dem Array bzw. dem Grid funktioniert, dann dürfte sich ja wahrscheinlich auch das Thema RB erledigt haben...

Link zu diesem Kommentar
Auf anderen Seiten teilen

vor 1 Stunde schrieb Der_Stefan_:

Weißt du aus dem Stehgreif ob hier kürzlich schon mal jemand mit Grids zu tun hatte bzw. kannst du mir eine passende Hilfestellung empfehlen

Ja, schon :D

Auch hier gibt's verschiedene Ansätze, hier ist einer.

Ein 2D-Array erstellt man so (Komma in die eckigen Klammern, und zwei Dimensionen [width, height] beim Erstellen):

[SerializeField]
private int width = 10;
[SerializeField]
private int height = 10;
private LabyrinthElement[,] grid;

private void Start()
{
  grid = new LabyrinthElement[width, height];

Das musst du natürlich irgendwie füllen. Wenn du deine Elemente schon vor dem Spielstart im Editor platziert haben willst, dann musst du die alle Finden und einordnen. Aber ich glaube, bei deinem Spiel läuft es eher darauf hinaus, dass du eine Liste von Prefabs hast und die beim Spielstart instanziieren möchtest:

[SerializeField]
private GameObject[] prefabs = default;
[SerializeField]
private float elementSize = 12f;

private void Start()
{
  grid = new LabyrinthElement[width, height];

  for (var y = 0; y < height; y++)
  {
    for (var x = 0; x < height; x++)
    {
      var randomPrefab = prefabs[Random.Range(0, prefabs.Length)];
      var position = new Vector3(x, 0, y) * elementSize;
        
      grid[x, y] = Instantiate(randomPrefab, position, Quaternion.identity);
    }
  }
}

Hier ist noch ein Array mit allen Prefabs, die zufällig instanziiert werden können.

Die zweidimensionale For-Schleife läuft einmal über das gesamte Array und packt in jedes Element des Arrays eine Referenz auf ein instanziiertes Prefab, das außerdem gleich an der richtigen Stelle gespawnt wird. Hierfür werden einfach die sowieso vorhandenen Laufvariablen x und y verwendet.

Mit derselben Syntax können wir jetzt das Grid manipulieren:

public void MoveRow(int rowIndex, bool positive)
{
  if (rowIndex < 0 || rowIndex >= height) throw new System.IndexOutOfRangeException("Row #" + index + " doesn't exist");
  
  LabyrinthElement bufferedElement = null; // The element that is pushed out of the grid before it's added again on the other side
  
  if (positive)
  {
    for (var x = width - 1; x >= 0; x--)
    {
      var element = grid[x, rowIndex];
      if (x == width -1)
      {
        bufferedElement = element; // Remember the removed element
      }
      else
      {
        grid[x + 1, rowIndex] = element; // Store the element in the adjacent cell on the grid
        MoveToGridPosition(element.transform, x + 1, rowIndex); // Actually move the object to another position
          
        if (x == 0)
        {
          grid[x, rowIndex] = bufferedElement; // Put the remembered Element back
          MoveToGridPosition(bufferedElement.transform, x, rowIndex);
        }
      }
    }
  }
  else
  {
    for (var x = 0; x < width; x++)
    {
      var element = grid[x, rowIndex];
      if (x == 0)
      {
        bufferedElement = element; // Remember the removed element
      }
      else
      {
        grid[x - 1, rowIndex] = element; // Store the element in the adjacent cell on the grid
        MoveToGridPosition(element.transform, x - 1, rowIndex); // Actually move the object to another position
          
        if (x == width - 1)
        {
          grid[x, rowIndex] = bufferedElement; // Put the remembered Element back
          MoveToGridPosition(bufferedElement.transform, x, rowIndex);
        }
      }
    }
  }
}

private void MoveToGridPosition(Transform transform, int x, int y)
{
  transform.position = new Vector3(x, 0, y) * elementSize;
}

Hier ist natürlich unschön, dass da zwei komplette, aber sehr ähnliche Schleifen sind. Es gibt aber genügend Unterschiede (angefangen damit, dass die Schleifen in unterschiedliche Richtungen laufen), dass eine Lösung mit weniger Code-Duplizierung schon etwas komplex werden würde. Kann man sich aber mal ransetzen :)

Ich hoffe, der Code ist gerade mit den Kommentaren einigermaßen selbsterklärend. Die Methode ist nur für Zeilen, für Spalten braucht man nochmal eine ähnliche (wieder unschöne Code-Duplizierung, wieder aus Einfachheitsgründen). Bei Fragen gerne fragen.

Die Methode MoveToGridPosition ist interessant, weil man dort statt einer Positionsänderung einen Befehl zum Start eines Tweens einbauen könnte, sodass die Objekte sich schön bewegen anstatt sich zu teleportieren. Dann muss man natürlich schauen, was man mit dem Element macht, das auf der anderen Seite wieder reinkommt. Das soll ja nicht einmal quer durch die Reihe durch alle anderen Elemente einmal durchclippen. Hier gibt's beliebig Raum nach oben für cooles Design: Wenn du das richtig cool machen willst, kann man in dem Element drinstehen und merkt gar nicht, dass das Element das Grid zwischenzeitlich verlässt :D

Link zu diesem Kommentar
Auf anderen Seiten teilen

Okay... wow... Ganz lieben Dank dass du dir die Mühe gemacht hast und mir das so aufzuschlüsseln. Und du sagst, das ist einfacher ?! Ayayay... na ja gut, ich hab's mir so ausgesucht und jetzt muss ich da durch :D

Ab heute ist Lockdown, jetzt habe ich erst recht die Abende wieder frei um daran rumzubasteln. Ich bin selber sehr gespannt, ob ich das so umgesetzt bekomme. Jetzt gerade verspüre ich gerade doch einen ziemlichen Respept vor der Aufgabe :D

So oder so, ich gebe Bescheid was draus wird, bzw. wenn (nicht falls) noch offene Fragen bestehen :)

Link zu diesem Kommentar
Auf anderen Seiten teilen

vor 2 Stunden schrieb Der_Stefan_:

Und du sagst, das ist einfacher ?!

Naja, "einfacher" ist ein sehr komplexer Begriff :)

Jede Entscheidung, die du triffst, hat irgendwelche Konsequenzen für dein Projekt. Im Falle der Entscheidung, die Physik-Engine zu benutzen, um die zu verschiebenden Elemente zu ermitteln, hast du als Konsequenz das jede weitere Verwendung der Physik-Engine um diesen Umstand herum gebaut werden muss. Wenn dein Spieler oder irgendein anderes Objekt mit Collider also in einem Gang steht, musst du sicherstellen, dass dieses Objekt nicht dazwischenfunkt, wenn es potentiell vom Raycast getroffen wird. Wenn du irgendwann ein neues Labyrinth-Element zum Spiel hinzufügst und kurz vergisst, welche Form der Collider genau haben muss, dann kann es sein, dass dieses Element nicht richtig mitspielt. Das sind so die Quellen für Bugs in Spielen. Mit jeder Entscheidung, die in irgendeiner Weise nach dem Motto "naja, das funktioniert jetzt erstmal so" getroffen wird, baut man so genannte "Technische Schulden" (Tech Debt) auf, die sehr schwer wieder abzutragen sind.

Zum einen ist diese Lösung also sicherer und kann dir auf lange Sicht eine Menge Kopfschmerzen ersparen, zum anderen, ganze ehrlich... der komplexe Teil daran ist der, der so oder hätte implementiert werden müssen; das mit dem Verschieben :D

vor 2 Stunden schrieb Der_Stefan_:

Jetzt gerade verspüre ich gerade doch einen ziemlichen Respept vor der Aufgabe

Ja, das ist normal. Es kann helfen, die Aufgabe in lauter kleinere zu zerteilen und das auch irgendwo aufzuschreiben (z.B. auf einem Issue Board wie Trello).

  • Grid-Klasse bauen, die ein zweidimensionales Array hat
  • Grid mit Elementen befüllen
  • Zeilen und Spalten verschieben können
    • Irgendetwas bauen, womit der Spieler Zeile oder Spalte definieren kann
    • Code bauen, der über die betroffenen Elemente im Array iteriert

usw.

Dann wirkt das immer gleich viel schaffbarer.

Link zu diesem Kommentar
Auf anderen Seiten teilen

@Sasche: Aufbauende Worte 😘 Mit jedem Schritt und jedem Try und Fail wird`s ein kleines Stück selbsverständlicher. Ein schönes Gefühl, muss ich sagen.

Ich hab mich jetzt mit einer großen Tasse Kaffee vor den Computer gesetzt und dein Script in einer Klasse zusammengefügt. Die darauffolgende Stunde (fast 1 1/2 sogar) habe ich damit verbracht, mir Zeile für Zeile anzuschauen und zu verstehen, was, was, wie macht oder auslöst.

Am meisten überrascht hat mich eigentlich die Zeile ( public void MoveRow(int rowIndex, bool positive ), dass du da Int und Bool direkt in der Void (Methode, oder?) anlegen kannst. Ich dachte bisher, die Benennung dieser MUSS immer im Kopf, bzw. unabhängig von einem Void o.ä. geschehen. Ich kann es ja jetzt noch nicht direkt ausprobieren weil die Klasse noch nicht funktioniert, aber ich gehe davon aus, dass dieser bool z.B. dann auch nur innerhalb dieser Void funktioniert, richtig?

Eine Frage habe ich außerdem noch zu den zu ladenden Prefabs: Diese sind mit den eckigen Klammern deklariert ( private GameObject [ ] prefabs = default;  ). Auch das kann ich noch nicht ausprobieren, wahrscheinlich wird sich die Frage später von selbst erledigen, aber kann man durch die eckigen Klammern mehrere Objekte deklarieren (in meinem Fall Kurve, Gerade und T-Kreuzung), oder muss ich das später 3 mal setzen und die Klasse per Zufall darauf verweisen lassen?

Und - warum ich das Ganze noch nicht ausprobieren kann - über eine Sache stolpere ich noch (ich freue mich unglaublich darüber, dass das tatsächlich die einzige Sache ist, die ich mir nicht herleiten oder erklären kann  :D:D :D ):

Unity bemängelt, dass das "LabyrinthElement" aus dem Kopf ( private LabyrinthElement[ , ] grid; ) nicht gefunden werden kann. Meinem Verständnis nach sollte das doch aber als Oberbegriff für die jeweiligen Labyrinth-Bausteine dienen, welche in der Start-Funktion mit dieser Bezeichnung instanziert werden. Will sagen, kein haptisches Objekt, sondern lediglich ein Name um sich darauf beziehen zu können. Daher kann ich ja auch nichts verknüpfen oder irgendwohin verweisen, oder? Ich habe versucht zu ergründen (Google, YouTube) ob dennoch irgendwo irgendein Verweis fehlt. Jedoch wurde bei CodeMonkey und Co nirgends Prefabs in die aufgesetzten Grids geladen, so dass dieser Punkt nirgends beschrieben ist (oder ich hab zumindest nichts gefunden). Auch mit diversen möglichen Versuchen das zu ändern und damit zu kordieren bin ich auf keinen grünen Zweig gekommen. Daher muss ich dich leider doch noch mal mit der Bitte um Hilfe nerven .. :) 

 Lieben Gruß

Der Stefan

 

 


 
Link zu diesem Kommentar
Auf anderen Seiten teilen

vor 7 Stunden schrieb Der_Stefan_:

(Methode, oder?)

Jau, Methode ist immer richtig in C#.

vor 7 Stunden schrieb Der_Stefan_:

Ich dachte bisher, die Benennung dieser MUSS immer im Kopf, bzw. unabhängig von einem Void o.ä. geschehen

Nein, es gibt sozusagen zwei bis drei (je nach Betrachtung) Arten von Variablen: Felder (das sind Variablen, die Teil einer Klasse/eines Structs sind und damit ein Teil der Daten des Objekts beinhalten, und lokale Variablen, die nur* innerhalb eines Methodenaufrufs existieren. Parameter kann man als drittes Zählen: Sie sind wie lokale Variablen, stehen aber in der Signatur der Methode und benötigen dadurch beim Methodenaufruf Werte.

In deinem Code benutzt du ja Debug.DrawRay, Debug.Log und Physics.Raycast - da übergibst du auch Werte. Dass die Methoden diese Werte verlangen, ist genau auf diese Art deklariert worden.

*Ausnahmen gibt's immer ;)

vor 7 Stunden schrieb Der_Stefan_:

aber kann man durch die eckigen Klammern mehrere Objekte deklarieren

Genau das. Unity zeigt bei so etwas dann eine in der Länge verstellbare Liste an, die du dann füllen kannst.

vor 7 Stunden schrieb Der_Stefan_:

Unity bemängelt, dass das "LabyrinthElement" aus dem Kopf ( private LabyrinthElement[ , ] grid; ) nicht gefunden werden kann

Genau, die Klasse gibt's nicht. Die musst du selber einfügen. Erstmal reicht sowas:

public class LabyrinthElement : MonoBehaviour
{

}

Das kommt dann auf die Prefabs drauf.

Wenn man eine Komponente nimmt statt "GameObject", dann kann man alle GameObjects (und Prefabs sind einfach nur GameObjects) reinziehen, die diese Komponente haben. Damit verhinderst du in diesem Fall, dass man einen Baum oder einen Tisch reinziehen kann, sondern wirklich nur Labyrinth-Elemente. Damit hast du dann auch Sicherheit im Bezug auf Funktionalität. Wenn du in diese Komponente z.B. eine Methode reintust, die eine schöne Animation beim Bewegen macht (nennen wir sie MoveToAnimated), dann kannst du diese auch mit Sicherheit aufrufen, weil du ja keine GameObjects reinziehen kannst, die diese Komponente nicht haben:

public LabyrinthElement prefab; // Als Beispiel nur ein einzelnes

private void Start()
{ 
  var instance = Instantiate(prefab);
  
  instance.MoveToAnimated( ... );
}

In diesem Beispiel referenziert die Variable "instance" nicht das GameObject, das Instantiate() erstellt, sondern direkt die LabyrinthElement-Komponente, die darauf liegt. Dieser Code ist also in etwa identisch zu:

public GameObject prefab;

private void Start()
{ 
  var instance = Instantiate(prefab);
  
  instance.GetComponent<LabyrinthElement>().MoveToAnimated( ... );
}

Hier kannst du ein GameObject reinziehen, und Instantiate gibt entsprechend eine Referenz auf das GameObject zurück. Von diesem kann man mit GetComponent bestimmte Komponenten finden. Aber wenn diese Komponente gar nicht darauf vorhanden ist, dann kracht's an der Stelle! Somit ist die obere Variante durch ihre einschränkende Natur sicherer.

Lange Rede, kurzer Sinn: Darum habe ich immer Komponenten für alle möglichen Arten von Objekten, damit klar ist, was für ein Objekt man da reinziehen sollte. Und leer bleiben diese Klassen sowieso nie lange.

Link zu diesem Kommentar
Auf anderen Seiten teilen

  • 2 weeks later...

Ich check`s einfach nicht...

seit dem Aufkommen des Problems, bastel ich jetzt seit geschlagenen 2 Wochen nur noch da dran rum und bin nicht einen einzigen Schritt weitergekommen. So langsam kommt da echt der Frust auf und ich muss ich mir wohl oder übel eingestehen, dass ich bei diesem Projekt einfach zu hoch gestapelt habe und das vielleicht einfach noch ein paar Monate liegen lasse, bis ich etwas besser geworden bin... 

Seit deinem letzten Post, Sascha, kann ich die Klasse gefühlt auswendig runterlaiern, es funktioniert aber dennoch nicht. 

Auch habe ich in der Zwischenzeit den Code komplett verworfen und versucht von Null ein neues Array selbst zu bauen. Einfach um es besser verstehen zu können. Hat sogar auf Anhieb geklappt. Allerdings konnte ich dadurch noch immer nicht das Grundsätzliche Problems dieses Threads lösen. Wie ich einzelne Reihen/ Spalten identifiziere und gemeinsam und bewege.

 

Wie gesagt, im Bereich des 2D-Array fängt`s schon an (ich hab mal die Probleme und Alternativen als Kommentare in den Code geschrieben:

private void Start()
    {
        GameObject[,] grid = new GameObject[width, height];
		//LabyrinthElement[,] grid = new LabyrinthElement[width, height];
		// In der Variante mit dem GameObject wird das Labyrinth zwar aufgebaut, hat aber die Teile haben so keinenVerweis auf den Namen "LabyrinthElement". Du hast ja gesagt, dass ist nur dafür dass das nur dazu da ist, dass nur diese ganz bestimmten Prefabs geladen werden können. Daher... muss das unbedingt sein?

        for (var y = 0; y < height; y++)   //for (int x = 0; x < width; x++)  //
        {
            for (var x = 0; x < height; x++) //for (int z = 0; z < width; z++)  //
            {
                var randomPrefab = prefabs[Random.Range(0, prefabs.Length)];
                var position = new Vector3(x, 0, y) * elementSize;
                var randomRot = Quaternion.Euler(0, Random.Range (0, 4 ) * 90, 0);
                
                grid[x, y] = Instantiate(randomPrefab, position, randomRot);     
				//Instantiate(randomPrefab, position, randomRot);
				//Hier andersrum: Wenn ich den Teil "grid[x,y] weglasse, funktioniert es mit dem Namen LabyrinthElement weiter oben und das Labyrinth baut sich ebenfalls auf. Aber die Prefabs werden dann ja nicht Teil des Oberbegriffs "grid" oder...?
            }
        }
    }

Mir scheint, dass da irgendwas zu viel bzw. doppelt abgefragt wird und dadurch kollidiert. Aber je nach Fehlermeldung scheint es entweder das "grid" oder das "LabyrinthElement" zu sein. 

 

 Und bei der Methode MoveRow bekomme ich, wenn ich sie aufrufe nur ganz schlicht nur die Meldung, dass Unity sie nicht laden kann. Keine weitere Erklärung... Unbenannt.thumb.JPG.8f3caad03c9a5bef20d47e856516a5fe.JPG

Link zu diesem Kommentar
Auf anderen Seiten teilen

vor 13 Stunden schrieb Der_Stefan_:

So langsam kommt da echt der Frust auf und ich muss ich mir wohl oder übel eingestehen, dass ich bei diesem Projekt einfach zu hoch gestapelt habe und das vielleicht einfach noch ein paar Monate liegen lasse, bis ich etwas besser geworden bin...

Das ist leider manchmal so. Ein bisschen Pause kann da tatsächlich helfen. Hauptsache, du lässt dich nicht demotivieren. Irgendwann wird es klappen.

vor 13 Stunden schrieb Der_Stefan_:

Auch habe ich in der Zwischenzeit den Code komplett verworfen und versucht von Null ein neues Array selbst zu bauen. Einfach um es besser verstehen zu können.

Das war eine hervorragende Idee 👍

vor 13 Stunden schrieb Der_Stefan_:

Hat sogar auf Anhieb geklappt.

Geil :D

vor 13 Stunden schrieb Der_Stefan_:

Allerdings konnte ich dadurch noch immer nicht das Grundsätzliche Problems dieses Threads lösen. Wie ich einzelne Reihen/ Spalten identifiziere und gemeinsam und bewege.

Ja, das ist heftig.

vor 13 Stunden schrieb Der_Stefan_:

// In der Variante mit dem GameObject wird das Labyrinth zwar aufgebaut, hat aber die Teile haben so keinenVerweis auf den Namen "LabyrinthElement". Du hast ja gesagt, dass ist nur dafür dass das nur dazu da ist, dass nur diese ganz bestimmten Prefabs geladen werden können. Daher... muss das unbedingt sein?

Nein, nicht unbedingt. Es gibt dir halt wie gesagt die zusätzliche Sicherheit, dass da nicht eine Fackel oder der Spieler reingezogen werden können, und meistens spart man sich dann noch jeweils ein GetComponent, wenn man dann mit der Komponente arbeiten will.

Was den "Keinen Verweis" angeht, meinst du damit, dass du nicht meinDingsbums.LabyrinthElement (oder sowas) schreiben kannst? Da brauchst du GetComponent für, oder du benutzt einfach direkt LabyrinthElement für dein Prefab-Array. Hier mal ein paar Beispiele, vielleicht helfen die.

Hier kann man jedes beliebige GameObject (aus der Szene oder den Assets, also Prefabs) reinziehen:

public GameObject prefab;

private void Start()
{
  GameObject instance = Instantiate(prefab);
}

Instantiate gibt dann eine Referenz auf das neue GameObject zurück, das es durch Kopieren des Prefabs erstellt hat. Deshalb kann man die Referenz in eine Variable vom Typ "GameObject" schreiben. Wenn man jetzt mit Komponenten auf diesem Prefab arbeiten will, braucht man GetComponent:

public GameObject prefab;

private void Start()
{
  GameObject instance = Instantiate(prefab);
  Light instanceLight = instance.GetComponent<Light>(); // <-- !
  instanceLight.color = Color.red;
}

Damit kannst du dir von dem GameObject alle Komponenten geben lassen. Sollte das Prefab, und damit die Instanz, keine Light-Komponente haben, dann gibt GetComponent null zurück und in der letzten Zeile kracht es dann.

Wenn du statt GameObject als Typ eine Komponente nimmst, dann kann man wie gesagt weiterhin GameObjects da reinziehen, aber eben nur die, die die Komponente auch haben:

public Light lightPrefab;

private void Start()
{
  Light instanceLight = Instantiate(lightPrefab);
}

Instantiate baut hier wieder eine Kopie des gesamten GameObjects, gibt aber nicht die Referenz darauf zurück, sondern auf die "Light"-Komponente. So spart man sich das GetComponent. Wenn man jetzt davon das GameObject haben will, nutzt man die "gameObject"-Eigenschaft:

GameObject instance = instanceLight.gameObject;

So kann man mit "GetComponent<>()" eine Komponente eines GameObjects kriegen und mit "gameObject" das GameObject einer Komponente.

Bei Arrays ist das dann genauso:

public GameObject prefab;
public GameObject[] instances; // Wir machen das mal public, dann kann man das Ergebnis einfacher im Inspektor ansehen

private void Start()
{
  instances = new GameObject[3];
  
  instances[0] = Instantiate(prefab);
  instances[1] = Instantiate(prefab);
  instances[2] = Instantiate(prefab);
}

Oder eben mit Komponente als Typ:

public Light prefab;
public Light[] instances;

private void Start()
{
  instances = new Light[3];
  
  instances[0] = Instantiate(prefab);
  instances[1] = Instantiate(prefab);
  instances[2] = Instantiate(prefab);
}

 

vor 13 Stunden schrieb Der_Stefan_:

Und bei der Methode MoveRow bekomme ich, wenn ich sie aufrufe nur ganz schlicht nur die Meldung, dass Unity sie nicht laden kann. Keine weitere Erklärung...

Das ist extrem merkwürdig. Wie sieht denn dein Code aus, der die Methode aufrufen soll?

Link zu diesem Kommentar
Auf anderen Seiten teilen

vor 23 Stunden schrieb Sascha:

Das ist extrem merkwürdig. Wie sieht denn dein Code aus, der die Methode aufrufen soll?

Erstmal nur einfach über einen Invoke.

public void Update()
    {
        if (Input.GetKeyDown (KeyCode.Space))
        Invoke ("MoveRow",1); 
    }

Weiter kam ich ja noch gar nicht, weil ich den Schritt davor, das Erstellen noch nicht hinbekommen hab. Hab ich mich halt auch noch nicht näher mit beschäftigt, weil ich mir dachte, dass das Problem hier durch den Teil davor verursacht wird... Unity spuckt jedenfalls die folgende Meldung  in der Konsole aus (siehe Bild in meinen Beitrag vorher): "Trying to Invoke method: GridClaas.MoveRow couldn't be called."

Link zu diesem Kommentar
Auf anderen Seiten teilen

Invoker-Klasse

vor 23 Minuten schrieb Der_Stefan_:

Erstmal nur einfach über einen Invoke.

Hab ich's doch gewusst :D

Invoke ist Kacke, genau wegen solcher Dinge. Entweder, du hast dich bei "MoveRow" vertippt (und der Compiler kann dir da nichtmal helfen, weil das ein doofer String ist), oder du hast da Parameter in der Methode, was Invoke schonmal gar nicht kann. Ich nehme an, du benutzt da meine MoveRow-Methode, die braucht ja noch einen int und einen bool - ohne kann man sie nicht aufrufen. Invoke versucht quasi das:

MoveRow();

Aber das muss ja so aussehen:

MoveRow(3, true);

3 und true sind natürlich nur Platzhalter. Der Punkt ist aber, dass Invoke keine Parameter kann, man aber eine Methode mit Parametern nicht ohne Parameterwerte aufrufen kann. Kannst ja auch nicht deinem Taschenrechner sagen, er solle mal addieren, ihm aber dafür keine Zahlen geben ;)

Wenn du etwas nach x Sekunden aufrufen willst, empfehle ich dir meine Invoker-Klasse. Die packst du irgendwo gemütlich in deine Assets und kannst sie benutzen. Damit sieht das so aus:

StartCoroutine(Invoker.Invoke(MoveRow, 1));

Ist ein bisschen länger, aber dafür ohne String. Außerdem kannst du Parameter benutzen:

StartCoroutine(Invoker.Invoke(() => MoveRow(3, true), 2));

Ich weiß, sieht ein bisschen krass aus. Nennt sich "Lambda-Ausdruck". Nicht verwirren lassen, der wichtige Punkt ist, dass da die beiden Parameter drinstehen.

Link zu diesem Kommentar
Auf anderen Seiten teilen

Archiviert

Dieses Thema ist jetzt archiviert und für weitere Antworten gesperrt.

×
×
  • Neu erstellen...