Java Ein wenig Ünterstützung beim "Raycasten" benötigt

CPU

Lieutenant
Registriert
Jan. 2006
Beiträge
704
Hallo,

ich baue mir gerade meinen einfachen Raycasting-Algorithmus zusammen. Undzwar arbeite ich dabei nach dem Tutorial von Permadi [1] und diesem hier [2]. Unten ist mein teils selbstständig erarbeiteter, teils (zusammen-)kopierter Quellcode.

Nun habe ich zwei Fragen:
1. Ich habe folgende Tatsache noch nicht so verstanden: der Spieler hat eine (ganzzahlige) Position auf dem Spielfeld in einer Zelle, die durchaus die Ausmaße von 64x64 Pixeln haben kann. Nun das Raycasting wird ja nun wieder Pixelgenau berechnet. Müsste man dann nicht immer von der Mitte einer solchen Zelle ausgehen und die Strahlen aussenden?

2. Mein Algorithmus funktioniert nicht korrekt: obwohl ich es aus [2] nur kopiert habe und die Datentypen angepasst habe, wird in der horizontalen Erkennung manchmal ein vollkommen anderes Kästchen erkannt (orange hinterlegt, dazu z.B. 11 mal die Links taste drücken). Woran könnte das liegen?

Viele Grüße,
CPU

Der Code ist im Anhang. Achtung: dieser Code ist aus den Tutorials [1] und [2] zusammenkopiert und weder schön noch funktionsfähig. Weiter unten ist selbst geschriebener Code der sogar funktioniert. Hura. :)
 

Anhänge

Zuletzt bearbeitet: (Ich habe mal den Code in den Anhang verschoben, weil der so viel Platz weggenommen hat ...)
Ich würde dir raten, den Quellcode Funktion für Funktion auseinander zu nehmen.
Es bringt dir später für die Verwendung des Codes nichts wenn du deine eigenen Schnittstellen und Prozesse nicht verstehst.

Kopieren bringt nur dann etwas wenn du entweder
a) nur die Schnittstellen kennst und natürlich zu 100% sicher bist, dass deine Schnittstelle bzw deine Interfaces dazu funktionieren.
b) du den Algorithmus selbst geschrieben hast (eben vor etwas längerer Zeit) und ihn nur wiederverwendest.

Das Maß an eigenem Mitdenken ist für das nutzen von Fremden Quellcode eben auch nötig und sollte man aufbringen können.

Gruß
 
1)
Ja. Berechnet wird auch vom exakten Standpunkt des Spielers aus.

Die Darstellung der Linie ist allerdings falsch. Da müsstest du noch ein 1/2 Pixel Offset von den Koordinaten abziehen. Da das Geschehen 15fach vergrößert wird (miniMapScale), musst du den Offset ebenfalls mit dem Zoomfaktor multiplizieren: 1/2 * 15 = 15/2


2)
Der Fehler muss in der ersten While-Schleife in der rayCast-Methode sein. Mir ist das jetzt zu kompliziert, als dass ich mich da reinarbeiten möchte, aber es sieht schwer danach aus, als ob es dort Probleme mit der Rundung gibt. Du castest da ja fleißig zwischen Double und Int hin und her, und rundest den Kram usw. Wenn man in den Zeilen 147 & 148 das Runden raus nimmt, werden schon einige Fehler eliminiert. Aber nicht alle.
Ich empfehle dir, an der Stelle mal Zettel und Stift rauszuholen und nachzurechnen. Es gibt ja einen Unterschied zwischen dem Algorithmus und dem, was du programmiert hast.
 
Zuletzt bearbeitet:
Hallo,

zunächst einmal Dankeschön für die Antworten!

Nun ich denke, dass ich noch ein Verständnisproblem habe bei folgender Tatsache: die Karte ist ja ein Array. Nun und jedes Feld kann ich nur ganzzahlig addressieren. Wenn ich mich aber anständig bewegen möchte, dann kommen ja auch krumme Zahlen als mein aktueller Standpunkt heraus. Also stehe ich ja eigentlich zwischen Arrayfeldern. Und wenn ich dann meinen Raycasting Hokuspokus mache, werden die Zahlen ja noch krummer und dann schließe ich mit div. Rundungsmethoden wieder auf die Arrayfelder. Das kann doch eigentlich nur schief gehen, oder? Wenn dauernd herumgerundet wird und von floating point zu Integer und zurück!

