Abend Community,
seit einigen Tagen arbeite ich an einer neuen TopDown Engine. Bis jetzt lief alles mehr oder minder gut, doch irgendwann kommt ja immer der Punkt wo man stundenlang im selben Code rumrührt und nichts mehr gebacken kriegt
Nach dem fünften Tage Dauerprogrammieren mit jeweils großzügigen 6 Stunden Schlaf scheint mein Hirn die Konsistenz eines Smoothies erlangt zu haben, denn klare Gedanken zu fassen gestaltet sich mitunter als schwierig und selbst das Verfassen dieses Textes gleicht eines mentalen Marathons, daher entschuldigt, falls ich mich im weiteren Verlauf unglücklich artikulieren sollte.
Um langsam zur Sache zu kommen, bis jetzt habe ich eine N-Gon basierte Lichtengine so wie Kollisionsengine geschrieben. Beide laufen Hand in Hand miteinander und beziehen als Rohdaten die Eckpunkte eines Polygons (N-Gons) für alle weiteren Berechnungen. Hier wird zwichen statischen und dynamischen Objekten unterschieden. Die Eckpunkte (von nun an "casts") werden für die dynamischen in Echtzeit berechnet (dies geschieht zur Optimierung nur wenn benötigt, etwa wenn im Einflussbereich eines Lichtkegels oder bei "Kollision" (simples place_meeting() )mit dem Spieler). Bei bspw. einem rotierenden Würfel werden die casts also nicht neuberechnet, wenn diese momentan keine Bedeutung haben.
Alle N-Gone haben Grundlegend folgende Attribute :
Eine Variable cast_points, diese hält die Anzahl (n) an casts(Eckpunkten).
Ein Array cast_x_raw[n] sowie cast_y_raw[n], diese halten die casts als offsets relativ zu x,y des Objekts.
Als Grundlage habe ich 2 Objekt-templates erstellt, ein Rechteck :: und ein Dreieck .:
Da diese die casts als sprite_xoffset/sprite_yoffset enthalten, können diese im roomeditor beliebig transformiert werden.
Die Engine kann allerdings mit -n casts umgehen, es können also komplexere zusammenhängende Strukturen modelliert werden.
Die oben gelisteten Arrays halten die Offsets für die untransformierte Figur, nach Erstellen der Instanz werden mindestens einmal die casts geupdated und in zwei neue Arrays cast_x[n] und cast_y[n] geschrieben.
Rechteck:
Spoiler anzeigen
Alles anzeigen
Bis jetzt alles super, caster_update() wird hier also einmal ausgeführt und berechnet die transformierten casts.
Beim Licht ist auch alles in Ordnung, alle casts werden richtig berechnet.
Nun kommen wir jedoch zum Problem, der Kollisionsabgrage :
Hier habe ich mir "perfect collsions" mit sliding als Ziel gesetzt, was sogar annähernd funktioniert.
Wichtig ist, dass der Spieler auch in Beweglichen Objekten niemals hängen bleibt, wir also eine Art softbody-collision haben, daher auch der Ansatz mit den Polys.
Beim herkömmlichen Collision-Event des Spielers mit dem Parent wird mein Kollisionsscript aufgerufen.
Hier durchlaufen wir eine Schlaufe n-mal (n=other.cast_points) und verbinden die casts zu Linien (cast_x[0]/cast_y[0] zu cast_x[1]/cast_y[1]...)
Der nächste Punkt lässt sich also mit [n+1] finden. Wir müssen fragen ob [n+1] == cast_points entspricht, ist dies der Fall gibt es keinen Punkt [n+1], wir müssen also zum ersten Punkt [0] verbinden. Somit haben wir individuelle collision_lines welche wir auf Kollision testen können. Trifft nun eine Seite zu, berechnen wir den Normalenvektor dieser (dies bin ich vorher weit komplizierter angegangen, ich bin mir sicher dies ist der Knackpunkt, da ich nun einfach die point_direction um 90Grad drehe, allerdings finde ich keinen passenden Ansatz). Zuletzt bewegen wir den Spieler in Richtung des Normalenvektors, bis keine Kollision mehr zutrifft.
Hier müsste noch ein weiterer Faktor einspielen um zu bestimmen, ob der Spieler in Richtung der Normalen hin oder von ihr weg bewegt werden muss.
Da ich auf keine gescheite Lösung komme, mir aber denke dass dieses Problem relativ simpel zu lösen ist, möchte ich euch um Rat bitten.
Die Problematik äußert sich in sofern, als dass der Spieler an Ecken bzw. sehr dünnen Objekten(allerdings nur von einer Seite) in die falsche Richtung "teleportiert" wird, da er womöglich mit gegenüberliegenden Seiten kollidiert (?) und so in die Falsche Richtung bewegt wird.
Eventuell könnte man den Mittelpunkt der Figur errechnen und dann mit den Koordinaten des Spielers vergleichen um die Richtung zu bestimmen.
Hier bin ich nun auf helle Köpfe angewiesen, denn Mathe war nie meine Stärke und mit Trigonometrie wurde sich auch im Matheunterricht leider nie wirklich beschäftigt.
Ich habe in den letzten Tagen bestimmt öfters sinus/cosinus verwendet als vorher jemals in meinem Leben, mir fehlt defacto das Verständnis mathematischer Funktionen dieses Gebiets und die Leichtigkeit diese anzuwenden.
Anbei das jetzige Kollisionsscript(Aufgerufen im Collision-Event des Spielers mit dem Parent) :
Spoiler anzeigen
Alles anzeigen
Hier habe ich euch noch eine kleine Demo zusammengeschmissen an der praktisch alle Fehler repliziert werden können :
Steuerung : W A S D, Maus
F- Taschenlampe an/aus
Enter- Debug Overlay, malt casts, Mittelpunkte der collision-lines sowie center of mass. Schaltet weitere Funktionen frei(siehe unten).
(wenn Debug Overlay an):
K- Zerstört letztes Licht
N- Zerstört letzten dynamischen Caster
V- VSync an/aus
X- Antialiasing 0,2,4,8
Y- aktiviert/deaktiviert alle Lichter
Mausrad-Helligkeit der Umgebung
DOWNLOAD DEMO
Zuletzt noch was fürs Auge, Screenshots sind von "alt" nach neu.
Danke für jegliche Hilfe/Einwände/Tipps etc !
-Rhazul
seit einigen Tagen arbeite ich an einer neuen TopDown Engine. Bis jetzt lief alles mehr oder minder gut, doch irgendwann kommt ja immer der Punkt wo man stundenlang im selben Code rumrührt und nichts mehr gebacken kriegt
Nach dem fünften Tage Dauerprogrammieren mit jeweils großzügigen 6 Stunden Schlaf scheint mein Hirn die Konsistenz eines Smoothies erlangt zu haben, denn klare Gedanken zu fassen gestaltet sich mitunter als schwierig und selbst das Verfassen dieses Textes gleicht eines mentalen Marathons, daher entschuldigt, falls ich mich im weiteren Verlauf unglücklich artikulieren sollte.
Um langsam zur Sache zu kommen, bis jetzt habe ich eine N-Gon basierte Lichtengine so wie Kollisionsengine geschrieben. Beide laufen Hand in Hand miteinander und beziehen als Rohdaten die Eckpunkte eines Polygons (N-Gons) für alle weiteren Berechnungen. Hier wird zwichen statischen und dynamischen Objekten unterschieden. Die Eckpunkte (von nun an "casts") werden für die dynamischen in Echtzeit berechnet (dies geschieht zur Optimierung nur wenn benötigt, etwa wenn im Einflussbereich eines Lichtkegels oder bei "Kollision" (simples place_meeting() )mit dem Spieler). Bei bspw. einem rotierenden Würfel werden die casts also nicht neuberechnet, wenn diese momentan keine Bedeutung haben.
Alle N-Gone haben Grundlegend folgende Attribute :
Eine Variable cast_points, diese hält die Anzahl (n) an casts(Eckpunkten).
Ein Array cast_x_raw[n] sowie cast_y_raw[n], diese halten die casts als offsets relativ zu x,y des Objekts.
Als Grundlage habe ich 2 Objekt-templates erstellt, ein Rechteck :: und ein Dreieck .:
Da diese die casts als sprite_xoffset/sprite_yoffset enthalten, können diese im roomeditor beliebig transformiert werden.
Die Engine kann allerdings mit -n casts umgehen, es können also komplexere zusammenhängende Strukturen modelliert werden.
Die oben gelisteten Arrays halten die Offsets für die untransformierte Figur, nach Erstellen der Instanz werden mindestens einmal die casts geupdated und in zwei neue Arrays cast_x[n] und cast_y[n] geschrieben.
Rechteck:
GML-Quellcode
- ///caster_init(static, solid)
- cast_points = 4;
- cast_x_raw[0] = -sprite_xoffset;
- cast_y_raw[0] = -sprite_yoffset;
- cast_x_raw[1] = -sprite_xoffset+sprite_width;
- cast_y_raw[1] = -sprite_yoffset;
- cast_x_raw[2] = -sprite_xoffset+sprite_width;
- cast_y_raw[2] = -sprite_yoffset+sprite_height;
- cast_x_raw[3] = -sprite_xoffset;
- cast_y_raw[3] = -sprite_yoffset+sprite_height;
- static = argument0;
- is_solid = argument1;
- if (!static) {
- global.dynamic_casts += 1;
- }
- caster_update(image_angle);
Bis jetzt alles super, caster_update() wird hier also einmal ausgeführt und berechnet die transformierten casts.
Beim Licht ist auch alles in Ordnung, alle casts werden richtig berechnet.
Nun kommen wir jedoch zum Problem, der Kollisionsabgrage :
Hier habe ich mir "perfect collsions" mit sliding als Ziel gesetzt, was sogar annähernd funktioniert.
Wichtig ist, dass der Spieler auch in Beweglichen Objekten niemals hängen bleibt, wir also eine Art softbody-collision haben, daher auch der Ansatz mit den Polys.
Beim herkömmlichen Collision-Event des Spielers mit dem Parent wird mein Kollisionsscript aufgerufen.
Hier durchlaufen wir eine Schlaufe n-mal (n=other.cast_points) und verbinden die casts zu Linien (cast_x[0]/cast_y[0] zu cast_x[1]/cast_y[1]...)
Der nächste Punkt lässt sich also mit [n+1] finden. Wir müssen fragen ob [n+1] == cast_points entspricht, ist dies der Fall gibt es keinen Punkt [n+1], wir müssen also zum ersten Punkt [0] verbinden. Somit haben wir individuelle collision_lines welche wir auf Kollision testen können. Trifft nun eine Seite zu, berechnen wir den Normalenvektor dieser (dies bin ich vorher weit komplizierter angegangen, ich bin mir sicher dies ist der Knackpunkt, da ich nun einfach die point_direction um 90Grad drehe, allerdings finde ich keinen passenden Ansatz). Zuletzt bewegen wir den Spieler in Richtung des Normalenvektors, bis keine Kollision mehr zutrifft.
Hier müsste noch ein weiterer Faktor einspielen um zu bestimmen, ob der Spieler in Richtung der Normalen hin oder von ihr weg bewegt werden muss.
Da ich auf keine gescheite Lösung komme, mir aber denke dass dieses Problem relativ simpel zu lösen ist, möchte ich euch um Rat bitten.
Die Problematik äußert sich in sofern, als dass der Spieler an Ecken bzw. sehr dünnen Objekten(allerdings nur von einer Seite) in die falsche Richtung "teleportiert" wird, da er womöglich mit gegenüberliegenden Seiten kollidiert (?) und so in die Falsche Richtung bewegt wird.
Eventuell könnte man den Mittelpunkt der Figur errechnen und dann mit den Koordinaten des Spielers vergleichen um die Richtung zu bestimmen.
Hier bin ich nun auf helle Köpfe angewiesen, denn Mathe war nie meine Stärke und mit Trigonometrie wurde sich auch im Matheunterricht leider nie wirklich beschäftigt.
Ich habe in den letzten Tagen bestimmt öfters sinus/cosinus verwendet als vorher jemals in meinem Leben, mir fehlt defacto das Verständnis mathematischer Funktionen dieses Gebiets und die Leichtigkeit diese anzuwenden.
Anbei das jetzige Kollisionsscript(Aufgerufen im Collision-Event des Spielers mit dem Parent) :
GML-Quellcode
- ///player_collision(resolution)
- var resolution;
- resolution = argument0;
- if (other.is_solid) {
- for (i = 0; i < other.cast_points; i++) {
- var ii;
- if (i+1 = other.cast_points) {ii = 0;}
- else {ii = i+1;}
- if (collision_line(other.x + other.cast_x[i],other.y + other.cast_y[i],other.x + other.cast_x[ii],other.y + other.cast_y[ii], id, true, false) != noone) {
- var x1,y1,x2,y2,dir;
- x1 = other.x + other.cast_x[i];
- y1 = other.y + other.cast_y[i];
- x2 = other.x + other.cast_x[ii];
- y2 = other.y + other.cast_y[ii];
- dir = point_direction(x1,y1,x2,y2)-90;
- while (place_meeting(x, y, other.id)) {
- //playerHSpeed = 0;
- //playerVSpeed = 0;
- x = x+lengthdir_x(-resolution,dir);
- y = y+lengthdir_y(-resolution,dir);
- }
- x = round(x);
- y = round(y);
- }
- }
- }
Hier habe ich euch noch eine kleine Demo zusammengeschmissen an der praktisch alle Fehler repliziert werden können :
Steuerung : W A S D, Maus
F- Taschenlampe an/aus
Enter- Debug Overlay, malt casts, Mittelpunkte der collision-lines sowie center of mass. Schaltet weitere Funktionen frei(siehe unten).
(wenn Debug Overlay an):
K- Zerstört letztes Licht
N- Zerstört letzten dynamischen Caster
V- VSync an/aus
X- Antialiasing 0,2,4,8
Y- aktiviert/deaktiviert alle Lichter
Mausrad-Helligkeit der Umgebung
DOWNLOAD DEMO
Zuletzt noch was fürs Auge, Screenshots sind von "alt" nach neu.
Danke für jegliche Hilfe/Einwände/Tipps etc !
-Rhazul
132 little bugs in the code. 132 little bugs. Fix a few, set the compiler to stew, 172 little bugs in the code...