Files |  Tutorials |  Articles |  Links |  Home |  Team |  Forum |  Wiki |  Impressum

Aktuelle Zeit: Do Mai 16, 2024 22:49

Foren-Übersicht » Programmierung » Allgemein
Unbeantwortete Themen | Aktive Themen



Ein neues Thema erstellen Auf das Thema antworten  [ 12 Beiträge ] 
Autor Nachricht
 Betreff des Beitrags: Probleme mit Platformer Physik
BeitragVerfasst: Sa Mär 31, 2012 08:53 
Offline
DGL Member

Registriert: Mi Okt 16, 2002 15:06
Beiträge: 1012
Serv,

ich versuch grad nen Tilebased Platformer zu schreiben, habe aber probleme mit der Physik.
So wie ich es jetzt grad mache, kann man jederzeit wenn man von einer platform runterfällt wieder zurück, was aber nicht möglich sein sollte.

Kann mir einer sagen was ich falsch mache?

Code:
  1.  
  2.         private bool CollisionVertical(float xpos, float ypos, out int tilecoordx)
  3.         {
  4.             tilecoordx = (int)Math.Floor(xpos / tileSize);
  5.             int tiletop = (int)Math.Floor(ypos / tileSize);
  6.             int tilebottom = (int)Math.Floor((ypos + h - 1) / tileSize);
  7.             for (int i = tiletop; i <= tilebottom; i++)
  8.             {
  9.                 if (map.IsSolid(tilecoordx, i))
  10.                     return true;
  11.             }
  12.             return false;
  13.         }
  14.  
  15.         private bool CollisionHorizontal(float xpos, float ypos, out int tilecoordy)
  16.         {
  17.             tilecoordy = (int)Math.Floor(ypos / tileSize);
  18.             int tileleft = (int)Math.Floor(xpos / tileSize);
  19.             int tileright = (int)Math.Floor((xpos + w - 1) / tileSize);
  20.             for (int i = tileleft; i <= tileright; i++)
  21.             {
  22.                 if (map.IsSolid(i, tilecoordy))
  23.                     return true;
  24.             }
  25.             return false;
  26.         }
  27.  
  28.         public void Update(float frametime, ControlState ctrlstate)
  29.         {
  30.             const float Gravity = 1.0f;
  31.             const float VelJump = 100.0f;
  32.  
  33.             velx = 0;
  34.             if (ctrlstate.HasFlag(ControlState.Left))
  35.             {
  36.                 velx = -MoveSpeedX;
  37.             }
  38.             else if (ctrlstate.HasFlag(ControlState.Right))
  39.             {
  40.                 velx = MoveSpeedX;
  41.             }
  42.             if (ctrlstate.HasFlag(ControlState.Jump) && !lockJumping)
  43.             {
  44.                 lockJumping = true;
  45.                 vely = -VelJump;
  46.             }
  47.  
  48.             int tilecoord;
  49.  
  50.             float mapSizeX = tileSize * map.TileCountX;
  51.             float mapSizeY = tileSize * map.TileCountY;
  52.  
  53.             if (velx < 0.0f)
  54.             {
  55.                 // Move left
  56.                 float lefttest = x + (velx * frametime);
  57.                 if (lefttest <= 0.0f)
  58.                 {
  59.                     x = 0;
  60.                     velx = 0;
  61.                 }
  62.                 else
  63.                 {
  64.                     if (CollisionVertical(lefttest, y, out tilecoord))
  65.                     {
  66.                         x = (tilecoord + 1) * tileSize;
  67.                         velx = 0;
  68.                     }
  69.                     else
  70.                     {
  71.                         x += velx * frametime;
  72.                     }
  73.                 }
  74.             }
  75.             else if (velx > 0.0f)
  76.             {
  77.                 float righttest = x + (velx * frametime) + w;
  78.                 if (righttest >= mapSizeX)
  79.                 {
  80.                     x = mapSizeX - w;
  81.                     velx = 0;
  82.                 }
  83.                 else
  84.                 {
  85.                     // Move right
  86.                     if (CollisionVertical(righttest, y, out tilecoord))
  87.                     {
  88.                         x = (tilecoord * tileSize) - w;
  89.                         velx = 0;
  90.                     }
  91.                     else
  92.                     {
  93.                         x += velx * frametime;
  94.                     }
  95.                 }
  96.             }
  97.  
  98.             if (vely < 0.0f)
  99.             {
  100.                 float toptest = y + (vely * frametime);
  101.                 if (toptest <= 0.0f)
  102.                 {
  103.                     // Hit on ceiling
  104.                     y = 0.0f;
  105.                     vely = 0;
  106.                 }
  107.                 else
  108.                 {
  109.                     // Jumping
  110.                     if (CollisionHorizontal(x, toptest, out tilecoord))
  111.                     {
  112.                         y = (tilecoord + 1) * tileSize;
  113.                         vely = 0;
  114.                     }
  115.                     else
  116.                     {
  117.                         y += vely * frametime;
  118.                         vely += Gravity;
  119.                     }
  120.                 }
  121.             }
  122.             else
  123.             {
  124.                 // Falling or in air
  125.                 float maxBottom = map.TileCountY * tileSize;
  126.                 float bottomtest = y + (vely * frametime) + h;
  127.  
  128.                 if (bottomtest >= maxBottom)
  129.                 {
  130.                     // Bodenloses loch
  131.                     y = maxBottom - h;
  132.                     vely = 0;
  133.                     if (!ctrlstate.HasFlag(ControlState.Jump))
  134.                         lockJumping = false;
  135.                 }
  136.                 else
  137.                 {
  138.                     if (CollisionHorizontal(x, bottomtest, out tilecoord))
  139.                     {
  140.                         Console.WriteLine("Collision floor - " + bottomtest);
  141.                         y = (tilecoord * tileSize) - h;
  142.                         vely = 1;
  143.                         if (!ctrlstate.HasFlag(ControlState.Jump))
  144.                             lockJumping = false;
  145.                     }
  146.                     else
  147.                     {
  148.                         vely += Gravity;
  149.                         y += vely * frametime;
  150.                         lockJumping = true;
  151.                     }
  152.  
  153.                 }
  154.             }
  155.         }
  156.  