In dem Tutorial von Permadi wird sogar davon gesprochen, dass jedes Arrayfeld 64x64 Pixel groß ist. Aber das bedeutet doch, dass sich der Spieler in jedem Feld bewgen kann alleine schon bewegen kann.

Also ich weiß nicht, ob ich mein Verständisproblem herüberbringen konnte aber: kann man nicht mit einer Pixelmap arbeiten? D.h. der Spieler hat eine Integer-Position auf der Karte und kann sich demnach in 8 Richtungen der benachbarten Felder bewegen (45° Drehwinkel)?

Edit: ich habe mal diesen Wert (PI/4=45°) in dem Javascript-Raycaster ausprobiert. Nun die Bewegung ist etwas "steif" und macht keinen Spaß! Also das ist nicht so optimal. Aber wie kann ich denn von diesen ganzen Fließkommazahlen wegkommen?

Gruß,
CPU
 
Zuletzt bearbeitet:
Natürlich kannst du mit einer Pixelmap arbeiten. Im Grunde ist das aber auch nur ein Array, bei dem die Elemente eine Kantenlänge von nur einem Pixel haben. Da würde man allerdings denselben Algorithmus verwenden, den du gerade versuchst umzusetzen.

Ich empfehle dir, die ganze Rechnerei mal auseinander zu nehmen. Mache eine Methode, die einen Strahl wirft und eine zweite Methode, die nachsieht, wo der Strahl entlangwandert. Die zweite Methode ruft dann eine dritte Methode auf, die konkret überprüft, ob der Strahl in einem Element des Arrays mit irgendetwas kollidiert.
Deinen aktuellen Code empfinde ich als etwas zu undurchsichtig. Ich habe an einigen Stellen nur da gesessen und überlegt, was da eigentlich passiert. Eine bessere Strukturierung macht es nicht nur für mich einfacher sondern auch für dich. Da findet man Fehler einfacher. ;)
 
Hey Leute,

e-Laurin schrieb:
Natürlich kannst du mit einer Pixelmap arbeiten. Im Grunde ist das aber auch nur ein Array, bei dem die Elemente eine Kantenlänge von nur einem Pixel haben. Da würde man allerdings denselben Algorithmus verwenden, den du gerade versuchst umzusetzen.
okay. Dann mache ich mal weiter so wie bisher. Aber trotzdem finde ich das verwirrend!

e-Laurin schrieb:
Ich empfehle dir, die ganze Rechnerei mal auseinander zu nehmen. [...]
Das habe ich auch gemacht. Ich habe alles aus meiner Datei herausgeworfen. Die Tutorials auf dem Papier klar gemacht und dann Schritt für Schritt wieder implementiert.

e-Laurin schrieb:
Deinen aktuellen Code empfinde ich als etwas zu undurchsichtig.
Das ist ja klar. Ich habe mir einfach etwas zusammenkopiert. Das das aber nichts (also das Zusammenkopieren) bringt habe ich spätestens mit dieser Frage hier im Forum gemerkt.

Also ich habe jetzt nochmal von vorne angefangen und neu implementiert. Im Anhang mein Ergebnis. Was haltet Ihr davon? Es werden ja schon 320 Strahlen geworfen, aber man kann ja auch die Schleife auskommentieren und nur mit "p.angle" - also die Blickrichtung des Spielers - versuchen. Dann kann man sich drehen und die Ergebnisse begutachten.

Es wäre echt suuuper, wenn nochmal jemand drüberschaut, ob das so in Ordnung ist - ich meine Fehlerfrei!

