Jump to content
Unity Insider Forum

Gegner bewegung


Tintenklecks

Recommended Posts

Hallo Leute!

Ich hoffe, Ich bin hier jetzt im richtigen Forum Teil!

Und zwar:  Ich arbeite an einem 2D Plattformer und hätte gerne, das wenn sich 2 Bewegte Obecte, beispielsweise 2 Gegner, wenn diese sich begegnen an einander Vorbei laufen. Hätte aber auch gerne, das diese herunter fallen, wenn sie den Boden nicht mehr berühren. Leider bekomme ich das nicht hin. Endweder, Die beiden Stoßen ineinander, oder sie bewegen sicht überhaupt nicht mehr.

Ich habe versucht, ihnen über eine Transformkomponente zu ihren Füßen, die Prüft, ob sie auf dem Boden berühren probiert. Der Plan war, das sie fallen, solange sie sich in der Luft befinden, und sobald sie mit dem Boden Collidieren, das sie aufhöhren zu fallen. Den Boden erkennen sie zwar, beginnen aber nicht zu fallen, solange sie über diesem Schweben.

Vermutlich hab ich irgendwo einen totalen Denkfehler drinnen. Kann mir da jemand Helfen, den Knoten zu lösen?

Link zu diesem Kommentar
Auf anderen Seiten teilen

Warum nutzt du nicht einfach Rigidbody2Ds? Die sind doch genau für "Dinge berühren" und "Runterfallen" da?

Wenn du jetzt willst, dass sie sich nicht mehr gegenseitig berühren, dann packe sie auf einen gemeinsamen Layer und deaktiviere die Kollision zwischen Objekten dieses Layers in der Layer Collision Matrix.

Wie das geht, steht hier: https://docs.unity3d.com/Manual/LayerBasedCollision.html

Link zu diesem Kommentar
Auf anderen Seiten teilen

Bei diesen Layern (nicht zu verwechseln mit Sorting Layern) geht das tatsächlich nicht. Der Grund dafür ist, dass Layer und LayerMasks 32-bit integers sind. Also 32-mal eine 0 oder eine 1 hintereinander. Die benutzt Unity dann, um sehr schnell durch bitweises Vergleichen festzustellen, ob zwei Dinge kollidieren oder ein Raycast trifft. Theoretisch könnte Unity auf 64-bit upgraden, gibt's aber eigentlich keinen Grund für. Wenn man Layer richtig benutzt, braucht man nicht viele davon.

Link zu diesem Kommentar
Auf anderen Seiten teilen

Ich bin ja noch absoluter Anfänger, und ich wollte da einfach mal nachfragen. Ich habs jetzt auch nicht gedacht, aber ich will ja versuchen, zu verstehen wie Unity funktioniert.

Ich hab jetzt zwar mal versucht, herauszufinden, was ein Raycast ist, aber hab dazu nur verwirrendes Zeug gefunden, und ihr erklährt die sachen immer soooooo Toll...?:D

Link zu diesem Kommentar
Auf anderen Seiten teilen

Ein Raycast ist das Abschießen eines Strahls (Ray = Strahl, Cast = werfen). Du kannst dir das wie einen Laserstrahl vorstellen, der irgendwo losgeht und in dann in irgendeine Richtung zeigt. Worauf auch immer der Lichtpunkt fällt, ist vom Raycast "getroffen".

Ein Gameengine-Raycast ist natürlich unsichtbar. Bei Unity kann er nur Collider treffen (bzw. nur Collider2D wenn du Physics2D.Raycast benutzt). Ein sichtbares Objekt wird also nicht zwangsläufig getroffen, wenn an der Stelle, wo der Strahl langgeht, kein Collider ist. Andersherum kann der Raycast unsichtbare Wände treffen, da diese ja einen Collider haben.

