Du bist nicht angemeldet.

Werbeanzeige

thijeman

Frischling

  • »thijeman« ist der Autor dieses Themas

Beiträge: 13

Beruf: Webentwickler

  • Private Nachricht senden

1

20.07.2021, 13:13

Movement und Koordinatenverhalten (2D)

Hallo Leute,
ich bin sehr frisch in der Spieleentwicklung (2-3 Wochen) und schreibe gerade mein eigenes 2D RPG Gameframework. Ich bin gerade dabei das Movement zu konzeptionieren und habe ein paar Überlegungen.

Zu erst einmal: Ich habe verschiedene Map Layer aus denen Koordinatensysteme gebildet werden. Diese verfolgen unterschiedliche Aufgaben. Beispielsweise habe ich den Texturen Layer wo ID's stehen die auf eine Textur referieren. Bei der Auswertung des Raster (mit entsprechenden ID's) wird das Koordinatensystem gebildet und die Welt mit den entsprechenden Assets gezeichnet. Ebenso gibt es einen Layer für Kollisionen und für Events. Bei diesen beiden Layer ist die Abbildung der Referenz etwas einfacher, denn diese setzen im Fall des Event Layer einen Trigger an die entsprechende Position und im Fall des Collision Layer eine Blockade dass sich der Spieler nicht an diese Position bewegen kann.

Während ich das Movement System realisiert habe, hatte ich folgende Überlegung:
... Ich könnte eigentlich ganz simple und stumpf abfragen ob die Koordinate an der sich der Spieler bewegen will eine Kollision oder ein Event ist.

Das würde ich realisieren indem ich die neue Koordinate, auf die der Spieler sich positionieren möchte, mit den Daten des Collision und Event Layer gegenprüfen würde.
Allerdings stellt mich diese Lösung ehrlich gesagt nicht ganz zufrieden und das aus folgenden Gründen:

1. Die Idee dass ich erst das Verhalten der Koordinate kenne stellt mich nicht zufrieden. Wir haben doch schon die Daten?
2. Sollte ich eine Brücke haben auf der nichts passiert, sondern nur zum passieren da ist, ist das wiederholte Abfragen der Koordinate unnötig. Wie in Pnkt 1 erwähnt: Eigentlich sind die Daten schon vorhanden.
3. Was ist mit Koordinaten die ein Spieler theoretisch niemals erreichen wird? Es könnte ja sein, dass die Kollision sich an einem Punkt befindet die der Spieler nicht erreichen soll. Ein weiter Grund wäre aber auch, dass die Koordinate zwar erreichbar ist, aber der Spieler diese Koordinate nicht unbedingt erreichen möchte, weil er das Spiel schon kennt oder nicht bis ans Ende der Karte gehen möchte.

Gerade Punkt drei hatte mich dazu angeregt dass Movement System und die Abfrage des Koordinatenverhalten mehr auf das Spielerverhalten zuzuschneiden. Daher war meine nächste Überlegung...
... Ich könnte das Verhalten mehrerer Koordinaten sammeln, wenn sich diese in einem bestimmten Radius des Spieler befinden.

Meine Gedanken waren dazu:
1. Die Koordinaten die entweder unmittelbar oder in greifbarer Nähe dem Spieler zur Verfügung stehen werden mit den Layern die den Koordinaten ein Verhalten versprechen gegengeprüft und dann mit der Logik des Verhalten befüllt (Stichwort Callback).
2. Die Abfrage wäre kleiner und schneller, weil sich die neuzugesteuerte Koordinate nicht länger mit dem kompletten ausgewerteten Layer validieren muss, sondern nur noch mit den referenzierten Koordinaten die im Radius sind.
3. Es werden keine Koordinaten mehr mit einbezogen, die der Spieler theoretisch gar nicht erreichen möchte. Das Verhalten ist mehr auf den Spieler zugeschnitten.

Natürlich ist man von seiner eignen Idee recht überzeugt, daher möchte ich euch gerne um Feedback fragen. Wie findet ihr diese Überlegung? Welche Ansätze verfolgt ihr? Gibt es einen besseren Weg den ihr mir raten würdet?

Natürlich ist das Thema Performance bei dieser einfachen Algorithmus-Frage kein großartiges Thema mehr - allerdings finde ich die Idee interessant und die Erfahrung kann sicherlich nicht schaden. Programmierer aus Leidenschaft!

Schorsch

Supermoderator

Beiträge: 5 206

Wohnort: Wickede

Beruf: Student

  • Private Nachricht senden

2

20.07.2021, 13:26

