Entwickler-Ecke

Sonstiges (Delphi) - Effizienz bei Bewegungen mittels Timer


beastofchaos - Fr 24.12.10 12:31
Titel: Effizienz bei Bewegungen mittels Timer
Hallo Leute,
ich arbeite mit meinem Freund an einem Minigolfspiel, geschrieben mit Delphi. Wird werden es für ein wichtiges Referat in der Schule brauchen ( haben noch ein halbes jahr Zeit ). Hab da aber so einige Fragen, da mit Delphi es immer wieder Probleme gibt.

Vorweg: Ein gutes Vorbild ( nicht mit Delphi geschrieben ) ist dieses Spiel, das doch sehr Spaß macht und einwandfrei funktioniert:
http://de.playforia.com/spielen/minigolf/

Meine erste Frage:
Kann man den Timer.Interval irgendwie niedriger als 1 setzen, da sich die Kugel sonst zu langsam bewegt ( Im timer soll dich die Left-/Top-Position immer um 1 bewegen)
Wenn das nicht kleiner geht, dann hab ich das Problem, dass ich im Timer sonst die Kugel für hohe Geschwindigkeiten 2 oder 3 Positionen auf einmal weiterspringen lassen muss. Dabei kann er aber über meine Hindernisse eventuell springen (if Kugel.Left = Hindernis.Left then *Winkel berechnen und Laufbahn der Kugel spiegeln*), weil ich ja nur bestimmte Positionen als Hindernis definiere :/

Kann euch ja mal meine bisherige Arbeit zeigen, wenn es auf diese Frage ne Lösung gibt. Hab da tausende Problem mit Winkelberechnug, Linie zur Maus ziehen, etc.

Freue mich auf Antwort
MfG Thomas

Moderiert von user profile iconNarses: Binaries aus den Anhängen entfernt.


Gausi - Fr 24.12.10 12:37

Hallo und :welcome: in der Entwickler-Ecke,

Timer-Intervall kleiner als 1 geht nicht. Genau genommen geht auch ein Timerintervall kleiner als ungefähr 25 nicht, da Windows bei weniger nicht mehr mitmacht.

Außerdem: Für die Anzeige wären das dann 1000 Frames pro Sekunde - das ist doch viel zu viel. ;-)
Für die Kollisionsabfrage mit den Hindernissen musst du dir dann was anderes überlegen - ein erster Anfang wäre eine Überprüfung per "<=" oder ">=" anstelle von "=". :)


platzwart - Fr 24.12.10 12:40

Kleiner 1? Das wäre dann ja über 1000 Schritte/ Pixel pro Sekunde?!? :gruebel:

Mach bitte nicht den Fehler, beim Auslösen des Timers immer eine feste Schrittweite zu nehmen. Anstatt dessen solltet ihr besser ein paar Formeln zu Herzen nehmen, die ihr im Physikunterricht gelernt haben solltet. Arbeitet besser mit einer Geschwindigkeit, die ihr euch merkt und rechnet pro Timerintervall aus, wo der Ball sein müsste und zeichnet ihn dort. Weg = Strecke mal Zeit, so als erster Ansatz :wink:


Bergmann89 - Fr 24.12.10 12:44

Hey,

@Gausi: das kann bei schnellen Geschwindigkeiten und hohen Intervallen zu Fehlergebnisen führen.

Ich würde es so machen:

Quelltext
1:
2:
3:
4:
5:
6:
7:
-neue Position berechnen
-mit der neuen Position auf Hindernise prüfen
-wenn die Position hinter einem Hindernis ist, dann:
  -bestimmt den Punkt an dem der Ball das Hindernis berührt
  -berechnen den zurückgelegten Weg
  -lass den Ball am Hinderniss abprallen (Richtungsänderung)
  -lasse den Ball soweit weiter rollen, bis die gewünschte entfernung für diesen Schritt erreicht ist


Ich hab mal sowas ähnliches gemacht, un das hat 1a funktioniert. Hat auch mit sehr hohen Geschwindigkeiten und Intervallen die richtigen Ergebnise geliefert.

MfG Bergmann.


Gausi - Fr 24.12.10 12:50

user profile iconBergmann89 hat folgendes geschrieben Zum zitierten Posting springen:
@Gausi: das kann bei schnellen Geschwindigkeiten und hohen Intervallen zu Fehlergebnisen führen.


Das ist mir schon klar. ;-)