Nun gehe ich gleich mal einen Schritt weiter: Performance. Die 320 Strahlen zu werfen dauert bei mir im Durchschnitt etwa 1/1000 Sekunde. Nun ist das arg langsam? Wenn ich auf 30 FPS heraus wollte (will ich das?), dann habe ich ja so 30/1000 Sekunden Zeit um meine Logik und das Rendering hinter mich zu bringen. Im Vergleich hierzu wäre es ja doch nur eine kurze Zeit oder?

Nun was haltet Ihr von folgender Optimierung: ich legen ein 2-D-Array von float-Zahlen an mit der Länge 360. Und dort werden jeweils der sin und der cos gecached. Wenn der wert einmal errechnet wurde. Oder besser: zu Anfang einmal durchiterieren und Werte belegen. Aber ist der Array-Zugriff wirklich um so vieles schneller? Und geht das mit dem Speicherplatz in Ordnung?

Habt Ihr vielleicht noch Optimierungsideen?

Außerdem noch eine Frage: ich werde mich jetzt mal an die Bewegung herantasten. Habt Ihr eine Empfehlung, wie ich das am Besten anstelle?

Beste Grüße,
CPU

EDIT: Es scheint, dass es doch nicht so glatt läuft! Mmmmm. Wenn man die große Karte nimmt (s.u.) und die Position belässt und sich dann um 180° dreht kommt Schrott heraus!

Große Karte:
Code:
{
			{1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1},
			{1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1},
			{1,0,0,3,3,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1},
			{1,0,0,3,0,3,0,0,1,1,1,2,1,1,1,1,1,2,1,1,1,2,1,0,0,0,0,0,0,0,0,1},
			{1,0,0,3,0,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1},
			{1,0,0,0,0,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1},
			{1,0,0,0,0,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3,1,1,1,1,1},
			{1,0,0,3,0,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1},
			{1,0,0,3,0,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2},
			{1,0,0,3,0,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1},
			{1,0,0,3,3,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3,1,1,1,1,1},
			{1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1},
			{1,0,0,0,0,0,0,0,0,3,3,3,0,0,3,3,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2},
			{1,0,0,0,0,0,0,0,0,3,3,3,0,0,3,3,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1},
			{1,0,0,0,0,0,0,0,0,3,3,3,0,0,3,3,3,0,0,0,0,0,0,0,0,0,3,1,1,1,1,1},
			{1,0,0,0,0,0,0,0,0,3,3,3,0,0,3,3,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1},
			{1,0,0,4,0,0,4,2,0,2,2,2,2,2,2,2,2,0,2,4,4,0,0,4,0,0,0,0,0,0,0,1},
			{1,0,0,4,0,0,4,0,0,0,0,0,0,0,0,0,0,0,0,0,4,0,0,4,0,0,0,0,0,0,0,1},
			{1,0,0,4,0,0,4,0,0,0,0,0,0,0,0,0,0,0,0,0,4,0,0,4,0,0,0,0,0,0,0,1},
			{1,0,0,4,0,0,4,0,0,0,0,0,0,0,0,0,0,0,0,0,4,0,0,4,0,0,0,0,0,0,0,1},
			{1,0,0,4,3,3,4,2,2,2,2,2,2,2,2,2,2,2,2,2,4,3,3,4,0,0,0,0,0,0,0,1},
			{1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1},
			{1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1},
			{1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1}
		};
 

Anhänge

Zuletzt bearbeitet:
Oha, was hast du denn da programmiert? ^^
Schaut schon etwas wüst mit die Hilfsfunktionen aus.

Die Ursache des Fehlers ist klar: Die Position (4/4) ist Teil der Wand. Und genau da steht der Spieler. Bei (4.0001 / 4.0001) wäre er außerhalb.
Am besten sieht man das, wenn man als Position (8/8) angibt, dann funktioniert die Erkennung rundherum.

Aber eines steht fest, es sieht super aus! :daumen:


Zu der Performance:
1s/30fps ergibt bei mir 33 Tausendstel. Da ist also sogar noch etwas mehr Luft.
Die Performance ist in diesem Augenblick eigentlich noch Nebensache. Optimiert wird eigentlich nur, wenn es am Ende nicht passt.