Hey,
mir ist nicht ganz klar wo dein Problem liegt. Du hast dein Raster mit den jeweiligen Daten. Wenn du weißt wo sich der Spieler hinbewegen wird kannst du genau diese Zellen im Raster kontrollieren. Da dein Spieler nicht nur ein einzelner Punkt ist wird es nicht ausreichen nur eine einzelne Zelle zu prüfen. Du kennst ja die Größe des Spielers und kannst dadurch berechnen welche umliegenden Zellen du prüfen möchtest. Jetzt musst du einfach checken ob der Spieler kollidiert und/oder irgendein Trigger ausgelöst wird. Alle anderen Felder betrachtest du zu diesem Zeitpunkt gar nicht. Dadurch erübrigen sich deine Probleme doch oder nicht? Mal vereinfacht betrachtet ist deine Welt ein Raster und dein Spieler kann sich nur genau auf diesem Raster bewegen. In dieser Version kann dein Spieler also immer nur genau auf einer Zelle sein. Du musst jetzt lediglich die Ziel-Zelle des Spielers prüfen. Das macht pro Schritt eine Überprüfung auf Kollision und eine Überprüfung auf ein Event. Das klingt für mich völlig in Ordnung.
„Es ist doch so. Zwei und zwei macht irgendwas, und vier und vier macht irgendwas. Leider nicht dasselbe, dann wär's leicht.
Das ist aber auch schon höhere Mathematik.“

thijeman

Frischling

  • »thijeman« ist der Autor dieses Themas

Beiträge: 13

Beruf: Webentwickler

  • Private Nachricht senden

3

20.07.2021, 14:20

Hey,
mir ist nicht ganz klar wo dein Problem liegt. Du hast dein Raster mit den jeweiligen Daten. Wenn du weißt wo sich der Spieler hinbewegen wird kannst du genau diese Zellen im Raster kontrollieren. Da dein Spieler nicht nur ein einzelner Punkt ist wird es nicht ausreichen nur eine einzelne Zelle zu prüfen. Du kennst ja die Größe des Spielers und kannst dadurch berechnen welche umliegenden Zellen du prüfen möchtest. Jetzt musst du einfach checken ob der Spieler kollidiert und/oder irgendein Trigger ausgelöst wird. Alle anderen Felder betrachtest du zu diesem Zeitpunkt gar nicht. Dadurch erübrigen sich deine Probleme doch oder nicht? Mal vereinfacht betrachtet ist deine Welt ein Raster und dein Spieler kann sich nur genau auf diesem Raster bewegen. In dieser Version kann dein Spieler also immer nur genau auf einer Zelle sein. Du musst jetzt lediglich die Ziel-Zelle des Spielers prüfen. Das macht pro Schritt eine Überprüfung auf Kollision und eine Überprüfung auf ein Event. Das klingt für mich völlig in Ordnung.

Mir geht es zB darum die Abfragen nach möglichen Kollisions, Event und normalen Koordinaten zu verringern indem ich die koordinaten die der spieler fast in unmittelbarer nähe hat zu prüfen so dass der radius nicht laenger mehr geprüft werden muss solange der spieler sich dort befindet. Wenn ich eine lange Brücke habe die nichts tut außer das man sich dort bewegen kann waere es doch quatsch trotzdem jede einzelne koordinate auf dieser brücke auf das verhalten zu prüfen. Oder waere es ja auch kontraproduktiv wenn die koordinate zum xten mal ueberprueft wird wenn der spieler diese ziemlich oft betritt

Schorsch

Supermoderator

Beiträge: 5 206

Wohnort: Wickede

Beruf: Student

  • Private Nachricht senden

4

21.07.2021, 08:43

Gegenfrage, wie willst du dir denn merken ob du die Brücke schon überprüft hast? Du würdest einfach die eine gegen eine andere Abfrage tauschen. Und was genau meinst du mit jeder einzelnen Koordinate der Brücke? Wenn der Spieler sich von Kachel zu kachel bewegt dann musst du natürlich auch prüfen ob er das überhaupt darf.
„Es ist doch so. Zwei und zwei macht irgendwas, und vier und vier macht irgendwas. Leider nicht dasselbe, dann wär's leicht.
Das ist aber auch schon höhere Mathematik.“

thijeman

Frischling

  • »thijeman« ist der Autor dieses Themas

Beiträge: 13

Beruf: Webentwickler

  • Private Nachricht senden

5

21.07.2021, 11:35

Gegenfrage, wie willst du dir denn merken ob du die Brücke schon überprüft hast? Du würdest einfach die eine gegen eine andere Abfrage tauschen. Und was genau meinst du mit jeder einzelnen Koordinate der Brücke? Wenn der Spieler sich von Kachel zu kachel bewegt dann musst du natürlich auch prüfen ob er das überhaupt darf.

