Du bist nicht angemeldet.

Stilllegung des Forums
Das Forum wurde am 05.06.2023 nach über 20 Jahren stillgelegt (weitere Informationen und ein kleiner Rückblick).
Registrierungen, Anmeldungen und Postings sind nicht mehr möglich. Öffentliche Inhalte sind weiterhin zugänglich.
Das Team von spieleprogrammierer.de bedankt sich bei der Community für die vielen schönen Jahre.
Wenn du eine deutschsprachige Spieleentwickler-Community suchst, schau doch mal im Discord und auf ZFX vorbei!

Werbeanzeige

1

12.01.2011, 19:53

[gelöst] Richtige Kollisionserkennung mit 2D Array

Hi Leute, ich brauch mal wieder Hilfe :D

Meine Map besteht aus farbigen Klötzen, die in einem 2D Array gespeichert sind.
Jetzt möchte ich die Kollision zwischen dem Spieler (nicht an das Array gebunden, kann sich unabhängig bewegen) und der Map prüfen. Also praktisch ob er gerade nach unten,oben,links oder rechts laufen kann.
Jeder einzelne Klotz meiner Map hat eine bool Variable bIsVisible. Wenn sie true ist wird eben gerendert + kollisionen gecheckt.

Im Moment ist mein Spieler noch an das Array gebunden, damit er nur mit dem Klotz über sich,unter sich,links von ihm und rechts von ihm kollidieren kann. Da das aber ziemlich doof und ruckelig aussieht, wenn er sich immer in Sprüngen von 20 Pixeln bewegt (größe eines Klotzes), will ich den Spieler unabhängig vom Array halten.

Mein Problem ist jetzt, dass das nicht so einfach ist, wie ich mir das dachte. Um zu wissen, welche Klötze ich aktuell auf eine Kollision überprüfen muss, brauche ich die Position des Spielers in Array Koordinaten. Diese berechne ich einfach so:

C-/C++-Quelltext

1
int PosArrayX = PosX / 20; int PosArrayY = PosY / 20; // PosX bzw. PosY ist die aktuelle Position des Spielers; ein Klotz bzw. ein Array Feld ist 20x20 groß

Wenn der Spieler jetzt aber auf dem Bildschirm nicht in das Array Muster passt, kann es sein, dass Rundungsfehler entstehen, also dass die Position des Spielers im Array ein halbes Feld von der echten Position abweicht. folglich funktioniert die Kollisionserkennung nicht richtig bzw. funktioniert schon richtig, aber es sieht eben auf dem Bildschirm anders aus, als es sollte.

Kann mir jemand erklären, wie ich die Kollisionen richtig berechnen kann und die Spielfigur dabei geschmeidig bewegen kann?

Um Kollisionen zu testen, benutze ich die Collision Klasse aus dem sfml Wiki
Und zwar den BoundingBox Test, da ja alles aus Quadratischen Klötzen besteht.

lg chaia

Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von »Chaia*« (15.01.2011, 12:47)


Fred

Supermoderator

Beiträge: 2 121

Beruf: Softwareentwickler

  • Private Nachricht senden

2

12.01.2011, 21:04

Also wenn du einfach nur richtig runden wolltest könntest du es in etwa so machen:

C-/C++-Quelltext

1
static_cast<int>( value > 0 ? value + 0.5f : value - 0.5f );

value ist dann eben die entsprechende Division. Hilft dir aber vmtl. nicht viel weiter.
Denn dein Problem ist ja, dass deine Figur auch zwischeni zwe Feldern stehen kann. Dann musst du logischerweise auch beide Felder prüfen. Das Problem ist dann wiederum, dass wenn die Klötze so groß sind, wie die Felder und die Figur steht zwischen beiden Feldern, dann ist es bereits zu spät und die Figur kollidiert bereits.
Du solltest also prüfen auf welchem Feld sich die Figur befindet und prüfen in welche Richtung sie sich bewegen will. Und wenn in diese Richtung allerdings ein Block auf dem nächsten Feld steht, dann ist das Bewegen verboten(da brauchst du nicht einmal eine BoundingBox. Du schaust einfach, ob das gewünschte Feld frei ist oder nicht und bewegst dann(flüssig) die Figur weiter).
Wenn nun aber deine Figur kleiner ist als ein Feld und du möchtest, dass die Figur, auch wenn auf dem nächsten Feld sich ein Block befindet, sich an den Block bewegen kann und dagegen laufen kann, bis sie kollidiert. Dann solltest du prüfen, auf welchem Feld sich die Figur befindet und dann eben das Feld, dem sich die Figur nähert prüfen, ob es da einen Block gibt und wenn die Figur eben zu nahe an den Block kommt(hängt von der Größe der Figur ab), dann die Bewegung blockieren.
Und wenn jetzt die Blöcke auch noch kleiner wären als die Felder, dann kann sich der Spieler eben auf das andere Feld bewegen. Allerdings muss man dann natürlich bei der Bewegung prüfen, ob sich auch auf dem eigenen Feld gerade ein Block befindet, dem der Spieler zu nahe kommen könnte und ggf. die Bewegung blockieren.