user profile iconGausi hat folgendes geschrieben Zum zitierten Posting springen:
[...] ein erster Anfang wäre [...]


Aber man muss ja mal klein anfangen. Geht ja schließlich auch nicht um Maxi-Golf. :mrgreen:


F34r0fTh3D4rk - Fr 24.12.10 13:12

Ich verweise immer sehr gerne hierauf: http://wiki.delphigl.com/index.php/Timebased_Movement
Damit sollte das Problem mit dem Timer erledigt sein. Falls es noch Probleme mit der Berechnung von Bewegungen/Kollisionen geben sollte, kann ich dieses Tutorial hier empfehlen: http://wiki.delphigl.com/index.php/Tutorial_Lineare_Algebra


beastofchaos - Fr 24.12.10 17:59

user profile iconBergmann89 hat folgendes geschrieben Zum zitierten Posting springen:
Hey,
Ich würde es so machen:

Quelltext
1:
2:
3:
4:
5:
6:
7:
-neue Position berechnen
-mit der neuen Position auf Hindernise prüfen
-wenn die Position hinter einem Hindernis ist, dann:
  -bestimmt den Punkt an dem der Ball das Hindernis berührt
  -berechnen den zurückgelegten Weg
  -lass den Ball am Hinderniss abprallen (Richtungsänderung)
  -lasse den Ball soweit weiter rollen, bis die gewünschte entfernung für diesen Schritt erreicht ist



So ähnlich dachte ich mir das auch, aber wie stellst du im Quelltext dann fest, dass zwischen der ersten und zweiten Position ein hindernis ist?


Ich beginne währenddessen schon einmal mit der zweiten Frage(die später auch zur dritten führen wird :D):
Hier mal ein Bilder des Delphi-x/y-"Gitters" (jpg, 158.49 KB)
Es zeigt an, dass eine Linie zur Maus erzeugt wird und ich habe mal graue Kugeln gezeichnet, die die zu zeichnenden Positionen dar stellen sollen mit abnehmender Geschwindigkeit, wenn die procedure "Maus.Click" aktiviert wird.

Zur Schussrichtung und der Stärke benutze ich wie in meinem Vorbild eine Linie, die bis zur Mausposition erzeugt wird.
Dabei müssen die Positionen der Kugel später so genau bestimmt werden, dass es realistisch wird. Leider gibt es keine Kommazahlen in dem Gitter,
weshalb ich trunc() benutzen muss. Meint ihr, man wird die minimale Positionenverschiebung sehen können?
Nebenbei hab ich noch ne kleine Frage zur Mausposition: Wenn ich Cursor.Pos.X/Y auslese liest er die Position auf dem Bildschirm und nicht in dem Form. Deswegen muss ich die Formel "X:=Cursor.Pos.X - Form1.Left". Selbst dann ist die Position nicht genau wegen der größe des Bildschimrs und ich muss 3 bis 20 nochmal dazu abziehen.
Gibt es keinen Befehl wie "X:=Form1.Cursor.Pos.X"?

MfG Thomas
PS: Danke schonmal für die viiielen Antworten auf das Thema ;)

Moderiert von user profile iconNarses: Bild als Anhang hochgeladen.


glotzer - Fr 24.12.10 18:08

soo wegen dem komma problem: einfach intern mit extended rechnen und erst beim zeichnen runden


beastofchaos - Fr 24.12.10 18:15

user profile iconglotzer hat folgendes geschrieben Zum zitierten Posting springen:
soo wegen dem komma problem: einfach intern mit extended rechnen und erst beim zeichnen runden


ich benutze zur Berechnung lieber meinen Standard ( Real ) und die Position rund ich mit trunc(). obwohl mir grad einfällt, dass trunc() nur abrundet. Gibt es noch einen Befehl, der ab ",5" auch aufrundet? Und nebenbei kennt ihr auch einen Befehl, der den Betrag berechnet ( also x = betrag(-5))? Das bräuchte ich nämlich zur Winkelberechnung beim Zusammenstoß mit Hidnernis.



user profile iconGausi hat folgendes geschrieben Zum zitierten Posting springen:
Timer-Intervall kleiner als 1 geht nicht. Genau genommen geht auch ein Timerintervall kleiner als ungefähr 25 nicht, da Windows bei weniger nicht mehr mitmacht.