Dann kann man aber, wie du schon geschrieben hast, sich Sinus- und Cosinus-Tabellen aufstellen. Ein Arrayzugriff geht sehr viel schneller als die beiden mathematischen Funktionen zu berechnen. Du musst aber schauen, wie viele Einträge in den Tabellen Sinn ergeben. Vielleicht sind 3600 besser, da man dort bei großen Entfernungen weniger Präzisionsverlust hat.

Der dafür benötigte Speicherplatz ist egal. 3600 mal 4 Bytes pro Float sind ca. 14 KB. Das kann man vernachlässigen.

Ansonsten kann man noch schauen, ob man etwas an der Berechnung noch optimieren kann. Da fallen insbesondere die ganzen Methodenaufrufe auf. Ich meine damit die kleinen Hilfsfunktionen für das Runden usw. Der Aufruf an sich benötigt ja auch etwas Zeit.

Mehr Potential sehe ich aber im Moment nicht. Es käme auf eine Analyse an, an welcher Stelle der Computer die meiste Zeit braucht.
 
Hallo Leute,

e-Laurin schrieb:
Oha, was hast du denn da programmiert? ^^
Schaut schon etwas wüst mit die Hilfsfunktionen aus.
So unübersichtlich ... die Hilfsfunktionen kann man ja noch später herausnehmen, aber ersteinmal wollte ich klare (Hilfs-)Methoden für die Gradfunktionen haben (da ich mit Grad und eben nicht Rad arbeite).

e-Laurin schrieb:
Die Ursache des Fehlers ist klar: Die Position (4/4) ist Teil der Wand. Und genau da steht der Spieler. Bei (4.0001 / 4.0001) wäre er außerhalb.
Am besten sieht man das, wenn man als Position (8/8) angibt, dann funktioniert die Erkennung rundherum.
Sorry. Das war mir klar und ich habe vergessen die Positionen zu ändern vor dem Hochladen. Dennoch kommt der Fehler erst mit der Bewegung:

Unten habe ich nochmal eine Datei angehangen, wo auch die Bewegung (zumindest nach vorne) eingebaut ist. Und wenn Du Dich jetzt einfach nur drehst ist alles okay. Wenn Du jetzt aber z.B. Dich ein Stück nach rechts unten drehst, etwas vor gehst und dann rundherum drehst wird manchmal bei mir sogar die (neue) Wand einfach ignoriert.

Meine Vermutung ist, dass irgendwo vielleicht beim Runden der x/y-Koordinaten des Spielers etwas schief geht und dass der eigentliche Algorithmus einwandfrei funktioniert. Was meinst Du (Ihr)?

e-Laurin schrieb:
Aber eines steht fest, es sieht super aus!
Danke. Das freut mich! :)

e-Laurin schrieb:
Zu der Performance:
1s/30fps ergibt bei mir 33 Tausendstel. Da ist also sogar noch etwas mehr Luft.
Die Performance ist in diesem Augenblick eigentlich noch Nebensache. Optimiert wird eigentlich nur, wenn es am Ende nicht passt.
Ja, ja ... 33,33333... ach, ich optimiere gerne auch mal zwischendurch, wenn der Algorithmus mehr oder weniger steht.

e-Laurin schrieb:
Dann kann man aber, wie du schon geschrieben hast, sich Sinus- und Cosinus-Tabellen aufstellen. Ein Arrayzugriff geht sehr viel schneller als die beiden mathematischen Funktionen zu berechnen. Du musst aber schauen, wie viele Einträge in den Tabellen Sinn ergeben. Vielleicht sind 3600 besser, da man dort bei großen Entfernungen weniger Präzisionsverlust hat.

Der dafür benötigte Speicherplatz ist egal. 3600 mal 4 Bytes pro Float sind ca. 14 KB. Das kann man vernachlässigen.
Stimmt! Nebenbei: kann mal also wirklich sagen, ich habe 3600 Floting-Point-Zahlen in Single-Precision, 32-Bit = 4 Byte und das sind 3600*4 Byte = 14 KB? Sehr interessant ...