Vielleicht überseh ich hier was? Oder vielleicht drück ich mich ja falsch aus...

Wenn ich das Verhalten der Koordinaten innerhalb des Radius ausgehend vom Spieler überprüfe, also bspw mit meiner Kollisionliste gegenprüfe, habe ich eine neue Liste an Koordinaten die sich im naheliegenden Radius des Spieler befinden (also die Koordinaten die mit wenigen Bewegungen vom Spieler erreichbar wären) mit einem entsprechenden Callback zum Verhalten. Das würde auch bedeuten, wenn sich Spieler innerhalb des Radius bewegt, wären die Einträge die in der neuen Liste zwischengespeichert werden kleiner, weil ich nicht mehr die große Kollisionliste gegenprüfen muss sondern nur noch die Liste mit den Verhalten der Koordinaten die sich im Radius des Spieler befinden.

Kurz gesagt: Eine Prüfung findet immer statt, allerdings dann nur noch mit den Koordinaten die der Spieler nach seinen aktuellen Koordinaten erreichen kann.

Wieso sollte ich die Kollisionen der anderen Ende der Karte abfragen, wenn der Spieler doch noch ganz am Anfang ist? Ich stelle mir dieses Verhalten schließlich auch unnötig Zeitintensiv vor. Hier muss geprüft werden obwohl es zum aktuellen Zeitpunkt nicht notwendig ist.

Schorsch

Supermoderator

Beiträge: 5 206

Wohnort: Wickede

Beruf: Student

  • Private Nachricht senden

6

21.07.2021, 15:25

Ich glaube wir reden stark aneinander vorbei. Wenn ich dich richtig verstehe hast du doch eine Art von Tilemap. Sagen wir mal deine Kollisionseben sieht in etwa so aus:
1111111111
1000000001
1000000001
1000110011
1000010011
1111111111
Hierbei steht 1 für Kollision und 0 für eine freie Fläche.
Dein Spieler befindet sich jetzt an irgendeiner Stelle auf der Karte. Du musst die Kollision natürlich nur bei den Kacheln abfragen die der Spieler aktuell auch erreicht. Befindet sich der Spieler am unteren Rand der Karte prüfst du natürlich nicht auf Kollision mit Flächen die sich oben befinden. Ist meine Annahme hier schon falsch?
„Es ist doch so. Zwei und zwei macht irgendwas, und vier und vier macht irgendwas. Leider nicht dasselbe, dann wär's leicht.
Das ist aber auch schon höhere Mathematik.“

thijeman

Frischling

  • »thijeman« ist der Autor dieses Themas

Beiträge: 13

Beruf: Webentwickler

  • Private Nachricht senden

7

21.07.2021, 16:08

Ich glaube wir reden stark aneinander vorbei. Wenn ich dich richtig verstehe hast du doch eine Art von Tilemap. Sagen wir mal deine Kollisionseben sieht in etwa so aus:
1111111111
1000000001
1000000001
1000110011
1000010011
1111111111
Hierbei steht 1 für Kollision und 0 für eine freie Fläche.
Dein Spieler befindet sich jetzt an irgendeiner Stelle auf der Karte. Du musst die Kollision natürlich nur bei den Kacheln abfragen die der Spieler aktuell auch erreicht. Befindet sich der Spieler am unteren Rand der Karte prüfst du natürlich nicht auf Kollision mit Flächen die sich oben befinden. Ist meine Annahme hier schon falsch?

Nein, deine Annahme ist richtig. Mein Punkt ist eine Zwischenliste zu speichern, so dass beim weiteren Movement nur noch die umliegenden Koordinaten geprüft werden. Als Beispiel:

Die TileMap wird ausgewertet, so dass wir wissen an welcher Koordinate sich eine 1 (Kollision) befindet.

Ein Beispielbild:

(Link)


Rot: Kollision, Orange: Event, Gelb: Spieler, Grün: Bewegbare Fläche, Lila: Radius

In diesem Fall will ich jetzt, dass in dem gezeichneten Radius die Koordinaten gesammelt werden und mit der ausgewerteten Kollisionsliste und Eventliste gegengeprüft wird.

Jetzt haben wir eine weitere Liste mit Koordinaten wo sich eine Kollision oder ein Event befindet. Der Unterschied ist allerdings: Statt dessen dass wir die große Kollisionliste verwenden, verwenden wir die zwischengespeicherte Liste die kleiner ist, weil wir eine Kollisionsliste aus den umliegenden Koordinaten gebildet haben.

Schorsch

Supermoderator

Beiträge: 5 206

Wohnort: Wickede

Beruf: Student

  • Private Nachricht senden

8

22.07.2021, 08:50