Ich hoffe du verstehst ungefähr, was ich meine.

3

13.01.2011, 18:57

Hi,

das Problem ist ja leider, dass ich nicht exakt die Position des Spielers in Array Koordinaten habe bzw. diese viel zu stark gerundet sind. Sonst würde ich es so machen, wie du sagst, also einfach prüfen, ob das nächste Feld frei ist. Aber wie gesagt, ich kann nicht genau das nächste Feld bestimmen, weil die Spieler Position nicht genau ist.
So kann es z.B. sein, dass gar keine Kollision stattfindet, obwohl eine stattfinden sollte, weil der Spieler zwischen den Klötzen steht und dann durch Rundung der falsche Klotz geprüft wird. Rechnerisch ist es dann richtig, sieht auf dem Bildschirm aber natürlich nicht so toll aus.
Ich könnte jetzt auch einfach immer einen größeren Ausschnitt der Map durchlaufen und falls der Teil sichtbar ist (bIsVisible == true) auf Kollision prüfen (BoundingBox).
Allerdings habe ich dann das Problem, dass ich nicht so einfach bestimmen kann, ob der Spieler jetzt noch nach links,rechts,oben oder unten bewegt werden kann.

Verstehst du was ich meine?
Wie kann ich das lösen? Wie macht ihr das normalerweise?
(Noch kann ich das Map System umstellen, habe noch nicht soo viel gemacht)

lg chaia

Schorsch

Supermoderator

Beiträge: 5 145

Wohnort: Wickede

Beruf: Softwareentwickler

  • Private Nachricht senden

4

13.01.2011, 23:11

Gibt da 2 Möglichkeiten. Entweder dein Spieler bewegt sich nur auf deinen Blöcken/Tiles oder er bewegt sich frei.
Wenn du dich nur auf den Tiles bewegst dann hast du natürlich nicht das Problem mit der Kollision. Wenn nach rechts gedrückt wird prüfst du das Feld rechts vom Spieler zuerst auf Kollision. Damit die Bewegung trotzdem flüssig wirkt, bewegst du den Spieler nicht in einem. Du sagst deine Tiles und dein Spieler sind jeweils Quadratisch und haben eine Kantenlänge von 20Pixeln.
Vom Ablauf:

Quellcode

1
2
Wenn(Spieler nicht in Bewegung und linke Taste wird gedrückt:){    Wenn(Prüfe linkes Feld auf Kollision. Falls keine Kollision:)  {       Spieler in Bewegung.    }}
Wenn(Spieler in Bewegung){  Wenn(Spielerposition.X  == 0 (Der Spieler ist wieder passend im Raster deiner Karte) )  {       Spieler nicht mehr in Bewegung. }   Ansonsten   {       Bewege Spieler um 2 Pixel nach links    }}



Wenn der Spieler in Bewegung setzt kannst du zum Beispiel einfach eine bool variable setzen. Du könntest auch einfach die Koordinaten vom Spieler durch die Größe eines Tiles teilen und wenn der Rest == 0 ist dann ist der Spieler nicht in Bewegung, andernfalls ist er in Bewegung.

Wenn du den Spieler nun vom Raster trennen möchtest kannst du ihn frei bewegen lassen. Die Kollision kannst du nun so testen:(Ich gehe davon aus das die Koordinaten vom Spieler an der linken oberen Ecke von seinem Bild sind)

Quellcode