e-Laurin schrieb:
Ansonsten kann man noch schauen, ob man etwas an der Berechnung noch optimieren kann. Da fallen insbesondere die ganzen Methodenaufrufe auf. Ich meine damit die kleinen Hilfsfunktionen für das Runden usw. Der Aufruf an sich benötigt ja auch etwas Zeit.

Mehr Potential sehe ich aber im Moment nicht. Es käme auf eine Analyse an, an welcher Stelle der Computer die meiste Zeit braucht.
Also ich finde 1 ms nicht schlecht für die Berechnung!

Beste Grüße,
CPU
 

Anhänge

Hallo Leute,

qwertzuio schrieb:
Kannst du das fertige Spiel hochladen?

Bzw. auf youtube eine demo machen.

Dankeschön.
Nun im Moment ist es ja noch kein "Spiel", da es ja noch nicht ganz sauber funktioniert :(
(und noch die eigentliche Pseudo-3D-Ansicht fehlt).

Aber geplant ist mal ein (kleines) Spiel. Mal sehen, wie weit ich komme ...

Gruß,
CPU
 
CPU schrieb:
Meine Vermutung ist, dass irgendwo vielleicht beim Runden der x/y-Koordinaten des Spielers etwas schief geht und dass der eigentliche Algorithmus einwandfrei funktioniert. Was meinst Du (Ihr)?
Je mehr ich mir das anschaue, desto eher bin ich der Meinung, dass generell etwas an der Rechnung nicht stimmen kann. Das sind sicher nicht Rundungsfehler oder nicht nur.

Ich wollte testweise einfach mal alle Quadrate blau färben, die von einem Strahl berührt werden. Dazu habe ich in den beiden While-Schleifen map[wallY][wallX] = 2; eingefügt und die render-Methode entsprechend angepasst.
Das Ergebnis war... interessant. Der hat Dinge blau gefärbt, die überhaupt nicht in Blickrichtung liegen. Und er hat Dinge nicht blau gefärbt, obwohl sie in Blickrichtung liegen.
Dazu hat er dazwischen auch mal Array-out-of-bounds-Exceptions geworfen.


CPU schrieb:
Nebenbei: kann mal also wirklich sagen, ich habe 3600 Floting-Point-Zahlen in Single-Precision, 32-Bit = 4 Byte und das sind 3600*4 Byte = 14 KB? Sehr interessant ...
Ja, das kann man so sagen. Es ist zwar nicht exakt, weil er zB irgendwo noch speichert, dass das ein Array ist, dass da Integers drin stehen usw. Das sind aber so wenig Daten (20 Bytes? 30 Bytes?), dass es nicht ins Gewicht fällt.
 
Hey Leute,

e-Laurin schrieb:
Je mehr ich mir das anschaue, desto eher bin ich der Meinung, dass generell etwas an der Rechnung nicht stimmen kann. Das sind sicher nicht Rundungsfehler oder nicht nur.
Schade ... nunja, es geht immer weiter. Gibt es eigentlich einen anderen Ansatz, um die nächste Wand zu finden?

e-Laurin schrieb:
Ich wollte testweise einfach mal alle Quadrate blau färben, die von einem Strahl berührt werden. Dazu habe ich in den beiden While-Schleifen map[wallY][wallX] = 2; eingefügt und die render-Methode entsprechend angepasst.
Dazu brauchst Du nur die einzelnen Zeilen mit "hit.add(new Point(wallX, wallY));" in der Berechnung auszukommentieren, dann werden die automatisch eingefärbt. Die zwei Zeilen darunter und darüber punkten noch den Pfad.
Wie gesagt habe ich das mit einem Strahl (ohne Bewegung) getestet und es hat immer funktioniert ... zurück ans Zeichenbrett ...

Außerdem habe ich jetzt noch die Pseudo-3D-Ansicht ergänzt. Ähm ja, was soll ich sagen: sieht interessant aus, wenn man sich dreht!

Im Moment weiß ich nicht mehr was ich machen soll und werde die Sache mal auf morgen vertagen!

Gruß,
CPU
 

Anhänge

Der mMn üblichste Ansatz für die Kollisionserkennung bei Raytracing ist ein simples Nachschauen, ob der Ray ein Objekt schneidet. Damit man nicht alle möglichen Objekte, die existieren, überprüfen muss, verwendet man Bäume deren Knoten 2, 4 oder 8 Äste haben. Jeder Knoten entspricht dabei einem Raumvolumen oder Fläche, in der sich Objekte befinden können. Die Knoten können Objekte enthalten. Enthalten sie Objekte, erfolgt eine Kollisionsüberprüfung.

Siehe dazu: BSP, Quadtree und Octree
Das Prinzip ist immer dasselbe: Man nehme einen Raum oder eine Fläche, teile sie in n Stücke. Dann nimmt man die Stücke und unterteilt sie wieder in n Stücke usw.


Zur Kantenerkennung im 2D-Raum kann man noch den Ansatz verfolgen, die Ausdehnung einer Fläche zu errechnen. Die Ausdehnung ist quasi eine Linie, die senkrecht zum Strahl steht. Schneidet der Strahl diese Linie (das ist einfache Mathematik), bedeutet das eine Kollision. Die Ausdehnung muss für jeden Strahl einzeln und genau ein Mal berechnet werden. Die bleibt ja gleich, egal wie weit das Objekt vom Strahlenursprung entfernt ist. Nur der Winkel ist da wichtig. Bei dir sind es 90 Strahlen, also sind das 90 Ausdehnungen.
Mit Vektorrechnung kann man da so einiges daraus machen.
 

Anhänge

  • Unbenannt.png
    Unbenannt.png
    7,6 KB · Aufrufe: 104
Moin Moin!
Ich habe zwar keine konkreten Lösungen für dein Problem, allerdings kann ich dir nur empfehlen mal Findbugs und PMDin Eclipse zu integrieren und laufen zu lassen. (Findbugs findet man direkt im Eclipse Marketplace und kann es von da aus installieren)
Ich hab mir mal deinen Code importiert und die beiden Tools mal drüber laufen lassen, und es wurde mir einige Warnungen angezeigt:

Findbugs

Betreffend Zeile 150 & 181:
Bug: integral value cast to double and then passed to Math.ceil in RaycastingTest.rayCast(double)

This code converts an integral value (e.g., int or long) to a double precision floating point number and then passing the result to the Math.ceil() function, which rounds a double to the next higher integer value. This operation should always be a no-op, since the converting an integer to a double should give a number with no fractional part. It is likely that the operation that generated the value to be passed to Math.ceil was intended to be performed using double precision floating point arithmetic.

Betreffend Zeile 48&49:
Bug: Write to static field RaycastingTest.mapWidth from instance method RaycastingTest.init()

This instance method writes to a static field. This is tricky to get correct if multiple instances are being manipulated, and generally bad practice.

Betreffend Zeile 67:
Bug: Switch statement found in RaycastingTest.processKeyEvent(KeyEvent) where default case is missing

This method contains a switch statement where default case is missing. Usually you need to provide a default case.
 
Hallo Leute,

@don.dope: Danke für die Hinweise zu den Programmen :lol:. Ich suche schon länger nach einem ähnlichen Plugin für Eclipse. Nämlich eins, mit dem man genau ausweisen kann wie lange eine Anweisung gedauert hat. Sonst mache ich das immer mit "System.nanoTime()".

@Mein eigentliches Problem:
Nun, nach einer kleinen Fehleranalyse heute morgen bin ich zu dem Schluss gekommen, dass da etwas ganz schön im Argen liegt. Also habe ich alles nochmals auseinander genommen und mich jetzt noch nicht mal mehr an dem Code aus den Tutorials orientiert, sondern mal meine eigenen Ideen entwickelt und verwendet (auch wenn sie natürlich davon beeinflusst sind). Und siehe da: es ist sogar etwas dabei heraus gekommen, dass sich sehen lassen kann. Im Anhang die Java-Datei - schon mit der Pseudo-3D-Ansicht. Man kann sich jetzt auch bewegen, ohne dass etwas schief läuft. Und drehen natürlich auch.

Ich würde mich freuen, wenn Ihr mal drüber schauen könntet und ein Feedback geben könntet. Eigentlich dürften zwar keine Fehler mehr drin sein, aber man weiß ja nie! Die Pseudo-3D-Ansicht ist doch frei vom Fisheye-Effekt, oder?

Aber Achtung: momentan ist die Schleife für das Raycasting noch statisch (sowie einiges Andere auch). D.h. es wird 40 Einheiten weit horizontal und vertikal gegangen. Dannach ist schluss. Natürlich kann man das noch äußerst Performant mit Abbruchbedingung und vergoldetem "break;" implementieren. Doch mir ging es zunächst einmal darum den Algorithmus zum Laufen zu bekommen.

Achso: anbei noch ein Bild. Ist das Java Schuld, dass die blauen Strahlen so unschön gezeichnet werden? Ich plediere auf jeden Fall für Java, weil ja die Pseudo-3D-Ansicht okay ist.

Viele Grüße, :)
CPU