Außerdem: Für die Anzeige wären das dann 1000 Frames pro Sekunde - das ist doch viel zu viel. ;-)
Für die Kollisionsabfrage mit den Hindernissen musst du dir dann was anderes überlegen - ein erster Anfang wäre eine Überprüfung per "<=" oder ">=" anstelle von "=". :)


Und danke, mir fällt auch erst jetzt auf, dass es zwischen Interval 25 und 1 keinen Unterschied gibt bzw. windows es nicht schneller machen kann ;) damit hab ich glaub ich schon ein problem mit der abfallenden Geschwindigkeit behoben ( genauere Werte beim Abfallen ).


PS: außerdem wünsch ich allen frohe Weihnachten, die innerhalb der nächsten Stunde nicht mehr on kommen ( solange bin ich ncoh da ).

Moderiert von user profile iconNarses: Zitat gekürzt.


platzwart - Fr 24.12.10 18:16

Und bitte für jede Frage ein eigenes Topic aufmachen, sonst verlieren wir hier alle die Übersicht, Danke ;)

P.S.:
1) Dein 'Standard' real ist aber kein Standard für Delphi mehr. Daher bitte aktuelle Datentypen verwenden.
2) Trunc() -> Round()
3) Betrag -> Abs()

... wo wir wieder beim Thema wären, dass alle Fragen/Antworten gemischt werden ;)


jaenicke - Fr 24.12.10 18:26

Wegen der Datei bei Rapidshare:
Häng die bitte einfach an. Das macht den Download einfacher, außerdem ist die Datei dann auch später noch da, falls jemand den Thread findet und sich die Sachen anschauen möchte. ;-)


beastofchaos - Fr 24.12.10 18:42

Ok, im Anhang sind beide Dateien. Lösche die beiden Links.
Schonmal vielen Dank für die Antworten, ich werde jetzt zu jeder wichtigen Frage ein Topic erstellen.

Bis zum nächster Thema ;)

Moderiert von user profile iconNarses: Anhänge in den ersten Beitrag verschoben.


Bergmann89 - Fr 24.12.10 23:24

Hey,

nochmal zur kollisionsabfrage. Betrachte das Hinternis, sowie den Vektor vom aktuellen Punkt zum neuen Punkt als Linie. Jetzt berechnest du den Schnittpunkt der beiden Linien. Jetzt musst du nur noch gucken, ob der Punkt auf der Linie in den Richtigen Grenzen liegt: linke bzw. rechte Grenze des Hindernissen und Abstand zum aktuellen Punkt. Wenn der Punkt zwischen den Endpunkten des Hindernises liegt, und der Abstand zum aktuellen Punkt kleiner als die Länge des Vektors ist, dann Musst du eine Kollision berechnen.
Nochwas zur Bestimmung der Maus-Koordinaten. Einfach auf deiner Zeichenebene das OnMouseMove-Ereignis benutzten, da werden die Koordinaten in Anhängigkeit zum Objekt übergeben.

MfG Bergmann.


beastofchaos - Sa 25.12.10 02:57

Moderiert von user profile iconNarses: Komplett-Zitat des letzten Beitrags entfernt.

Ich hab das schon in einem anderen Forum gefunden. Und zwar arbeite ich nicht mit einzelnen Intergerwerten, sondern TPoint.

"Maus=TPoint;
.
.
.
Maus:=ScreenToClient(Mouse.CursorPos);
.
.
."

Habs das ganze erst in einem Timer gebunden, aber dan narbeitet das Programm die ganez Zeit. Deswegen ist ein MousEMov deutlich besser. Hab zur Hidnernis berechnung schon eine Lösung gefunden, aber kommt ja eig nicht hierein. WENN ich da noc hFragen hab erstell ich ein neues Topic dazu. Vll schaff ichs auch cniht alleine mit der Vektor zum Schießen der Kugel oder der Spiegelung der Kugel, aber in der Theroie, hab ichs schon ziemlich gelöst. Mach das alles erst mal auf nem Blatt Papier ;)

Vielen vielen Dank und nochmal Frohe Weihnachten, Leute. Bis zum nächsten Topiceintrag ;)


jaenicke - Sa 25.12.10 03:24

user profile iconbeastofchaos hat folgendes geschrieben Zum zitierten Posting springen:
Deswegen ist ein MousEMov deutlich besser.
Wenn es denn so gewollt ist, dass nix passiert, wenn die Maus nicht bewegt wird, ist es besser, ja.


beastofchaos - Sa 25.12.10 22:59

