PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : Expertenfrage (Rundungsfehler des Makers umgehen - wie?)



Alan
14.01.2009, 19:40
Hi @all,

kommt selten vor, dass ich eine Tech-Frage stelle ich weiß xD Aber die Lage ist ernst...

Also. Ich entwickle gerade ein kleines Shooter-Script. Dabei beherrscht der Held eine Art "Spezialangriff", bei dem die Zeit einfriert. Dann erscheint an einer zufälligen Position nahe bei einem Gegner ein Zielfadenkreuz (per Pic), welches sich auf den Gegner zu bewegt. Sobald es sich über dem Gegner befindet, wird es rot (hier muss der Spieler Enter drücken um zu treffen) und sobald es wieder weiter weg vom Gegner ist, erhält es wieder seine normale Farbe (sobald es den Zielpunkt erreicht, gilt der Schuss automatisch als "verfehlt"). Der Start- und Endpunkt der Animation bilden eine Strecke, in deren exakter Mitte sich der Gegner befindet. Da die Startposition des Fadenkreuzes ein Zufallswert ist, wird die eigentliche "Zielposition" des Pics per Vektorrechnung berechnet:

Ziel.X = Gegner.X + (Gegner.X - Ausgangspunkt.X)
Ziel.Y = Gegner.Y + (Gegner.Y - Ausgangspunkt.Y)

Wie ihr anhand des Anfangstextes schon lesen konntet, muss erstens der Spieler jederzeit dazu in der Lage sein, den Vorgang per ENTER zu unterbrechen (--> Trefferabfrage) und zweitens muss das Pic seine Farbe auf Rot und wieder zurück wechseln können. Das hier geht also NICHT

<>Show Pic (1, Start.X, Start.Y)
<>Move Pic (1, Ziel.X, Ziel.Y, 1.0s)

Stattdessen wird die Strecke "zerstückelt" in Teilstücke von jeweils 0.1 Sekunden. Nehmen wir an, die Gesamtdauer der Pic-Bewegung von Anfang bis Ende dauert 2 Sekunden. Das bedeutet, dass wir 20 Teilstrecken zu jeweils 0.1 Sekunden haben. Rechnerisch bedeutet das, dass wir herausfinden müssen, wie die Pic-Position nach jeweils 0.1 Sekunden aussieht. Das geht so:

Differenz_X = (Endpunkt.X - Anfangspunkt.X) / 20
Differenz_Y = (Endpunkt.Y - Anfangspunkt.Y) / 20

Momentane_Pic_Pos.X = Anfangspunkt.X + (time) * Differenz_X
Momentane_Pic_Pos.Y = Anfangspunkt.Y + (time) * Differenz_Y

... wobei (time) für die Anzahl von durchgeführten Move-Pictures steht, beginnend bei 1.


So weit, so gut. Jetzt das (im Endeffekt simple) PROBLEM:

Differenz_X = (Endpunkt.X - Anfangspunkt.X) / 20
Differenz_Y = (Endpunkt.Y - Anfangspunkt.Y) / 20

... ergibt KOMMAZAHLEN. Der Maker rundet die natürlich und das Pic landet an einem anderen Zielpunkt als mit dieser Formel vorausberechnet:

Zielpunkt.X = Anfangspunkt.X + (Gegner.X - Anfangspunkt.X)*2
Zielpunkt.Y = Anfangspunkt.Y + (Gegner.Y - Anfangspunkt.Y)*2

Das wäre aber für mein Script absolut unabdingbar. Irgendwelche Vorschläge? Cherry vielleicht was Passendes bei der Hand? xD


Greetz!



Alan