P.S.: Weil hier ja schon gefragt wurde, ob ich das fertige Spiel online stelle/auf YouTube zeige überlege ich, ob ich nicht ein Tutorial zu Raycasting schreibe. Vielleicht interessiert es ja andere Leute. Warum? Nun ja ich finde, dass das [1] Tutorial (siehe erster Beitrag) unzureichend auf die konkrete Umsetzung eingeht und nur die mathematischen Hintergründe behandelt. Das [2] Tutorial ist zu stark in der Programmierung und erklärt eigentlich garnicht, wie das Raycasting jetzt genau funktioniert. Ich finde es fehlt ein Tutorial, das Zeile für Zeile des Kernalgorithmus' durgeht und sagt, warum die Zeile gebraucht wird, was die mathematischen Hintergründe sind und was aus technischer Sicht zu beachten ist! Ist das eine gute Idee oder die Arbeit nicht wert? ... war nur so eine Idee ... :freaky:
 

Anhänge

  • unsauber.gif
    unsauber.gif
    32,8 KB · Aufrufe: 153
  • WallTest.txt
    WallTest.txt
    6,3 KB · Aufrufe: 188
Herzlichen Glückwunsch, dass du das hinbekommen hast! :daumen:

Wenn du ein Tutorial dazu machst, werde ich mir das definitiv durchlesen. Mit Nacharbeiten wird es da bei mir schwerer; ich habe ein paar eigene Projekte in der Warteschlange. Meeh, der Tag hat zu wenig Stunden.