Moderiert von user profile iconNarses: Komplett-Zitat des letzten Beitrags entfernt.

Naja, das stört mich nicht, aber iwann ist doch auch das Programm überlastet, wenn ich lauter Timer ununterbrochen laufen lass.


F34r0fTh3D4rk - So 26.12.10 15:35

Man braucht nicht mehr als einen Timer. Wenn es ein Performance-Problem gibt, dann liegt es woanders.


beastofchaos - So 26.12.10 16:34

user profile iconF34r0fTh3D4rk hat folgendes geschrieben Zum zitierten Posting springen:
Man braucht nicht mehr als einen Timer. Wenn es ein Performance-Problem gibt, dann liegt es woanders.

Naja, mein Timer für die bewegung der Minigolfkugel muss ja immer langsamer werden. Einer anderer Timer soll nur, wenn die Kugel sich nicht bewegt, Eine Linie von Kugel zur Maus erzeugen. Keien Ahnung, aber ich glaube ich werde insgesamt 3 Timer brauchen


Boldar - So 26.12.10 17:18

user profile iconF34r0fTh3D4rk hat folgendes geschrieben Zum zitierten Posting springen:
Ich verweise immer sehr gerne hierauf: http://wiki.delphigl.com/index.php/Timebased_Movement
Damit sollte das Problem mit dem Timer erledigt sein. Falls es noch Probleme mit der Berechnung von Bewegungen/Kollisionen geben sollte, kann ich dieses Tutorial hier empfehlen: http://wiki.delphigl.com/index.php/Tutorial_Lineare_Algebra


Schonmal angesehen?
Da steht, wie du die Kugel langsamer werden lässt, ohne den Interval zu ändern.
Du brauchst insgesamt nur einen Timer, in dem lässt du halt alles laufen. Wenn das erstmal klappt, kannst du dir mal application.OnIdle anschauen, und das dann eleganter ohne Timer lösen. Aber strebe erstmal einen Timer an.


beastofchaos - So 26.12.10 18:23

Moderiert von user profile iconNarses: Komplett-Zitat des letzten Beitrags entfernt.

Hab ich schon gesehen.
Ich kann das immer noch später machen. Schließlich läuft das Programm immer noch wie geschmiert, auch mit mehreren Timern. Und wenn man statt dem Interval die Strecke verlangsamt wird das sehr ungenau. er schießt bei mir imerm mit Entfernung 2 Positionen in eine Richtung. Da man bei x- und y-Werten imerm Runden muss, kommen nur so ungenaue Ergebnise raus( entweder 2,1 oder 0 -> nur drei verschiedene Geschwindigkeiten ).


Boldar - Di 28.12.10 23:16

Speichern und Darstellung trennen? Was hindert dich daran, intern extended zu nehmen? Wenn du keine Hilfe willst, machs halt alleine.
Timebased movement ist nunmal die Methode für sowas.


beastofchaos - Mi 29.12.10 01:01

Moderiert von user profile iconNarses: Komplett-Zitat des letzten Beitrags entfernt.

Klar will ich Hilfe, sonst wär ich auch schon längst nicht so weit gekommen ( auch wenn ich in 4 Tagen 15-20 Stunden da schon dran saß ) ;)

Ersten sollte ich doch Speicherplatz sparen wo es geht und Extended rechnet soweit ich weiß viel genauer als Single (= real). am Ende bevor die Kugel neu gezeichnet wird, muss man so doer so wieder runden, weildas Programm nicht so genau zeichnet. Damit hab ich auch schon sehr starke Probleme :/