Mivey
14.01.2009, 20:05
wie wäre es wenn du sobald der held enter drückst einfach das bild löschst? dann kannst du diese teilstrecken ersparen, es sei denn du willst eine ani des pics durchführen wenn die entertaste gedrückt wird. du könntest auch machen das das pild pixel um pixel ( was aber wohl bei gewissen winkel nicht geht (wenn x und y verhältnis nicht passt),

Alan
14.01.2009, 20:07
du könntest auch machen das das pild pixel um pixel ( was aber wohl bei gewissen winkel nicht geht (wenn x und y verhältnis nicht passt),

Genau DAS ist ja das Problem. Stattdessen muss man hier mit Teilstrecken arbeiten.

Mivey
14.01.2009, 20:11
was ist wenn du bestimmst das die strecke durch irgendeine zahl teilbar sein soll, bestimme die koordinate des ziels, gehe von dieser zufällig in eine richtung aber zb. nur 5 pixel nach x und y, und das zielpunkt des bildes ist dann halt auf der anderen seite vom ziel, dh. du könntest die strecke in 10*1 pixel teilen ( ist nur ein beispiel)

makenshi
14.01.2009, 20:13
Du kannst den PP Compact benutzen. Der erlaubt es Berechnungen per LUA durchzuführen und fertig gerundet zurückgeben zu lassen.

Alternativ hatte ich so ein Problem auch mal. Hab damals nen kleines Eventskript dazu geschrieben:

http://home.arcor.de/makenshi2k/Runden.png

Alan
14.01.2009, 20:14
@Mivey: Ich will den Startpunkt aber zufällig haben, also

Start.x = Gegner.X +/- Zufallszahl
Start.Y = Gegner.Y +/- Zufallszahl

und der Zielpunkt ist dann quasi die "gespiegelte" Strecke, sodass der Gegner wirklich exakt in der Mitte steht.

Es wäre theoretisch möglich, als Zufallszahlen nur Vielfache von 20 zuzulassen (also 20, 40, 80 etc.), damit die Differenz in JEDEM Fall durch 20 teilbar ist, aber da 40 Pixel mir schon zu weit entfernt sind, fällt diese Methode leider auch flach.


@Makenshi:

*Runden* kann der Maker ja richtig (also mathematisch). Das ist nicht das Problem. Das Problem ist, dass beim Runden eine Ungenauigkeit entsteht, die bei mehrfacher Anwendung (wie hier) das eigentliche Ziel bei Weitem verfehlt. Es müsste eine Möglichkeit geben, die Rundungsungenauigkeit auszugleichen. Eliminieren kann man sie nicht, der Maker kann kein Pic auf der Position 23,5/67,54 darstellen. Halbe Pixel gibt's keine xD Mein Ansatz diesbezüglich:

Der Rundungsfehler3 multipliziert mit dem Faktor X muss ein exaktes Vielfaches (Y) der Zahl 10000 ergeben.
X entspräche dann der Anzahl von Schritten bis zur Addition. Der addierte Wert entspricht Y.

Rundungsfehler3*X = 10000*Y

==> zwei Varis, eine Gleichung, nicht zu lösen.

Lua könnte hier in der Tat helfen, da sich dort dann die Rundungsungenauigkeiten dahin gehend reduzieren könnten, da Lua mit Kommazahlen rechnen kann.


!!!!

Makenshi, das ist die LÖSUNG! Man berechnet die 20 Koordinatenpaare im Voraus und dividiert DANN durch den Multiplikator.
Nochmal zum Mitdenken: Multipliziert man alle Werte durch ein Vielfaches von 10, rutscht das Komma nach hinten. Ich nehme hier zum Beispiel 10000. Der "Teilungsfaktor" ist hier natürlich 20, also 20 Animationsschritte.

Also zum Beispiel:

Pos1 = Anfangsposition * 10.000
Pos2 = Anfangsposition * 10.000 + (Differenz / Teilungsfaktor)*10.000
Pos3 = Pos2 + (Differenz / Teilungsfaktor) * 10.000
etc.



Und zum SCHLUSS dann:

Pos1 = Pos1 / 10000
Pos2 = Pos2 / 10000
Pos3 = Pos3 / 10000


Damit wird die Rundungsungenauigkeit nicht 20 mal angewandt, sondern de facto nur 1 mal. Und alles, was hinter der fünften Kommastelle ist, interessiert sowieso keinen xD


Danke Makenshi, das war der entscheidende Denkanstoß!

Mivey
14.01.2009, 20:16
dann nutz den PPC der kann mit relativ genauen gleitkommazahlen arbeiten. ( lua)

makenshi
14.01.2009, 20:25
@Makenshi:

*Runden* kann der Maker ja richtig (also mathematisch). Das ist nicht das Problem. Das Problem ist, dass beim Runden eine Ungenauigkeit entsteht, die bei mehrfacher Anwendung (wie hier) das eigentliche Ziel bei Weitem verfehlt. Es müsste eine Möglichkeit geben, die Rundungsungenauigkeit auszugleichen. Eliminieren kann man sie nicht, der Maker kann kein Pic auf der Position 23,5/67,54 darstellen. Halbe Pixel gibt's keine xD Mein Ansatz diesbezüglich:

Der Rundungsfehler3 multipliziert mit dem Faktor X muss ein exaktes Vielfaches (Y) der Zahl 10000 ergeben.
X entspräche dann der Anzahl von Schritten bis zur Addition. Der addierte Wert entspricht Y.

Rundungsfehler3*X = 10000*Y

==> zwei Varis, eine Gleichung, nicht zu lösen.

Lua könnte hier in der Tat helfen, da sich dort dann die Rundungsungenauigkeiten dahin gehend reduzieren könnten, da Lua mit Kommazahlen rechnen kann.

Lua wird dir definitv helfen dank Kommavariablen. ^^
Mein Skript hat mir damals in dem Thema allerdings auch geholfen, da ich halt Divisionen mit dem Skript durchgeführt habe. So ließen sich die Fehler reduzieren, da zwar nicht MIT Nachkommastellen gerechnet wurde, jedoch wurde wenigstens soweit aufgerundet das keine großen Beträge verloren gingen. Der Maker rundet nämlich afaik nicht, sondern schneidet simpel ab.
Dürften ja Integervariablen sein. ( Vorsicht, ohne Gewähr )

Edit:
Genau, du hast es. ^^

Cherry
14.01.2009, 20:51
der PPC eignet sich dafür aber nicht, da es relativ lange zum Starten braucht, da externen Programm (bis zu 1 Sek.!), und das bei jedem Aufruf.

Der große Power Patch (v0.43b) kann dir da aber besser helfen, da er nebenher läuft.

Bzw. bist du ja auch PP0.85-Betatester, daher könnte man da vllt auch was für dich "drehen".

Alan
15.01.2009, 13:16
Bzw. bist du ja auch PP0.85-Betatester, daher könnte man da vllt auch was für dich "drehen".

??? Seit wann bin ich Betatester? Da weiß ich ja gar nix davon *lol*

Cherry
15.01.2009, 21:20
LOL. Du hast du doch ganz ganz zu Beginn gemeldet, wie es dann soweit war, habe ich dir EIGENTLICH die Zugangsdaten geschickt. O.o

Alan
16.01.2009, 13:13
Muss schon verdammt lange her sein, dass ich mich nicht mehr erinnern kann. Aber Zugangsdaten hab' ich garantiert keine bekommen, daran könnte ich mich nämlich mit Sicherheit erinnern.

CapSeb
18.01.2009, 00:34
Alan, warum machst du das so komplizert? ^^

Der Maker rechnet immer von links nach rechts, das heißt von links nach rechts summieren sich Rundungsfehler auf. Wenn du den Rundungsfehler erst ganz zum Schluss entstehen lässt, d.h. ganz rechts, dann kann sich da nichts aufsummieren:

position
= start + (ziel-start)/20(gerundet)*teilstueck
= (ziel-start)*teilstueck /20(gerundet) + start

Bei jedem Teilstück, das was du als "time" bezeichnet hast, wird die Gesamtstrecke erneut geteilt. Da brauchst du dann keinen Patch, kein Lua und auch keine durch mal-tausend entsehende Riesenzahlen. Guck dir die Umstellung der Formelstücke genau an. Das ist sehr komprimierte Information.

Im Maker heißt das dann:

position = ziel;
position -= start;
position *= teilstueck;
position += 10;
position /= 20;
position += start;

das "plusgleich zehn" ist damit er nicht abschneidet sondern rundet.
10 ist die Hälfte von 20. Er soll "abrunden", wenn die Zahl, die geteilt wird, bis zu 10 zu groß ist. Das macht er immer, indem er abschneidet. Aber er soll "aufrunden", wenn sie um mehr als 10 zu groß ist. Das kann der Maker ja eigentlich nicht. Mit dem Plus-zehn-Trick geht beides.

Z.B. ist 47 um sieben zu groß. Er sollte eigentlich abrunden.
Wenn wir 10 zu der Zahl addieren, schneidet er zum Glück immer noch ab, denn 57/20 ist kleiner als 3. Tada, ein Abrunden simuliert. Und natürlich ist es kein Glück sondern normal.
Wenn sie mehr als 10 zu groß ist, sollte er eigentlich aufrunden. Aufrunden entspricht in den nächsten 20er-Bereich zu kommen und abzuschneiden. Wenn wir 10 z.B. zu 52 hinzuaddieren (52/20 müsste aufgerundet werden), kommen wir auf 62. Das befindet sich im nächsten 20er-Abschnitt und ergibt beim Durch-20-Teilen und Abschneiden 3. und 52/20 ist gerundet ja wirklich 3.
Funktioniert also.

Mathematisch lautet das Prinzip:

(Dividend+[Divisor/2])/Divisor
= Dividend/Divisor + [Divisor/2]/Divisor
= Dividend/Divisor + 0,5

für [(Dividend/Divisor modulo 1) < 0,5] gilt: Dividend/Divisor + Nachkommastelle
für [(Dividend/Divisor modulo 1) >= 0,5] gilt: Dividend/Divisor + 1,x

Ersteres ist die Abrundebedingung und enspricht beim Abschneiden dem Abrunden.
Zweiteres ist die Aufrundebedingung und enspricht beim Abschneiden dem Aufrunden.

Mathe statt Umstand. ;)


http://www.multimediaxis.de/images/smilies/old/s_017.gif CapSeb http://www.multimediaxis.de/images/smilies/old/s_065.gif

Alan
18.01.2009, 10:57
Whoa jetzt kommen wieder die Mathematiker daher, ellenlange Formeln im Gepäck und behaupten, ihre Lösung sei einfacher xD

Ne, Spaß beiseite. Dein Rundungs-"Trick" ist sicherlich schön und gut. Aber. Das Runden an sich ist - as said - nie das Problem gewesen. Das habe ich ja schon vorher mit Makenshi besprochen. Der Titel des Threads ist vielleicht etwas... ungünstig gewählt, da der Rundungsfehler nicht das Kernproblem darstellt, wie sich im weiteren Verlauf der Diskussion herausgestellt hat. Der Maker rundet nämlich durchaus richtig, nur hat mein usprünglicher Code den Rundungsfehler immer wieder "mitgeschleppt" und ihn durch nichts wieder ausgleichen können. De facto ist meiner Meinung nach die Multiplikation mit 10.000 (oder einer noch höheren Zahl) die einzig wirklich gangbare Möglichkeit zur effektiven Lösung dieses Problems mit den Mitteln, die einem im RPG-Maker zur Verfügung stehen, da somit das Rechnen mit Kommazahlen zumindest soweit "simuliert" wird, dass die maximale Abweichung vom vorausberechneten Ergebnis höchstens 1 Pixel betragen kann. Natürlich tritt der Fehler nach wie vor auf, wenn man dann mit den gerundeten Koordinaten weiterrechnet, aber wenn man für die weitere Berechnung auf die exakten, mit 10.000 multiplizierten, Koordinaten zurückgreift, beträgt der maximale Rundungsfehler nur 1 Pixel.

Trotzdem vielen Dank für deine Anregungen!


Greetz!


Alan

Kyuu
18.01.2009, 13:56
[...] Der Maker rundet nämlich durchaus richtig [...]

Das ist falsch, wurde vor mir aber auch schon angemerkt. Alle mathematischen Operationen im RPG Maker sind Integeroperationen, d.h., nach einer Division entsteht ein ganzzahliges Ergebnis und ein ganzzahliger Rest (Assembler-Programmierern müsste das bekannt vorkommen), wobei nur das Ergebnis verwendet wird und der Rest verworfen. Das entspricht dem Abschneiden der Nachkommastellen bei einer Gleitkommazahl, oder - für positive Gleitkommazahlen - dem Abrunden (Gaußklammer).

Bei deiner bisherigen Lösung verwendest du Festkommazahl-Arithmetik, d.h., du skalierst die Zahlen vor den Berechnungen mit einem Faktor, führst die Berechnungen bei einer höheren Präzision durch und konvertierst das Ergebnis dann in eine Gleitkommazahl mit dem Teilen durch den Skalierungsfaktor, wobei automatisch eine Ganzzahl entsteht, da die Division eine Integeroperation ist.
Das ist im Grunde die Standard-Vorgehensweise, die so auch in Sprachen wie C/C++ Einsatz findet, wenn die hohe Ausführungsgeschwindigkeit von Integeroperationen gewünscht wird, man aber nicht auf die Präzision von Gleitkommaoperationen verzichten will (wobei man da aber aus Optimierungsgründen mit Zweierpotenz-Faktoren rechnet).

Insofern ist dein Ansatz schon richtig, allerdings (wie schon oben gesagt) gehst du falsch in der Annahme aus, dass das Ergebnis gerundet wird. Um tatsächlich zu runden, musst du, wie CapSeb schon offenlegte, eine Korrektur vor der Konvertierung durchführen, indem du die Hälfte des Divisors hinzuaddierst.
Aufpassen musst du aber bei negativen Zahlen, denn dann musst du abziehen, statt addieren.

Alan
18.01.2009, 15:25
Oh, der Maker schneidet die Nachkommastellen also einfach ab? War mir auch neu - ich dachte immer, er rundet ab x,5 auf und darunter ab. Aber - man lernt nie aus! Von daher könnte es durchaus sein, dass man einmal auf den Trick von CabSeb zurückgreifen muss... ich werde ihn im Hinterkopf behalten, thx!



Greetz!


Alan

CapSeb
18.01.2009, 22:37
De facto ist meiner Meinung nach die Multiplikation mit 10.000 (oder einer noch höheren Zahl) die einzig wirklich gangbare Möglichkeit zur effektiven Lösung dieses Problems mit den Mitteln, die einem im RPG-Maker zur Verfügung stehen. [...]
Von daher könnte es durchaus sein, dass man einmal auf den Trick von CapSeb zurückgreifen muss...
Naja, man muss es so sehen: Der zweite Teil meines Posts bezieht sich auf das "+10". Wenn man will, kann man das einfach weglassen.
Dann gilt es nur noch jedes Mal zu rechnen:


position = ziel;
position -= start;
position *= teilstueck;
position /= 20;
position += start;

Verbrauchte Variablen sind dabei vier Stück - "start", "ziel", "teilstück" und "position". Das Ganze in einem Loop 20 Mal ausführen, das war's. Und die Abweichung beträgt dann null oder ein Pixel. Niemals mehr.
Gangbar, wie du es nennst, ist es auf jeden Fall. Ich mache das selbst immer so, mit guten Ergebnissen. Beim Mal-10000 musst du darauf achten, nicht die Variablen-Obergrenze zu überschreiten. Auch wenn das hier nicht der Fall sein dürfte, schrecke ich deswegen allgemein davor zurück.
Aber nimm es am besten als allgemeine Wissenserweiterung. Das Mal-10000 ist ein funktionierendes Prinzip. Beim nächsten Problem brauchst du aber vielleicht eine andere Lösung. ;)


http://www.multimediaxis.de/images/smilies/old/s_017.gif CapSeb http://www.multimediaxis.de/images/smilies/old/s_065.gif

kommen wieder die Mathematiker daher, ellenlange Formeln im Gepäck...
Ist doch nicht lang! Guck dir mal wirklich lange Formeln an! ^^

Alan
19.01.2009, 13:14
Aber nimm es am besten als allgemeine Wissenserweiterung. Das Mal-10000 ist ein funktionierendes Prinzip. Beim nächsten Problem brauchst du aber vielleicht eine andere Lösung. ;)


Ich nehme es auf jeden Fall als "allgemeine Wissenserweiterung", denn das kann nie schaden! Und gerade beim Maker braucht man OFT alternative Lösungen xD



kommen wieder die Mathematiker daher, ellenlange Formeln im Gepäck...
Ist doch nicht lang! Guck dir mal wirklich lange Formeln an! ^^

Einfach zu sagen, dass man im Zähler des Bruches die Hälfte des Nenners dazuaddieren muss, um den Maker "richtig" runden zu lassen, hätte allerdings auch leicht ausgereicht xD Geht auch so - ganz ohne Formel xD Trotzdem danke für alles ^^


Greetz!


Alan