Die Verwaltung der Liste ist aber schon ein ganz schöner overhead und am Ende ist vor allem wichtig dass dein Code einfach und verständlich ist. Wenn du zwei Monate später da rein guckst willst du möglichst direkt verstehen was da passiert. Und zusätzlich zum Verständnis hast du bei einfachem Code weniger Potenzial Fehler zu machen. Die paar Überprüfungen pro Frame werden schon schnell genug laufen, mach dir da mal keine Sorgen. Und solltest du am Ende tatsächlich Probleme mit der Performance haben ist es sinnvoller gezielt nach den Bottlenecks in deinem Code zu suchen. Du versuchst hier Dinge zu optimieren die das vermutlich gar nicht nötig haben.
Du kannst eher den Radius verkleinern. Dein Spieler hat die Größe einer Kachel. Dadurch kann er maximal auf vier Kacheln gleichzeitig stehen. Welche Kacheln das sind kannst du ja relativ simpel berechnen. Du kennst die Position des Spielers und die Größe der Kacheln und kannst damit die Indizes der Kacheln auf denen der Spieler steht berechnen. Das ist am Ende viel schneller als Kacheln auf Listen zu schieben, zu prüfen wie aktuell die Liste ist und diese wieder zu löschen.
„Es ist doch so. Zwei und zwei macht irgendwas, und vier und vier macht irgendwas. Leider nicht dasselbe, dann wär's leicht.
Das ist aber auch schon höhere Mathematik.“

thijeman

Frischling

  • »thijeman« ist der Autor dieses Themas

Beiträge: 13

Beruf: Webentwickler

  • Private Nachricht senden

9

23.07.2021, 09:22

Die Verwaltung der Liste ist aber schon ein ganz schöner overhead und am Ende ist vor allem wichtig dass dein Code einfach und verständlich ist. Wenn du zwei Monate später da rein guckst willst du möglichst direkt verstehen was da passiert. Und zusätzlich zum Verständnis hast du bei einfachem Code weniger Potenzial Fehler zu machen. Die paar Überprüfungen pro Frame werden schon schnell genug laufen, mach dir da mal keine Sorgen. Und solltest du am Ende tatsächlich Probleme mit der Performance haben ist es sinnvoller gezielt nach den Bottlenecks in deinem Code zu suchen. Du versuchst hier Dinge zu optimieren die das vermutlich gar nicht nötig haben.
Du kannst eher den Radius verkleinern. Dein Spieler hat die Größe einer Kachel. Dadurch kann er maximal auf vier Kacheln gleichzeitig stehen. Welche Kacheln das sind kannst du ja relativ simpel berechnen. Du kennst die Position des Spielers und die Größe der Kacheln und kannst damit die Indizes der Kacheln auf denen der Spieler steht berechnen. Das ist am Ende viel schneller als Kacheln auf Listen zu schieben, zu prüfen wie aktuell die Liste ist und diese wieder zu löschen.

Danke dir, war ja auch ne interessante Diskussion. Ich frage mich halt nur wie es z.B. Openworld Spiele wie Zelda Breath of the Wild machen :D Dass ähnliche Systeme für ein 2D RPG sinnfrei sind ist mir ja bewusst, mir geht aber hierbei um einen Lerneffekt und es ist auch sehr interessant neue Systeme kennenzulernen und zu entwickeln die vielleicht auf das Spielerverhalten angepasst sind o.ä.

Schorsch

Supermoderator

Beiträge: 5 206

Wohnort: Wickede

Beruf: Student

  • Private Nachricht senden

10

23.07.2021, 09:58

Ich frage mich halt nur wie es z.B. Openworld Spiele wie Zelda Breath of the Wild machen

Das läuft ganz anders ab. Dort werden keine Tilemaps genutzt sondern Objekte frei platziert. Damit nicht zwischen allen Objekten auf Kollision geprüft werden muss, wird die Welt unterteilt. Du kannst die Umgebung zum Beispiel wie bei einer Tilemap in viele Quadrate einteilen. Du speicherst dir dann welche Objekte sich in welchem Quadrat befinden. Kollisionen musst du dann nur unter Objekten innerhalb eines Quadrats überprüfen. Zur Partitionierung gibt es verschiedene Ansätze. Quadtree und Octree sind recht prominente Beispiele dafür. Ansonsten gibt es noch viele Möglichkeiten weiter zu optimieren. Zum Beispiel kannst du statische und bewegliche Objekte unterschiedlich behandeln.
„Es ist doch so. Zwei und zwei macht irgendwas, und vier und vier macht irgendwas. Leider nicht dasselbe, dann wär's leicht.
Das ist aber auch schon höhere Mathematik.“

Werbeanzeige