Das mit den Linien hängt damit zusammen, dass du eine begrenzte Anzahl Strahlen losschickst. Es sind nicht genug, um jeden Winkel einer Fläche oder einer Wand beliebig genau abzutasten. Also entweder lässt du es so, oder du wirfst mehr Strahlen raus (zB in Halbgradschritten).


In deinem Code hat sich wieder viel getan. Die ganzen kleinen Methoden hast du eliminiert und Codeabschnitte hast du mit einem Kommentar versehen. Es ist doch so gleich viel übersichtlicher. :)

Was ich aber noch anmerken möchte:
- Es zählt als Best Practice, wenn man globale und lokale Variable an den Anfang der Klasse und Methode setzt. So hat man einen zentralen Ort. Bei dir sind sie im Moment noch gerne in der Gegend verteilt, weswegen andere Leute etwas länger nach der Deklaration suchen.
Ob man das so machen will, ist eine reine Stilfrage.

- Da der Code nun funktioniert, und sich am Algorithmus nicht mehr wesentlich etwas verändern wird, wird es langsam Zeit für Doc Comments. ;)

- Die Sichtbarkeiten sind etwas komisch gesetzt. Es ist ein buntes Durcheinander von public, protected, private und package. Da solltest du noch einmal drüber gehen.

Das sind aber alles hauptsächlich nur Kleinigkeiten.
 
Zurück
Oben