Physics.Raycast, also der 3D-Raycast, gibt mit einem boolean zurück, ob irgendetwas getroffen wurde (getroffen = true, in's Leere gegangen = false). Mit dem optionalen RaycastHit-Parameter kann man sich mehr Infos geben lassen, zum Beispiel welcher Collider getroffen wurde, an welcher Stelle und wie der Aufprallwinkel des Strahls ist.

RaycastHit hit;
if(Physics.Raycast(start, direction, out hit))
{
  Debug.Log("Getroffen: " + hit.collider.gameObject.name);
}
else
{
  Debug.Log("Nix getroffen.");
}

Physics2D.Raycast dagegen gibt einfach direkt einen RaycastHit2D zurück, auch wenn nichts getroffen wurde. Wenn wirklich nichts getroffen wurde, hat die Eigenschaft RaycastHit2D.collider den Wert null.

RaycastHit2D hit = Physics2D.Raycast(start, direction);
if(hit.collider != null)
{
  Debug.Log("Getroffen: " + hit.collider.gameObject.name);
}
else
{
  Debug.Log("Nix getroffen.");
}

Raycasts werden für extrem viele Dinge benutzt. Man kann damit berechnen, wo ein Schuss einschlägt.

Wenn ein Objekt an einer zufälligen Position spawnen soll, kannst du eine X-Position (bei 3D X und Z) aussuchen und mit einen Raycast aus dem Himmel vertikal nach unten schauen, auf welchem Punkt auf dem Boden das Objekt spawnen soll. Würdest du sowohl X und Y zufällig aussuchen, würde das Objekt wahrscheinlich in der Luft spawnen, oder irgendwo drin.

Eine KI kann mit Raycasts abschätzen, wo sie sich langbewegen soll. Oder ob sie den Spieler sehen kann.

Theoretisch kann man mit einem Raycast auch schauen, ob ein Charakter den Boden berührt, um zu ermitteln, ob er springen kann. Allerdings würde ich dafür eher SweepTests empfehlen. Ist aber ein anderes Thema.

Übrigens: Die Zukunft der 3D-Grafik läuft auch über Raycasts. Die ersten brauchbaren Realtime-Pathtracer sind schon in der Mache. Weil Raycasts und wie sich reales Licht verhält in der Grundidee mehr oder weniger dasselbe ist.

Link zu diesem Kommentar
Auf anderen Seiten teilen

Das klingt sehr interresant!

Weil du das mit dem Boden Überprufen erwähnt hast, ich mache in meinem 2d Plattformer so, das ich an des Füßen meines Characters ein extra Child Object hab, welches überprüft, ob sich der Boden in dessen unmittelbaren umgebung befindet. Sobald das der Fall ist, wird eine Variable "Boden" auf true gesetzt.  Mit hilfe derer ermittle ich unter anderem, ob der Char springen kann, und welche Animation gespielt werden soll, da es bei mir sowol eine "Sprung" Animation, als auch eine "Fall" animation gibt, die endweder unmittelbar nach der SprungAnimation ausgeführt wird, oder eben, wenn man beispielsweise eine Kante hinunter läuft, also einfach fällt.

 

Könnte man eigentlich mit hilfe eines Raycasts dann einen Charakter prüfen lassen, wie lang die Plattform ist, auf der er sich bewegt, um herauszufinden, ob sich dieser Umdreht oder nicht?

Link zu diesem Kommentar
Auf anderen Seiten teilen

vor 11 Stunden schrieb Tintenklecks:

Könnte man eigentlich mit hilfe eines Raycasts dann einen Charakter prüfen lassen, wie lang die Plattform ist, auf der er sich bewegt, um herauszufinden, ob sich dieser Umdreht oder nicht?

Ja, das geht. Solange du wirklich nur schauen willst, wann er sich umdreht. Messen, wie lang die Plattform ist, ist schwierig. Messen, ob man gerade an einem Abgrund steht, geht gut. Einfach in Laufrichtung neben dem Character einen Raycast starten, der nach unten geht. Den begrenzt du dann noch in der Länge (dafür gibt's einen optionalen Parameter), sodass er nur bis unter die Plattform schaut, ob etwas getroffen wird. Wenn nicht, bist du am Rand und kannst dich umdrehen.

Link zu diesem Kommentar
Auf anderen Seiten teilen

Danke für die Geduldige erklärungen. Hätte noch ne klitze kleine frage, und zwar. Man nehme an, der Gegner soll z.B. 1 Meter hoch Springen können. Wenn jetzt der Gegner an einer Kante Steht, kann man dann mittelst Raycast herausfinden, wie viel tiefer die untere Plattform ist, der gegner, wenn diese weniger als 1 meter tiefer liegt runter springt,. wenn die weiter weg ist, das er dann umdreht?

Bei der Ganzen fragerei geht es tatsäcjlich ausschließlich darum, zu ermitteln, wann der Gegner umdreht, oder gegebenenfals, fals man das damit realisieren kann auch eine Klippe hinauf oder hinunter springen kann. Muss sagen, das ich das für mein Spiel putzig fänd, wenn man das halbwegs einfach realisieren kann. Ansonsten find ich es unheimlich interresant dieses ganze spiele Entwickeln.

Link zu diesem Kommentar
Auf anderen Seiten teilen

Ja, sollte gehen. Und wenn nicht mit Raycasts, dann mit den verwandten Methoden davon.

Allerdings kannst du als Faustregel rechnen, dass wenn die Beschreibung des Problems doppelt so viel Text braucht, die Lösung doppelt so komplex wird. Wenn du eine Plattform checken willst, dann willst du vermutlich nicht nur Checken, ob neben der Figur etwas im weg ist (1) und ob auf Sprunghöhe dann nichts mehr im Weg ist (2), sondern auch, dass auf der Plattform genügend Platz für die Figur ist (3).

1. ist relativ einfach, solange deine Hindernisse nicht zu komplex sind. Wenn du z.B. einen Raycast 0.5 Meter über dem Boden zur Seite losschickst - und in deinem Hinternis zufälligerweise da ein Loch von links nach rechts durchgeht, sodass der Strahl das Hindernis nicht trifft - ist alles gut.

2. Wäre mit einem zweiten Raycast zur Seite möglich, der dann auf derselben Höhe, aber plus eine Figurhöhe (also z.B. 2.0m + 0.5m) losgeschickt wird. Es wird dann umgekehrt geguckt, ob er nichts trifft statt dass er etwas trifft. Alternativ ginge vielleicht ein Collider2D.Raycast . Das ist ein Raycast, der alle Collider bis auf einen ignoriert. Du kannst damit den Collider, den der erste Raycast getroffen hat, von sehr weit oben beschießen und so feststellen, wo die Oberkante liegt. Geht aber wiederum nur, wenn da niemals mehrere Collider aufeinander geschichtet sind, sodass die tatsächliche Oberkante eigentlich einem anderen Collider gehört.

Siehst schon, wird immer komplizierter. Je nach dem, wie hochwertig deine Lösung sein soll.

3. wird dann nochmal netter. Wenn du die Oberkante gefunden hast, kannst du von ganz-knapp-darüber einen Raycast nach oben losschicken und schauen, wie hoch die Decke ist. Dass das gut funktioniert, hängt davon ab, wie gut 2. implementiert ist.

Die ganze Sache hängt stark davon ab, wie deine Welt aussieht. Wenn du jetzt z.B. eine Welt hast, die ausschließlich aus Tiles besteht, also lauter gleichgroßen Feldern, die entweder blockieren oder nicht, wird es viel einfacher. Vor allem, wenn Figuren nicht größer als ein Feld sind. Dann checkst du auf dem Feld neben der Figur ob da was ist, und wenn ja auf dem Feld darüber ob da nichts ist.

Wenn dein Spiel aber nicht zufälligerweise so aussieht, wird's graduell komplizierter, wie oben beschrieben. Da ist dann puzzle-artige Problemlösung gefragt. Schau dir mal Physics2D an und was da außer Raycast noch für Methoden sind. Dann kriegst du einen Überblick dafür, was für Werkzeuge dir zur Verfügung stehen.

Link zu diesem Kommentar
Auf anderen Seiten teilen

Der Boden selber besteht aus Tiles die 100x100 Pixel groß sind. Der Rest der welt ist nur dekoration, die ansich meist nur aus Sprites oder kleinen Animationen besteht, die häufig nicht mal collider besitzen (abgesehen von ein paar grasbücheln, die einfach, sobald diese passiert werden kurz schneller "Wackeln", als wäre eben jemand daran vorbei gewandert.) um das ganze etwas ansprechender zu gestallten. (Ein Baum, der im Hintergrund vorbei Zieht, oder ein kleines Blühmchen, das im Vordergrund wächst.)

Das es Komplitzierter wird, je Komplexer das Spiel ist, hab ich schon beim Gammaker gemerkt. Und da ich erstmal ein halbwegs einfaches spiel haben möchte, bei dem ich möglichst viele funktionen einfach kennen lernen möchte, hab ich einfach mal nachfragen wollen, wegen der Einfachheit des ganzen. Aber ich glaub, ch probiers einfach mal aus, wenn ichs dann doch noch nicht schaffe, frag ich endweder nochmal nach, oder stell das vorher wieder her.

Danke für deine Viele Geduld!

 

edit: Kleine Frage: Wie genau nutze ich denn den Raycast? Solche dinge wie: Richtung, Entfernung,  mintiefe und maxtiefe verstehe ich ja (Lass mir die Seite auf deutsch übersetzen, da ich kaum bis kein English verstehe), die sind für mich selbsterklärend. Aber den Ergebnis-Array die Ebenenmaske und den Kontaktfilter verstehe ich nicht ganz.

Trage ich bei der Ebenenmaske die Collider2D ein, auf die er Reagieren bzw, auf die er achten soll ein? Und der Ergebniss-Array, sagt der mir, wie viele solcher Collider er in seiner Reichweite berührt? Und Spuckt dann der Kontaktfilter die Ergebnisse aus, die ich gerne wüsste?

Nutze ich dann das Ergebnis in einer if-abfrage? so nach dem Motto: WENN der Raycast etwas berührt, DANN, WENN Bitte schau doch mal, ob du da Hoch hoppeln kannst DANN: drehe um oder Hobbel rauf?

Link zu diesem Kommentar
Auf anderen Seiten teilen

Die LayerMask (Ebenenmaske) ist relativ selbsterklärend, wenn du folgendes ausprobierst:

public LayerMask mask;

und dann mal anschauen wie das im Editor aussieht. Du setzt einfach Häckchen vor alle Layer, die er beachten soll. Jedes GameObject hat ja genau einen Layer zugewiesen (nicht zu verwechseln mit Sorting Layer, das ist was anderes). Durch die Collider, die auf GameObjects liegen, deren Layer nicht Teil der Maske ist, geht der Raycast glatt durch.

Obwohl in der Doku steht, dass da ein int erwartet wird, kannst du eine LayerMask übergeben, weil diese sich implizit zu int casten lässt.

ContactFilter2D ist einfach nur ein Struct, in das du deine ganzen Filter bündeln kannst:

var filter = new ContactFilter();
filter.SetLayerMask(mask);
filter.SetDepth(-10, 10);

Physics2D.Raycast( ..., filter, ...);

Was das result-Array angeht, bin ich auch ein bisschen überfragt. Ein Raycast kann eigentlich immer nur ein Ergebnis zurückliefern, also nur einen Collder treffen. Um nach dem Treffer weiterzufliegen und ggf. noch mehr Collider zu treffen, benutzt man RaycastAll. Von daher... keine Ahnung :D

vor 5 Stunden schrieb Tintenklecks:

Nutze ich dann das Ergebnis in einer if-abfrage? so nach dem Motto: WENN der Raycast etwas berührt, DANN, WENN Bitte schau doch mal, ob du da Hoch hoppeln kannst DANN: drehe um oder Hobbel rauf?

Klingt gut!

var hit = Physics2D.Raycast( ... );
if(hit.collider)
{
  DoSomethingWith(hit);
}

 

Link zu diesem Kommentar
Auf anderen Seiten teilen

OK... Ich glaub, so ganz verstehe ich das ganze mit den Raycasts noch nicht... Vielleicht hab ich den aber auch einfach nur falsch angelegt?

if(lookright) { RaycastHit2D hit = Physics2D.Raycast(transform.position, new Vector2(0.03f, 0), 0.1f, Boden); }
else if (!lookright) { RaycastHit2D hit = Physics2D.Raycast(transform.position, new Vector2(-0.03f, 0), 0.5f, Boden); }

if(hit && laufen)
{
	Drehen();
}

Ist alles in der FixUpdate.

Kann man den Raycast auf irgendeine Art und weiße "Sichtbar" machen, einfach um zu sehen, wo sich der überhaupt befindet? Hatte damit nämlich auch schon probleme. Da waren dann meine sachen einfach mal eben IRGENDWO in der Welt versteckt, nur nicht da, wo ich sie gerne haben wollte.

Ich glaub, ich hab das ganze doch noch weniger verstanden, als ich gedacht habe...

Wenn ich in der if abfrage hit auf true lasse, ignoriert er es einfach grandiös, und läuft gerade aus weiter. Wenn ich in der if abfrage das hit auf false setze macht mein gegner einen auf dancing Star.

Link zu diesem Kommentar
Auf anderen Seiten teilen

Dein Code, wie du ihn da hast, kann eigentlich gar nicht kompilieren. Naja, nicht alleine. Der Grund: Variablen leben nur innerhalb des Rumpfs (der Bereich zwischen den { } ), in dem sie deklariert werden.

{
  int zahl = 5;
  print(zahl); // Alles ok. "zahl" existiert hier noch.
}
print(zahl); // Fehler! "zahl" existiert hier nicht mehr.

Wozu genau dieser Rumpf gehört (if, while, for, garnix, Methode...), ist dabei völlig wurscht.

Dein "hit" der da in der if-Abfrage abgefragt wird exsitiert also gar nicht, es sei denn, du hast noch eine Variable, die so heißt.

int zahl = 5;

void Start()
{
  {
    int zahl = 10;
  }
  print(zahl); // Wird 5 ausgeben, weil es sich auf die obere Deklaration bezieht
}

Dieser Fall stellt keinen Syntaxfehler dar, ist aber aus offensichtlichen Gründen potentieller Verwirrung zu vermeiden.

Wir haben hier zwei Variablen namens "zahl". Die "zahl"-Variable, deren Wert 10 ist, existiert nur innerhalb ihres kleinen Rumpfes. Das "print" danach kann sich deshalb nur auf die andere Variable desselben Namens beziehen. Daher wird 5 ausgegeben.

Ich denke mal, genau das passiert hier. Du hast irgendwo weiter oben einen RaycastHit2D deklariert und dein if bezieht sich darauf. Diese andere "hit"-Variable muss weg.

Und damit deine richtige Variable angesprochen werden kann, muss sie außerhalb des if-Rumpfes deklariert werden, damit sie nach dem if noch existiert. Der Trick: Den Wert zuweisen kannst du trotzdem im if-Rumpf, denn dieses ganze Lebensdauer-Gedöns gilt nur für Deklarationen, nicht Werte.

RaycastHit2D hit;

if(foo)
{
  hit = Physics2D.Raycast( ... );
}
else
{
  hit = Physics2D.Raycast( ... );
}

if(hit.collider && laufen)
{
  ...
}

Bemerke übrigens, dass Physics2D.Raycast niemals null zurückgibt. Daher wird

if(hit)
// was dasselbe ist wie
if(hit != null)

immer den Rumpf darunter ausführen.

Wenn du abfragen willst, ob ein 2D-Raycast etwas getroffen hat, musst du "hit.collider" checken. Steht so in der Anleitung ;)

if(hit.collider)

Was die Visualisierung angeht: Ja, das geht. Dafür hat Unity die Events OnDrawGizmos und OnDrawGizmosSelected. Das erste malt Debug-Dinge in die Szene View (nicht ins Spiel selbst!), das zweite ebenso, aber nur, wenn das Objekt, auf dem das Skript liegt, markiert ist. Oder einer seiner Parents. Innerhalb dieser Methoden kannst du die Gizmos-Klasse benutzen, um alles mögliche zu zeichnen. Beispiel:

void OnDrawGizmosSelected()
{
  Gizmos.color = Color.blue; // Warum nicht
  Gizmos.DrawRay(transform.position, new Vector2(0.2f, 0));
}

Zuletzt noch: Deine Parameter im Raycast sind etwas verwirrend. Du hast:

  • origin: Bei dir transform.position. Das heißt, der Raycast geht genau beim Pivot des Objekts los. Das klingt erstmal okay.
  • direction: Bei dir jeweils ein Vector3 nach links oder rechts, mit der Länge 0.03f. Das ist etwas komisch, weil die Länge dieses Vektors egal ist. Die maximale Reichweite des Raycasts bestimmst du mit dem nächsten Parameter:
  • distance: 0.1f oder 0.5f. Warum schaut deine Figur nach links und rechts unterschiedlich weit?
Link zu diesem Kommentar
Auf anderen Seiten teilen

Erstmal ein ganz ganz dickes Dankeschön an dich, das du so Unglaublich geduldig und ausführlich alles erklährst! Das hilft mir, und bestimmt vielen anderen hier, unglaublich weiter.

So. Das mit den Rumpfen und Variablen wusste ich noch nicht, aber Theoretisch, wenn ich darüber nachgedacht HÄTTE, ja durchaus logisch. Die Hit Variable hab ich irgendwie nicht deklarieren können, aber jetzt merke ich, das ich das falsch versucht habe.

Damit ich das jetzt richtug verstanden habe: Wenn wir am anfang Zahl Deklarieren, beispielsweise mit 10, und in einem Rumpf 5 Dazu zählen, dann würden wir in der Variable 15 haben, richtg?

Die sache mit "hit.collider" hatte ich versucht gehabt, aber da hat er mir dann immer einen Fehler ausgespuckt, das das so nicht geht. Wenn ich dann abfragen möchte, ob der Collider trifft, muss ich dann "!hit.collider" schreiben?

Danke für den Tipp mit dem OnDrawGizmos. Dann kann ich das ding auch schön ordentlich platzieren.

Weil dus angesprochen hast: Das mit der Diraction, da war der uhrsprüngliche Hintergedanke, das ich den Ursprung des RayCast in die entsprechende richtung verschiebe, was sich allerdings erledigt hat, da ich noch eine Transform Komponente mit hinzu gefügt habe, von der aus der Raycast starten möchte. Also eigentlich nur schlampig gearbeitet... Leider.

Das Mit der Direction, da hab ich was probieren wollen, und aber nur einen einzelnen Wert geändert, und das dann aus welchem grund auch immer einfach so stehen lassen. Ich war gestern etwas verzweifelt, weil ich das problem nicht lösen konnte.

Also dann, vielleicht bekomm ich jetzt die Hörner vom Stier gepackt.

Link zu diesem Kommentar
Auf anderen Seiten teilen

vor 10 Minuten schrieb Tintenklecks:

Damit ich das jetzt richtug verstanden habe: Wenn wir am anfang Zahl Deklarieren, beispielsweise mit 10, und in einem Rumpf 5 Dazu zählen, dann würden wir in der Variable 15 haben, richtg?

Jau.

vor 11 Minuten schrieb Tintenklecks:

Die sache mit "hit.collider" hatte ich versucht gehabt, aber da hat er mir dann immer einen Fehler ausgespuckt, das das so nicht geht.

Keine Ahnung, was da schief gelaufen sein könnte. Aber in der Regel sagt die Fehlermeldung ja recht spezifisch aus, warum das nicht geht - dann muss man nicht blind rumexperimentieren :)

vor 12 Minuten schrieb Tintenklecks:

Wenn ich dann abfragen möchte, ob der Collider trifft, muss ich dann "!hit.collider" schreiben?

Ne, umgekehrt.

if(hit.collider)
{
  print("Getroffen: " + hit.collider.gameObject.name);
}
else
{
  print("Nichts getroffen.");
}

 

vor 14 Minuten schrieb Tintenklecks:

Also dann, vielleicht bekomm ich jetzt die Hörner vom Stier gepackt.

Viel Erfolg!

Link zu diesem Kommentar
Auf anderen Seiten teilen

Archiviert

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

×
×
  • Neu erstellen...