Nach oben
 Profil  
Mit Zitat antworten  
 Betreff des Beitrags: Re: Probleme mit Platformer Physik
BeitragVerfasst: Sa Mär 31, 2012 09:26 
Offline
DGL Member

Registriert: Mi Okt 16, 2002 15:06
Beiträge: 1012
Omg es war so simpel:

Falsch:
for (int i = tileleft; i <= tileright; i++)

Richtig:
for (int i = tileleft; i <= tileright; ++i)


Nach oben
 Profil  
Mit Zitat antworten  
 Betreff des Beitrags: Re: Probleme mit Platformer Physik
BeitragVerfasst: Sa Mär 31, 2012 14:23 
Offline
DGL Member

Registriert: Mi Okt 16, 2002 15:06
Beiträge: 1012
So hab nun das ganze nochmal mit OpenGL und Glut und C++ geproggt und es funktioniert zwar,
aber die "Frametime" spinnt völlig und ich weiß nicht wieso.

Normalerweise liegen meine frametimes weit über 0 ms, hier in meinem Platformer Spiel aber bleiben sie fast immer bei 0
mit heftigen einbrüchen. Lass ich die Frametime weg beim bewegen ist alles sehr smooth, das ist aber falsch.

Bitte schaut euch mal den Rendercode an. ich seh keinen Fehler :( Die FPS werden richtig berechnet!

Code:
  1.  
  2. float latestFrametime = 0.0f;
  3.  
  4. #ifdef _WIN32
  5. LONGLONG qpcFrequency;
  6. QueryPerformanceFrequency((LARGE_INTEGER*)&qpcFrequency);
  7.  
  8. double getCurTimeMilliseconds(){
  9.     LONGLONG cur = 0;
  10.     QueryPerformanceCounter((LARGE_INTEGER*)&cur);
  11.     return (double)(cur * 1000.0f / qpcFrequency);
  12. }
  13. #endif
  14.  
  15. void render() {
  16.     float framestart = (float)getCurTimeMilliseconds();
  17.  
  18.     // Calculate frames per second
  19.     framecounter++;
  20.     if (framestart - latestFrameCounterTime > 1000.0f) {
  21.         float elapsedTime = (float)(framestart - latestFrameCounterTime);
  22.         framesPerSecond = (((float)framecounter * 1000.0f)/ elapsedTime);
  23.         latestFrameCounterTime = framestart;
  24.         framecounter = 0;
  25.     }
  26.  
  27.     // Render & Update frame
  28.     renderFrame(latestFrametime);
  29.     glutSwapBuffers();
  30.     updateFrame(latestFrametime);
  31.  
  32.     // Calculate new latest frame time
  33.     float frameend = (float)getCurTimeMilliseconds();
  34.     latestFrametime = (float)(frameend - framestart);
  35. }
  36.  


bewegt wird relativ einfach:

x += velx * frametime;

Danke für die Hilfe,
Final

PS: Bei bedarf lad ich den VC++ 2010 source mal hoch.


Nach oben
 Profil  
Mit Zitat antworten  
 Betreff des Beitrags: Re: Probleme mit Platformer Physik
BeitragVerfasst: Sa Mär 31, 2012 15:35 
Offline
DGL Member
Benutzeravatar

Registriert: Di Mai 18, 2004 16:45
Beiträge: 2621
Wohnort: Berlin
Programmiersprache: Go, C/C++
Das erste Problem, was ich sehe ist die umwandlung von UInt64 in Float64, dann in Float32 und rechnest dann fleißig mit Float32. Dabei geht soviel genauigkeit flöten, dass spielt sich schon im ms Bereich ab. Von Daher bleib bei UInt64 und wandel nur für delta time in Float64.

Das zweite Problem ist, dass der Code sehr Anfällig gegen die Verfügbare Zeit auf der CPU ist und schon kleinste Änderungen der Prozessorzeit oder abweichen von regelmässigen Ausführen, zu Rucklern, kaputte Physik und Animationen führt.
Es ist besser mit Konstanter delta time zu arbeiten und die Updateloop entsprechend x mal auf zu rufen, bis die Zeit vom letzten Timestamp bis zu aktuellen nachgeholt wurde. So hast du auch nicht das ungenauigkeitsproblem von 1., da ja delta time ein Konstante ist, die nicht berechnet wird und der TimeStamp, vom Performancecounter mit UInt64 nur noch eine subtraktion braucht, statt mehrere floatingpoint multiplikationen und divisionen.

Das letzte und wohl dein eigentliches Problem ist, dass du diesen Thread auf ein Prozessorkern fest nageln musst, da
Code:
  1. QueryPerformanceCounter(reinterpret_cast<LARGE_INTEGER*>(&result))
und
Code:
  1. clock_gettime(CLOCK_PROCESS_CPUTIME_ID,&tmp)
CPU Core abhängig sind und dein thread per default auf den hin und her springt und damit total falsche Werte raus kommen. Damit das korrekt ist, musst du mit
Code:
  1. SetThreadAffinityMask(GetCurrentThread(),Mask);
ihn auf ein bestimmten Kern fest nageln. Mask kannst du auf 1 stellen, dann wird immer der erste Logische Prozessor verwendet, den gibt es immer ^^.

_________________
"Wer die Freiheit aufgibt um Sicherheit zu gewinnen, der wird am Ende beides verlieren"
Benjamin Franklin

Projekte: https://github.com/tak2004


Nach oben
 Profil  
Mit Zitat antworten  
 Betreff des Beitrags: Re: Probleme mit Platformer Physik
BeitragVerfasst: Sa Mär 31, 2012 19:35 
Offline
DGL Member

Registriert: Mi Okt 16, 2002 15:06
Beiträge: 1012
Also ich habe nun einiges umgestellt, aber es hat sich nichts geändert.
Gleiches problem wie vorher noch, kann es sein das GLUT für genaues Timing nix taugt?

Hier die sachen die ich geändert habe, zu erst den Perf Counter:

Code:
  1.  
  2. typedef unsigned long long UInt64;
  3. LARGE_INTEGER qpcFrequency;
  4.  
  5. void init() {
  6.   QueryPerformanceFrequency((LARGE_INTEGER*)&qpcFrequency);
  7.   DWORD mask = 1;
  8.   SetThreadAffinityMask(GetCurrentThread(), mask);
  9. }
  10.  
  11. UInt64 getCurTimeMilliseconds(){
  12.     LARGE_INTEGER cur;
  13.     QueryPerformanceCounter(reinterpret_cast<LARGE_INTEGER*>(&cur));
  14.     return cur.QuadPart * 1000 / qpcFrequency.QuadPart;
  15. }
  16.  
  17. void CGame::render() {
  18.  
  19.     UInt64 framestart = platform->getCurTimeMilliseconds();
  20.  
  21.     // Calculate frames per second
  22.     framecounter++;
  23.     if (framestart - latestFrameCounterTime > 1000) {
  24.         UInt64 elapsedTime = framestart - latestFrameCounterTime;
  25.         framesPerSecond = (((float)framecounter * 1000.0f) / elapsedTime);
  26.         latestFrameCounterTime = framestart;
  27.         framecounter = 0;
  28.     }
  29.  
  30.     // Render & Update frame
  31.     float frametime = (float)latestFrametime;
  32.     updateFrame(frametime);
  33.     renderFrame(frametime);
  34.     glutSwapBuffers();
  35.  
  36.     // Calculate new latest frame time
  37.     UInt64 frameend = platform->getCurTimeMilliseconds();
  38.     latestFrametime = frameend - framestart;
  39. }
  40.  
  41.  


Was echt komisch ist, das ich schon viele QPC Anwendungen gebaut habe, aber noch keine mit GLUT und alle hatten kein Problem mit "float" als DeltaTime/Frametime, aber GLUT schon.

Meine Fluid Simulation nutzt übrigens auch das gleiche verfahren und hat auch entsprechende Timing problem,
die aber nicht so auffallen wie hier in meinem Platformer.

Was macht eigentlich das reinterpret_cast ?


Nach oben
 Profil  
Mit Zitat antworten  
 Betreff des Beitrags: Re: Probleme mit Platformer Physik
BeitragVerfasst: Sa Mär 31, 2012 20:08 
Offline
DGL Member

Registriert: Mi Okt 16, 2002 15:06
Beiträge: 1012
Ich hab den source hochgeladen mit kompilierter debug version.
http://xenorate.com/tl_files/somekram/Platformer_Cpp_Glut.zip

Nicht wundern, das ding ist alles andere als fertig.


Nach oben
 Profil  
Mit Zitat antworten  
 Betreff des Beitrags: Re: Probleme mit Platformer Physik
BeitragVerfasst: Sa Mär 31, 2012 23:00 
Offline
DGL Member
Benutzeravatar

Registriert: Di Mai 18, 2004 16:45
Beiträge: 2621
Wohnort: Berlin
Programmiersprache: Go, C/C++
Du hast frametime als ms statt sekunden übergeben, daher ist es 1000 mal größer als es sein sollte.
Dann kommt noch ein unverständliches verhalten/bug hinzu, da zu zwischen renderstart und swap buffer misst und glutSwapBuffers nicht wie es eigentlich sollte noch die restliche Zeit, bis zum VSync, wartet, sind die Messwerte sehr klein, ungenau und falsch.
Du solltest lieber folgenden Code benutzten.
Code:
  1.     float frametime = (float)(framestart - latestFrametime)/1000.0f;
  2.     updateFrame(frametime);
  3.     renderFrame(frametime);
  4.     glutSwapBuffers();
  5.     // Calculate new latest frame time
  6.     UInt64 frameend = platform->getCurTimeMilliseconds();
  7.     latestFrametime = framestart;

Das hat es wesentlich stabiler gemacht aber ist natürlich immer noch den Problemen von nicht konstanten delta times unterlegen.
In Spielen sind folgendes GameLoops wesentlich besser.
Code:
  1. UInt64 delta,now,lastTime;
  2. static const UInt64 syncTime=16000LL;
  3. while(!thread.IsAborted())
  4. {
  5.   now=System::GetHighResolutionCounter();
  6.   delta=now-lastTime;
  7.   lastTime=now;
  8.  
  9.   while(delta>syncTime)
  10.   {
  11.     DoSomeStuff(syncTime);
  12.     delta-=syncTime;
  13.   }
  14.   thread.Sleep(0);
  15. }

Sleep mit 0 sorgt dafür, dass dieser Prozess pausiert wird, das System nun andere Prozesse, in der Queue, verarbeiten kann und wenn das passiert ist wieder zurück springt.
Passend zum Sleep muss ich noch sagen, wenn du SetThreadAffinityMask benutzt, dann mach danach noch ein Sleep, erst dann kannst du sicher sein, dass die Funktion greift. Denn Sleep ist eine der wenigen Funktionen,in der OS API, die dem Sheduler erlauben den Prozess an zu halten.

Stabiler gehts nicht mehr und wenn man physik macht, dann kann man mit der Lösung auch extrapolation weg werfen und kann bullet time, vowärts, rückwärtsspulen easy realisieren(syncTime*2 wäre doppelt so scnell, -syncTime wäre rückwärts spulen).
Der Grund dafür ist, diese Loop ist Deterministisch.

Hier mal eine komplexe aber auch exakte Variante, mit verschiedenen Geschwindigkeiten.
Code:
  1. UInt64 delta,now,lastTime,lastFrame,lastAI,lastPhy;
  2. static const UInt64 phyTime=8000LL;
  3. static const UInt64 aiTime=4000LL;
  4. static const UInt64 vsync=16000LL
  5. while(!thread.IsAborted())
  6. {
  7.   now=System::GetHighResolutionCounter();
  8.   delta=now-(lastTime-delta);// wenn delta nicht ganz auf ging, dann muss es in den nächsten durchlauf übernommen werden
  9.   lastTime=now;
  10.  
  11.   while(delta>aiTime)//aiTime hat die kleinste Zeit also richtet sich die loop nach dieser
  12.   {
  13.     if((now-lastPhy)>phyTime)
  14.     {
  15.       Physic(phyTime);
  16.       lastPhy+=phyTime;
  17.     }
  18.     AI(aiTime)
  19.  
  20.     if((now-lastFrame)>vsync)
  21.     {
  22.       Render();
  23.       lastFrame-=vsync;
  24.     }
  25.     delta-=aiTime;
  26.   }
  27.   thread.Sleep(0);
  28. }

_________________
"Wer die Freiheit aufgibt um Sicherheit zu gewinnen, der wird am Ende beides verlieren"
Benjamin Franklin

Projekte: https://github.com/tak2004


Nach oben
 Profil  
Mit Zitat antworten  
 Betreff des Beitrags: Re: Probleme mit Platformer Physik
BeitragVerfasst: So Apr 01, 2012 16:42 
Offline
DGL Member

Registriert: Mi Okt 16, 2002 15:06
Beiträge: 1012
Habe das ganze nun im XNA Framework 4.0 nachgebaut und ich komm weit besser vorran als ich erwartet habe,
vor allem muss ich mich nicht mit low level kram rumschlagen. Geht alles leicht von der Hand.
Eventuell werd ich es nochmal mit OpenTK probieren, aber erstmal bin ich mit meinem ergebnis ganz zu zufrieden.


Nach oben
 Profil  
Mit Zitat antworten  
 Betreff des Beitrags: Re: Probleme mit Platformer Physik
BeitragVerfasst: Do Apr 05, 2012 07:35 
Offline
DGL Member

Registriert: Mi Okt 16, 2002 15:06
Beiträge: 1012
Ich hab nochmal ein paar Tests gemacht mit GLUT und habe es nicht hinbekommen, eine brauchbare Gameloop zu bauen die
sauber ein Delta/Frametime erzeugt. Immer sind heftigste Schwankungen drin.
Vermute hier stark das die mainloop von GLUT irgendwie dafür unbrauchbar ist.

Whatever, wie ich geschrieben habe, habe ich das ganze eh in XNA gebaut und dieses Framework ist wirklich nicht schlecht.

Das gibts übrigens auch für mono in verbindung mit OpenGL: http://code.google.com/p/monoxna/
Allerdings nachdem ich mir den Source davon angeschaut habe musste ich leider feststellen, dass das Projekt alles andere als abgeschlossen ist. Es sind noch sehr viele NotImplementedExceptions in den Implementierungen :(

Allerdings könnte man das Grundgerüst von dem Framework recht schnell nachbauen mit OpenTK.

Was recht interessant an dem Framework ist, das laden von Content Assets.
Das läuft folgendermassen ab: Es gibt eine art Content-Projekt indem man alle Assets ablegt. Beim Kompilieren der Anwendung werden alle Assets über einen Content-Loader geladen und dann über Content-Writer als Binärdatei in den Output Ordner geschrieben und dann in der Anwendung mit dem Content-Reader gelesen.
Das bedeutet das man immer gezwungen ist, als End-Format ein Binärformat hat, indem die Assets abgelegt sind.


Nach oben
 Profil  
Mit Zitat antworten  
 Betreff des Beitrags: Re: Probleme mit Platformer Physik
BeitragVerfasst: Do Apr 05, 2012 13:29 
Offline
DGL Member
Benutzeravatar

Registriert: Di Dez 03, 2002 22:12
Beiträge: 2105
Wohnort: Vancouver, Canada
Programmiersprache: C++, Python
Finalspace hat geschrieben:
Omg es war so simpel:

Falsch:
for (int i = tileleft; i <= tileright; i++)

Richtig:
for (int i = tileleft; i <= tileright; ++i)


Nur so aus Neugierde.. damit hast du einen Fehler behoben?
Der einzige unterschied den ich sehe ist das i++ und ++i, das dürfte aber maximal eine Auswirkung auf die Performance der Schleife haben, nicht auf den Code der in der Schleife abgearbeitet wird - oder übersehe ich etwas?

Aya


Nach oben
 Profil  
Mit Zitat antworten  
 Betreff des Beitrags: Re: Probleme mit Platformer Physik
BeitragVerfasst: Do Apr 05, 2012 14:35 
Offline
DGL Member

Registriert: Mi Okt 16, 2002 15:06
Beiträge: 1012
Aya hat geschrieben:
Finalspace hat geschrieben:
Omg es war so simpel:

Falsch:
for (int i = tileleft; i <= tileright; i++)

Richtig:
for (int i = tileleft; i <= tileright; ++i)


Nur so aus Neugierde.. damit hast du einen Fehler behoben?
Der einzige unterschied den ich sehe ist das i++ und ++i, das dürfte aber maximal eine Auswirkung auf die Performance der Schleife haben, nicht auf den Code der in der Schleife abgearbeitet wird - oder übersehe ich etwas?

Aya


Moment, jetzt wo dus erwähnst klingt das schon merkwürdig.
Womöglich ist da noch ne andere Stelle die ich geändert habe, damit es lief.
Aber ich schaus mir nochmal an und änders auf i++ wieder ab und geb bescheid ^^


Nach oben
 Profil  
Mit Zitat antworten  
 Betreff des Beitrags: Re: Probleme mit Platformer Physik
BeitragVerfasst: Fr Apr 06, 2012 10:39 
Offline
DGL Member

Registriert: Mi Okt 16, 2002 15:06
Beiträge: 1012
Ja hast recht gehabt, irgendwas muss ich noch geändert haben damit es lief.
Habs auf i++ umgestellt keine änderung.


Nach oben
 Profil  
Mit Zitat antworten  
Beiträge der letzten Zeit anzeigen:  Sortiere nach  
Ein neues Thema erstellen Auf das Thema antworten  [ 12 Beiträge ] 
Foren-Übersicht » Programmierung » Allgemein


Wer ist online?

Mitglieder in diesem Forum: 0 Mitglieder und 3 Gäste


Du darfst keine neuen Themen in diesem Forum erstellen.
Du darfst keine Antworten zu Themen in diesem Forum erstellen.
Du darfst deine Beiträge in diesem Forum nicht ändern.
Du darfst deine Beiträge in diesem Forum nicht löschen.
Du darfst keine Dateianhänge in diesem Forum erstellen.

Suche nach:
Gehe zu:  
cron
  Powered by phpBB® Forum Software © phpBB Group
Deutsche Übersetzung durch phpBB.de
[ Time : 0.026s | 17 Queries | GZIP : On ]