PS: In dem Forum* hab ich die Datei und, wenn du Lust/Zeit hast und dir das mal ansiehst, wirst du merken, dass es einfach total ungenau läuft, so wie ich es mache, aber ich weiß nciht, wo ich korrigieren kann. *: blinkendes Image [http://www.delphi-forum.de/viewtopic.php?p=627887#627887]


bummi - Mi 29.12.10 01:31

Speicherplatz ist eh kein Problem, berechne in eine Dynamisches Array of Record folgende Werte
Millisekunde
X Double,
Y Double
Ich würde auch noch auch noch den aktuellen Richtungsvektor, Restgeschwindigkeit und Reibungskoeffizenten mitführen alle Werte rein bis die Geschwindigkeit 0 ist.
Beim Animieren suchst Du den passenden MS Eintrag von GetTickcount - StartTickcount und gibst an X und Y jetzt gerundet aus.


beastofchaos - Mi 29.12.10 14:43

Wozu brauch ich die "Millisekunde" und was für ne Einheit sol das dann sein?
und zu x und y double... ihr reitet wohl solange draufrum. Es ist doch eigentlich egal wie genau ich die Position berechne. Delphi malt nur integerpositionen... heißt: Ob ich auf die hunderste Stelle der auf die millionste Stelle berechne is total egal, da am Ende ich sowieso das ganz aufrunden muss...

Z den anderen Werten. Klar werde ich das noch hinzufügen, aber im Moment ist doch noch das Problem, dass die Kugel erst einmal in die Richtung laufen soll, die ich ihr vorgebe und, dass sie alles richtig spiegelt. Schaut euch doch endlich mal das an, was ich für euch zeichne und reinstelle...


jaenicke - Mi 29.12.10 14:54

user profile iconbeastofchaos hat folgendes geschrieben Zum zitierten Posting springen:
Ob ich auf die hunderste Stelle der auf die millionste Stelle berechne is total egal, da am Ende ich sowieso das ganz aufrunden muss...
Gemeint ist das so: Wenn du bei 100 Schritten jeweils ein halbes Pixel Fehler hast durch die Rundung, sind das am Ende 50 Pixel...
Deshalb ist es sinnvoll Zwischenschritte genau zu speichern und nur das Ergebnis auf ganze Pixel zu runden. sonst summieren sich die Rundungsfehler ggf. auf.


platzwart - Mi 29.12.10 14:58

Es reitet niemand wegen der Genauigkeit darauf rum, sondern weil die Aussage der Delphi-Entwickler sowas ist wie: "-ACHTUNG- Den Datentyp REAL NICHT mehr verwenden. In der aktuellen Delphiversion ist er nur noch aus Abwärtskompatibilitätsgründen vorhanden." (Real wird intern durch Double ersetzt) Und dann ists halt nicht ganz so schlau, genau diesen Datentyp zu verwenden ;)


bummi - Mi 29.12.10 15:03

jaenicke hat das wichtigste erwähnt.
Hinzu kommt das der Timer ein extrem ungenaues und unzuverlässiges Teil ist, Du kannst Dich nicht darauf verlassen alle 15 ms bedient zu werden.
Wenn Du das Array korrekt berechnet hast kannst Du den für den gerade getroffenen Zeitpunkt die Korrekten Koordinaten darstellen.
Deine Reflektionsmethode wird nur bei absolut senkrechten oder waagrechten Flächen funktionieren, damit wirst Du auf Dauer nicht auskommen und die Zeit lieber direkt in eine korrekte Implementierung investieren.


beastofchaos - Do 30.12.10 14:20

user profile iconbummi hat folgendes geschrieben Zum zitierten Posting springen:
jaenicke hat das wichtigste erwähnt.
Deine Reflektionsmethode wird nur bei absolut senkrechten oder waagrechten Flächen funktionieren, damit wirst Du auf Dauer nicht auskommen und die Zeit lieber direkt in eine korrekte Implementierung investieren.


Das stimmt, bloß hab ich leider keeeein ahnung, wie ich das zum beispiel bei einer gerundeten ecke machen soll oder bei einer schrägen... ich bräuchte immer einen Punkt der im rechten Winkel zum Reflektionspunkt und der vorherigen Position steht ( damit ich winkel berechnen kann oder auch direkt spiegeln kann... ) -> heißt ch müsste für jeden Kontaktpunkt mit einem Hindernis einen solchen Punkt definieren... Mir fällt dazu einfach keine gute Lösung ein... :/

Und danke an euch wegen der Erklärung zu Singel/Real. Ich änder das sofort in Double :p Mir ist gestern auch schon aufgefallen, dass er bei der Geschwindigkeit 1 zumbeispiel nur in 8 verschiedene Richtungen schießen kann... bei geschwindigkeit 4 schafft er 16 vgerschiedene richtungen... ic hweiß nciht, ow der fehler liegt.. vll wirklich an der ungenauen berechnung mit single. ich versuchs mal und meld mich wieder

---Moderiert von user profile iconNarses: Beiträge zusammengefasst---

mmmh, hilft mir nicht weiter mit Double :(

ich hab ja das Programm im anderen Topic hochgeladen, vll wollt ihrs mal abspielen und die Richtungen testen... ich überleg mir nochmal, wo der fehler liegt.

MfG Thomas