Du må være registrert og logget inn for å kunne legge ut innlegg på freak.no
X
LOGG INN
... eller du kan registrere deg nå
Dette nettstedet er avhengig av annonseinntekter for å holde driften og videre utvikling igang. Vi liker ikke reklame heller, men alternativene er ikke mange. Vær snill å vurder å slå av annonseblokkering, eller å abonnere på en reklamefri utgave av nettstedet.
  11 3152
Hero of Time
bronze's Avatar
Eg held for tida på å utvide programmeringshorisonten min, gjennom å utvikle enkle spel i C++ med SDL-biblioteket for grafikk, lyd og innputt. Både C++ og SDL er relativt nytt for meg, men problemet eg no skal spørre om er ikkje spesifikt for nokon av dei.

Situasjonen er å simulere meget enkel fysikk: Eit objekt (til dømes ein sprettball) skal starte i lufta (i ro), falle til bakken grunna tyngdekraft, sprette opp att når det treff bakken, og objektet skal kome seg like høgt som der det starta.

Vi antek null luft (og dermed null luftmostand), uniform tyngdekraft (slik at tyngdeakselerasjonen er konstant) og bevaring av mekanisk energi.

Om eg ikkje har gløymt noko så skal desse forenklingane tilseie at ei minimal (enkel) simulering kan sjå slik ut, merk at koda er forkorta og ufullstendig:

Kode

// Forenkla kode - GetTime gir noverande tid i millisekund
const double TIME_STEP = 10; // 10 ms tidsintervall gir 100 fps;
const double GRAVITY_CONSTANT = 0.002;
double t_last = GetTime()
// Game loop
bool running = true;
while (running) {
    double dt = GetTime() - t_last;
    tick(dt);
    t_last = GetTime();
    // + Noko kode for å rendre fysiske objekt til skjerm
    
    SDL_Delay(TIME_STEP);
}

void tick(double dt) {
    // For alle fysiske objekt
    for (auto* ent : m_entities) {
        // Akselerasjon grunna tyngdekraft
        ent.speedY = ent.speedY + GRAVITY_CONSTANT * dt;

        // Endring av posisjon
        ent.y = ent.y + ent.speedY * dt;

        // Sjekkar om objektet har byrja å gå ut av skjermen
        if ( collision() ) {
            // Snur fartsretninga, vi bryr oss ikkje om at objektet er litt under golvet
            ent.speedY = ent.speedY * (-1);
        }
    }
}
Denne koda fungerer greit, men gir unøyaktig rørsle, dvs. objekta sprett ikkje alltid like høgt som der dei starta. Det ser ut til å avhenge av blant anna FPS og GRAVITY_CONSTANT. Men, eg har klart å fikse dette ved å innføre kinetisk energi og potensiell energi, og stadig korrigere farta ved å løyse E = Ek + Ep = 0.5mv^2 + mgh for v (til dømes kvart tiande tick).

Kva er det som gjer at spretthøgda varierer såpass mykje når eg bruker den enklaste tilnærminga? Er det fordi formlane berre er gyldige når dt går mot 0, er det fordi bruken av double gir unøyaktigheit eller er det fordi framgangsmåten er for enkel eller evt. feil?

Merk at dette gjerast for undervisninga sin del, så det er derfor eg ønsker diskusjon om ulike feilkjelder ved slik kode.

Her er ein video av simuleringa med 100fps:
http://videobam.com/CNnsW
Merk at andre verdiar for fps og GRAVITY_CONSTANT kan gi betre resultat, dvs. høgare spretting

Her er fullstendig kjeldekode om nokon vil sjå alt:
http://s000.tinyupload.com/index.php...67376797391158

Merk at den ikkje kan kompilerast utan SDL og ei Windows-maskin (har nytta windows.h til tidsfunksjonalitet).
Passer du på at hvert tick pauser i 10ms minus kjøretid fra forrige tick? Basert på det du skriver her, virker det som at du pauser i 10ms uansett kjøretid fra forrige tick.

Jeg er ikke dreven på fysikk, men skal ikke et objekt som spretter i >0 gravity hele tiden sprette noe lavere enn forrige sprett eller utgangspunkt?
Hero of Time
bronze's Avatar
Trådstarter
Sitat av Ueland Vis innlegg
Passer du på at hvert tick pauser i 10ms minus kjøretid fra forrige tick? Basert på det du skriver her, virker det som at du pauser i 10ms uansett kjøretid fra forrige tick.

Jeg er ikke dreven på fysikk, men skal ikke et objekt som spretter i >0 gravity hele tiden sprette noe lavere enn forrige sprett eller utgangspunkt?
Vis hele sitatet...
Den forenkla koda her tek ikkje hensyn til køyretid av tick, men det skal heller ikkje spele inn i simuleringa, utanom at sjølve rendringa kan bli litt mindre glatt.

Gitt 0 luftmotstand, 0 friksjon og bevaring av energi skal objektet sprette like høgt kvar gong. All potensiell energi (stillingsenergi) går over til kinetisk energi medan objektet fell, og all kinetisk energi går over til potensiell energi på veg opp.