1
Wenn(Taste links gedrückt){    Wenn(map[Spieler.position.X][Spieler.position.Y]&&map[Spieler.position.X][Spieler.position.Y+20] == keine Kollision)    {       Bewege Spieler um 2 Pixel nach links    }}Wenn(Taste rechts gedrückt){ Wenn(map[Spieler.position.X+20][Spieler.position.Y]&&map[Spieler.position.X+20][Spieler.position.Y+20] == keine Kollision)  {       Bewege Spieler um 2 Pixel nach links    }}



Oben und Unten läuft dann genauso. Am Beispiel vom nach links Gehen:
Du guckst quasi ob das Feld im Raster(deine Karte/Map), auf dem der linke-obere Punkt von deinem Spieler liegt frei ist und ob der Punkt im Raster vom linken-unteren Punkt vom Spieler frei ist. Wenn An diesen beiden Stellen frei ist kannst du dich bewegen.
Die 2 Pixel für die Bewegung sind natürlich nur Beispielhaft, wobei du bei der ersten Variante natürlich drauf achten musst, dass jede Runde um eine Pixelanzahl bewegt wird, durch die sich die Breite deiner Tiles teilen lässt. Also Beispiel deine Tiles sind 20 Pixel breit und der Spieler bewegt sich um 2 Pixel: 20/2 -> Rest 0. Wenn du den Spieler jetzt um 3 Pixel bewegen würdest: 20/3 -> Rest 2 geht also nicht. Das ist dafür, dass der Spieler sich genau um eine Rasterbreite bewegt und dann wieder passende Koordinaten hat.



Sorry für die dumme Formatierung von dem Code Blöcken. Weiß auch nicht genau warum er das in 2 Zeilen quetschen möchte;)
„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.“

Fred

Supermoderator

Beiträge: 2 121

Beruf: Softwareentwickler

  • Private Nachricht senden

5

14.01.2011, 02:44

Du könntest auch einfach eine Box um deinen Spieler legen mit einer Größe von 20x20px. Dann Prüfst du in welchen Tiles die Ecken der Box liegen und schon hast du alle Tiles, die den Spieler bei seiner nächsten Bewegung interessieren könnten(also halt die Tiles um den Spieler herum) und dann schaust du halt, ob der Spieler, wenn er einen Schritt machen will, mit einem der Objekte von diesen Tiles kollidieren würde(sprich: ist die Position des Spielers + halbe/ganze(kommt darauf an, wo die Koordinaten des Spielers relativ zum Sprite sind) Länge oder Breite innerhalb des Klotzes auf dem nächsten Feld - halbe/ganze Länge) und verbietest dann entweder die Bewegung oder bewegst den Spieler.
Ist jetzt dafür gedacht, dass du nicht alle Objekte auf Kollision prüfst. Denn das braucht es ja nicht. Sondern nur die im näheren Umfeld.

6

14.01.2011, 07:13

Alles klar, danke.
Werde das ganze heute Abend nochmal angehen.

lg chaia

7

15.01.2011, 12:11

So, also ich habe jetzt nochmal etwas rumprobiert.
Zuerst versuchte ich, wie Schorsch gesagt hat, den Spieler zwar flüssig, aber dennoch im Raster zu bewegen. Allerdings muss man ihn ja immer um eine bestimmte Anzahl an Pixeln bewegen, damit das ganze durch 20 teilbar ist. Wenn ich ihn immer um 2 bewegen würde, wäre es ja auch verschiedenen Rechnern unterschiedlich schnell. Und wenn ich mit der Frametime multipliziere kommt natürlich was ungerades raus.
Dann habe ich das, was Fred gesagt hat probiert, also eine Box für den Spieler zu machen. Nach etwas experimentieren klappt es jetzt wunderbar.
Ich verschiebe die Box jeweils um den Betrag, um den das PlayerSprite verschoben werden würde. Wenn dann eine Kollision stattfindet, darf sich der Spieler eben nicht bewegen.

Danke, an alle die mir geholfen haben :)

lg chaia

Schorsch

Supermoderator

Beiträge: 5 145

Wohnort: Wickede

Beruf: Softwareentwickler

  • Private Nachricht senden

8

16.01.2011, 14:30

Mit dem festen Raster das klappt natürlich nur wenn du die Framerate fix setzt. Da mit der Box ist Quasi das was bei mir im 2ten Vorschlag gemeint war. Durch die hässliche Codeformatierung aber so halt nicht zu gebrauchen;) Aber hast es ja lösen können;)
„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