Edit: Ekstra info: I den faktiske kjeldekoda står der tick(TIME_STEP), slik at kvart tick har like stort tidsintervall.
Sist endret av bronze; 15. april 2014 kl. 19:56.
Jeg synes dette var ganske interessant, så jeg laget en kopi i Javascript, så det blir litt lettere for andre å leke seg med:

http://jsfiddle.net/v6sVb/3/

Buggen er altså reprodusert uten at jeg prøvde spesifikt på det.

Og litt nyttig lesestoff:

http://gafferongames.com/game-physic...ration-basics/
http://gafferongames.com/game-physic...your-timestep/
Sist endret av Ozma; 15. april 2014 kl. 20:25. Grunn: Automatisk sammenslåing med etterfølgende innlegg.
Hero of Time
bronze's Avatar
Trådstarter
Sitat av Ozma Vis innlegg
Jeg synes dette var ganske interessant, så jeg laget en kopi i Javascript, så det blir litt lettere for andre å leke seg med:

http://jsfiddle.net/v6sVb/3/

Buggen er altså reprodusert uten at jeg prøvde spesifikt på det.

Og litt nyttig lesestoff:

http://gafferongames.com/game-physic...ration-basics/
http://gafferongames.com/game-physic...your-timestep/
Vis hele sitatet...
Stilig jsFiddle!

Skal ta å lese den integrasjonslinken. Den andre har eg allereie bokmerka og lest, og akkurat no er det kun sjølve simuleringingslogikken eg fokuserer på.

Kan forresten legge til at høgare FPS gir mindre fartstap. Prøvde 2000 fps no (sjekka med fraps at den klarte det), og det gir nesten null fartstap. Dermed antek eg foreløpig at eg ikkje kan bruke så store dt og framleis forvente at alt følgjer dei fysiske lovene.
Sist endret av bronze; 15. april 2014 kl. 20:41.
Her er en fikset versjon: http://jsfiddle.net/v6sVb/9//.

Trikset er altså at update-funksjonen starter ved tiden t, og slutter ved t+dt. Da er det litt urimelig å bruke farten ved tiden t gjennom hele bevegelsen.

For å øke nøyaktigheten kan du altså bruke gjennomsnittet av farten ved t og ved t+dt. Dette er kjent som "the modified Euler method", og den er 100% nøyaktig ved konstant akselerasjon. (Dette er identisk med det jeg har gjort i koden)
Sist endret av Ozma; 15. april 2014 kl. 21:01.
Hero of Time
bronze's Avatar
Trådstarter
Sitat av Ozma Vis innlegg
Her er en fikset versjon: http://jsfiddle.net/v6sVb/9//.

Trikset er altså at update-funksjonen starter ved tiden t, og slutter ved t+dt. Da er det litt urimelig å bruke farten ved tiden t gjennom hele bevegelsen.

For å øke nøyaktigheten kan du altså bruke gjennomsnittet av farten ved t og ved t+dt. Dette er kjent som "the modified Euler method", og den er 100% nøyaktig ved konstant akselerasjon. (Dette er identisk med det jeg har gjort i koden)
Vis hele sitatet...
Takker. Les meg også opp på Runge-Kutta, som vi faktisk har hatt om i eit matematikk-fag på NTNU men som eg hadde gløymt.

Forresten, du kan prøve å modifisere den gamle koden (original Euler) ved å flytte fartsendringa under posisjonsendringa. Då får du motsatt unøyaktigheit - objektet sprett høgare og høgare!
Et tips som gir deg et godt utgangspunkt er å bruke hastighet og retning. Gravitasjon blir da et push i gravitasjonens retning, stort sett ned , men da blir det også lett å sette den til f.esk. opp, hvert tikk.

Kaizen
Hero of Time
bronze's Avatar
Trådstarter
Tillegg: Ellers er problemet løyst, og konklusjonen er at Eulers metode er meget unøyaktig. Dette burde eg selvfølgelig ha forstått og hugsa frå når vi hadde om det i matematikk.

Modifisert Eulers metode taklar konstant akselerasjon og Range-Kutta (RK4) ser ut til å takle varierande akselerasjon (med tilstrekkjeleg men ikkje eksakt presisjon).

Om nokon vil halde fram å diskutere emnet så vil eg halde fram å lese tråden.
Sist endret av bronze; 15. april 2014 kl. 21:19.
▼ ... over en uke senere ... ▼
Når vi først er inne på spillprogrammering, ta en titt på denne; http://www.koonsolo.com/news/dewitters-gameloop/
om du syns fjerdeordens-Runge-Kutta er spennende, kan du jo ta en titt på kildekoden til et gammelt C++-spill jeg og min kollega begynte på men aldri kom veldig langt med: https://github.com/plaimi/limbs-off -- ganske mye seriøs fysikk inni der.

videre vil jeg vel anbefale å bruke et mer høynivå språk enn C++. den feilen gjør jeg ikke en gang til... skal ikke skrevet mye spillkode i f.eks Haskell for å utvikle en grei mengde hat for C++. ;-)