Olen tekemässä peliä, jossa lennetään lentokoneella. Minulla tuli ongelma ohjattavuuden kanssa.
!HUOM! En ole tehnyt pelissä olevia grafiikoita. Lentokoneen mallinnusta EI saa käyttää omissa projekteissa!
Windows binääri: http://zux.sjr.fi/touho/Rage.zip
Kun lentokone on kääntynyt sekä x että z-akselin suuntaisesti, aluksen ohjaaminen (f tai r) käyttäyttäytyy väärin. Tiedän, mistä on kyse, mutta en osaa toteuttaa ajatuksiani. Osaatteko auttaa?
Ohjaaminen: a, s, d, w, r, f. p printtaa "log.txt" tiedostoon rotaation.
void KeyHandle(float dt) { if(glfwGetKey('w'-32)) { ship.speed += ship.acceleration*dt; if(ship.speed > ship.max_speed) ship.speed = ship.max_speed; } else ship.speed *= 1.0f - dt*0.5f; if(glfwGetKey('s'-32)) ship.speed -= ship.acceleration*dt; if(ship.speed < 0.0f) ship.speed = 0.0f; if(!ship.landed) { if(glfwGetKey('a'-32)) { ship.z_rot += ship.turn_speed*dt*1.5f; } if(glfwGetKey('d'-32)) { ship.z_rot -= ship.turn_speed*dt*1.5f; } if(glfwGetKey('r'-32) && ship.y > 0.0f) { //Nämä ovat väärin ship.x_rot -= cos(ship.z_rot*pi/180.0f)*ship.turn_speed*dt; ship.y_rot += sin(-ship.z_rot*pi/180.0f)*ship.turn_speed*dt; } if(glfwGetKey('f'-32)) { //Nämä ovat väärin ship.x_rot += cos(ship.z_rot*pi/180.0f)*ship.turn_speed*dt; ship.y_rot -= sin(-ship.z_rot*pi/180.0f)*ship.turn_speed*dt; } } //Näin voit katsoa, mihin suuntaan kone on kääntynyt. if(glfwGetKey('p'-32)) { ofstream write("log.txt"); write << "x_rot: " << ship.x_rot << "\n" << "y_rot: " << ship.y_rot << "\n" << "z_rot: " << ship.z_rot << "\n"; write.close(); } return; }
Aluksen piirtäminen:
glTranslatef(ship.x, ship.y, ship.z); glRotatef(ship.y_rot, 0.0f, 1.0f, 0.0f); glRotatef(ship.x_rot, 1.0f, 0.0f, 0.0f); glRotatef(ship.z_rot, 0.0f, 0.0f, 1.0f); DrawObject(&object[0]); //piirrä lentokone
En ole ihan varma mitä ajat takaa, mutta tarkistapas tämä ketju läpi: https://www.ohjelmointiputka.net/keskustelu/
Ei ollut tuosta apua, kiitos kuitenkin.
Huomasin, että pystyn yksinkertaistamaan kysymystä paljon.
Millä x:n ja z:n arvoilla saan tämän:
glRotatef(y, 0.0f, 1.0f, 0.0f);
glRotatef(x, 1.0f, 0.0f, 0.0f);
glRotatef(z, 0.0f, 0.0f, 1.0f);
tekemään saman, kuin tämä:
glRotatef(y, 0.0f, 1.0f, 0.0f);
glRotatef(x1, 1.0f, 0.0f, 0.0f);
glRotatef(z1, 0.0f, 0.0f, 1.0f);
glRotatef(x2, 1.0f, 0.0f, 0.0f);
Taitaa olla aivan selvää, että tehtävä on mahdoton. Kun vektoria pyöritetään 90 astetta ensin x:n, sitten z:n ja lopuksi -90 astetta x:n ympäri, saadaan sama vektori, joka saataisiin myös pyörittämällä y-akselin ympäri 90 astetta. Yleisesti siis xzx-kolmikkoja ei voi esittää xz-pareina.
Joo, en ajatellut tarpeeksi pitkälle.
Millä x:n, z:n ja y:n arvoilla saan tämän:
glRotatef(y, 0.0f, 1.0f, 0.0f);
glRotatef(x, 1.0f, 0.0f, 0.0f);
glRotatef(z, 0.0f, 0.0f, 1.0f);
tekemään saman, kuin tämä:
glRotatef(y1, 0.0f, 1.0f, 0.0f);
glRotatef(x1, 1.0f, 0.0f, 0.0f);
glRotatef(z1, 0.0f, 0.0f, 1.0f);
glRotatef(x2, 1.0f, 0.0f, 0.0f);
Touho kirjoitti:
Millä x:n, z:n ja y:n arvoilla saan tämän:
glRotatef(y, 0.0f, 1.0f, 0.0f);
glRotatef(x, 1.0f, 0.0f, 0.0f);
glRotatef(z, 0.0f, 0.0f, 1.0f);tekemään saman, kuin tämä:
glRotatef(y1, 0.0f, 1.0f, 0.0f);
glRotatef(x1, 1.0f, 0.0f, 0.0f);
glRotatef(z1, 0.0f, 0.0f, 1.0f);
glRotatef(x2, 1.0f, 0.0f, 0.0f);
Ratkaise x, y ja z yhtälöryhmästä:
cos(y)*cos(z) = cos(y1)*cos(z1)
cos(x)*cos(z) = cos(x2)*cos(x1)*cos(z1)
cos(x)*cos(y) = cos(x2)*cos(x1)*cos(y1)
Eli varmaankin: y = y1, z = z1 ja x = arccos(cos(x2)*cos(x1))
Epäilen tämän paikkaansapitävyyttä, koska jos nämä: glRotatef(z1, 0.0f, 0.0f, 1.0f) ja glRotatef(x2, 1.0f, 0.0f, 0.0f)
vaihtaisivat paikkaa, objekti kääntyy ihan eri tavalla, mutta sinun antamat kaavat eivät muuttuisi miksikään.
Touho kirjoitti:
Epäilen tämän paikkaansapitävyyttä, koska jos nämä: glRotatef(z1, 0.0f, 0.0f, 1.0f) ja glRotatef(x2, 1.0f, 0.0f, 0.0f)
vaihtaisivat paikkaa, objekti kääntyy ihan eri tavalla, mutta sinun antamat kaavat eivät muuttuisi miksikään.
Joo huomasin juuri tekemäni virheen. Oikeasta yhtälöryhmästä tulee niin monimutkainen, että en usko sillä olevan ratkaisua.
Edit.
Voithan kokeilla itse. Imuroi wxMaxima niminen algebraohjelma:
http://wxmaxima.sourceforge.net/wiki/index.php/
Kirjoita:
R(a, x, y, z):=matrix([x*x*(1-cos(a))+cos(a), x*y*(1-cos(a))-z*sin(a), x*z*(1-cos(a)) +y*sin(a)],[y*x*(1-cos(a))+z*sin(a), y*y*(1-cos(a))+cos(a), y*z*(1-cos(a))-x*sin(a)],[x*z*(1 -cos(a))-y*sin(a), y*z*(1-cos(a))+x*sin(a), z*z*(1-cos(a))+cos(a)])
Jolloin funktiokutsu R(a, x, y, z) tuottaa saman matriisin kuin OpenGL-kutsu glRotatef(a,x,y,z)
Kirjoita:
R(y, 0,1,0).R(x,1,0,0).R(z,0,0,1)
Jolloin saat ensimmäisen kolmen kutsun tuottaman matriisin.
Kirjoita:
R(y1, 0,1,0) . R(x1,1,0,0) . R(z1,0,0,1) . R(x2,1,0,0)
Jolloin saat seuraavan neljän kutsun tuottaman matriisin.
Aseta matriisit yhtä suuriksi alkioittain ja yritä ratkaista x, y ja z.
Miten opengl:ssä katsotaan, missä kohdassa tällä hetkellä ollaan? Näin voisin helpommin saada selville kulmat.
float dx, dy, dt; glTranslate(0.0f, 0.0f, -1.0f); glRotatef(y1, 0.0f, 1.0f, 0.0f); glRotatef(x1, 1.0f, 0.0f, 0.0f); glRotatef(z1, 0.0f, 0.0f, 1.0f); glRotatef(x2, 1.0f, 0.0f, 0.0f); GetCurrentCoordinates(&dx, &dy, &dt); //Ei ole valmisfunktio. glLoadIdentity();
Ja sitten laskea sinin ja kosinin avulla kulmat.
Kannattaa kyllä opetella vähän itsekin sitä 3D-maailman laskentaa, helpottuu ihmeesti pelinteko. ;)
Tein pienen PHP-skriptin helpottamaan laskemista, ja jos se näin parissa minuutissa tehtynä toimi oikein, niin tässä ovat vektorin (ai + bj + ck) pyöritetyt versiot:
y, x, z: ((c * sin(y) + a * cos(y)) * cos(z) - (b * cos(x) - (c * cos(y) - a * sin(y)) * sin(x)) * sin(z)) * i + ((c * sin(y) + a * cos(y)) * sin(z) + (b * cos(x) - (c * cos(y) - a * sin(y)) * sin(x)) * cos(z)) * j + (b * sin(x) + (c * cos(y) - a * sin(y)) * cos(x)) * k y1, x1, z1, x2: ((c * sin(y1) + a * cos(y1)) * cos(z1) - (b * cos(x1) - (c * cos(y1) - a * sin(y1)) * sin(x1)) * sin(z1)) * i + (((c * sin(y1) + a * cos(y1)) * sin(z1) + (b * cos(x1) - (c * cos(y1) - a * sin(y1)) * sin(x1)) * cos(z1)) * cos(x2) - (b * sin(x1) + (c * cos(y1) - a * sin(y1)) * cos(x1)) * sin(x2)) * j + (((c * sin(y1) + a * cos(y1)) * sin(z1) + (b * cos(x1) - (c * cos(y1) - a * sin(y1)) * sin(x1)) * cos(z1)) * sin(x2) + (b * sin(x1) + (c * cos(y1) - a * sin(y1)) * cos(x1)) * cos(x2)) * k
Kun nämä laskee auki ja merkitsee sopivasti yhtäsuuriksi, saa seuraavat yhdeksän yhtälöä (luultavasti aivan samat kuin noin matriisien kauttakin):
sin(x) = (cos(x1) * cos(z1) * sin(x2) + sin(x1) * cos(x2)) (cos(x) * sin(z)) = (cos(x1) * sin(z1)) (cos(x) * cos(z)) = (cos(x1) * cos(z1) * cos(x2) - sin(x1) * sin(x2)) -(sin(y) * cos(x)) = (cos(y1) * sin(z1) * sin(x2) + sin(y1) * sin(x1) * cos(z1) * sin(x2) - sin(y1) * cos(x1) * cos(x2)) (cos(y) * cos(x)) = (sin(y1) * sin(z1) * sin(x2) - cos(y1) * sin(x1) * cos(z1) * sin(x2) + cos(y1) * cos(x1) * cos(x2)) (cos(y) * cos(z) - sin(y) * sin(x) * sin(z)) = (cos(y1) * cos(z1) - sin(y1) * sin(x1) * sin(z1)) (sin(y) * cos(z) + cos(y) * sin(x) * sin(z)) = (sin(y1) * cos(z1) + cos(y1) * sin(x1) * sin(z1)) (cos(y) * sin(z) + sin(y) * cos(z) * sin(x)) = (cos(y1) * sin(z1) * cos(x2) + sin(y1) * sin(x1) * cos(z1) * cos(x2) + sin(y1) * cos(x1) * sin(x2)) (sin(y) * sin(z) - cos(y) * cos(z) * sin(x)) = (sin(y1) * sin(z1) * cos(x2) - cos(y1) * sin(x1) * cos(z1) * cos(x2) - cos(y1) * cos(x1) * sin(x2))
Kyllä tuolla varmasti ratkaisukin on.
Onko jokin aivan erityinen syy sille, että haluat juuri kolmena pyörityksenä tuon esittää? Helpommin tuon voisi saada vaikka vain yhdeksi käännöksi...
Yhtenä kääntönä sitä ei voi esittää, koska lentokoneen pitää pystyä pyörimään z-akselin ympäri vapaasti. Ajattelin, että on helpointa tehdä tuo akseleiden suuntaisesti.
Ei kannata ajatella lentokoneen ohjausta pyörityksinä x-, y-, ja z-akselin ympäri, kun lentokonetta halutaan ennemminkin kääntää suhteessa koneen kulloiseenkin asentoon eli lentosuunnan, koneen pystyakselin ja siipien suhteen. Tämä onnistuu varsin helposti kvaternio- tai matriisimatematiikan avulla, mihin OpenGL:ssä on tietääkseni valmiit funktiot.
Jos lentokoneen asentoa kuvataan matriisilla A0 ja haluttua käännöstä matriisilla R, niin lentokoneen uusi asento on:
A1 = RA0
Tässä A on 3x3-matriisi, jonka sarakkeet kuvaavat lentokoneen käännetyn koordinaatiston akseleita:
| lx px sx | A = | ly py sy | | lz px sz |
Vektori l (eli lx, ly, lz) kuvaa koneen lentosuuntaa, p koneen pystyakselia ja s siipien suuntaa xyz-koordinaatistossa. Akseleiden järjestyksen voi vapaasti valita, kunhan itse muistaa, mitä milläkin tarkoitti ja piirtää koneen sen mukaan. R-matriisin akselit vastaavasti kuvaavat käännöstä; minne l, p ja s olisivat kääntyneet, jos kone olisi alunperin ollut "suorassa" - siten, että l, p ja s ovat xyz-koordinaatiston akselit. R:n voi helposti rakentaa tuttuun tapaan xyz-pyörähdysten avulla.
Kannattaa myös muistaa, että numeerinen epätarkkuus pikkuhiljaa vääristää matriiseja siten, etteivät ne enään esitäkään pelkästään käännöksiä. Ratkaisuna tähän asentomatriisin voi helposti korjata siten, että sen sarakkeet todella esittävät toisiaan vasten kohtisuoria yksikkövektoreita. Ongelma ratkeaa näppärästi myös ns. kvaterniomatematiikalla. Aiheesta pitäisi googlella löytyä helposti lisää tietoa.
Eli näppäinmistöntarkastelufunktio tekee kääntömatriisin R ja kertoo vanhan aluksen suunnan A0:n R:llä, joten tulee A1, mikä on uusi aluksen suunta.
OpenGL käyttää 4x4 matriiseja, joten 3x3 matriisit eivät toimi. Miten järkestelen 4x4 matriisissa kääntyvyydet? Vai onko kannattavampaa vain tehdä käännöt akseleiden suhteen opengl:n funktioilla ja sitten ottaa matriisi talteen muuttujaan? ( glGetFloatv(GL_MODELVIEW_MATRIX,A) )
Painovoima vaikuttaa alaspäin ja se myös kääntää alusta. Miten saan selville pystyvektorin koordinaatit suuntamatriisin A suhteen?
Onnistuuko lentokoneen piirto käytännössä näin:
glLoadIdentity();
glPushMatrix();
glTranslatef(ship.x, ship.y, ship.z);
glMultMatrixf(A);
DrawObject(&ship);
glPopMatrix();
Itse saan varmaan laskettua koordinaatit nopeuden ja suuntamatriisin avulla, vaikka matriisimatematiikka onkin täysin uusi OpenGL:n mukana tullut asia minulle.
Touho kirjoitti:
matriisimatematiikka onkin täysin uusi OpenGL:n mukana tullut asia minulle.
Lue artikkelini "Matriisimatematiikkaa peliohjelmoijille":
http://www.suomipelit.com/files/artikkelit/56/
Sillä pääsee mukavasti alkuun.
Kiitos kaikille. Nyt sain lentokoneeni ohjattavuuden kuntoon. Artikkelisi on varmasti minulle tarpeen.
Touho kirjoitti:
Painovoima vaikuttaa alaspäin ja se myös kääntää alusta. Miten saan selville pystyvektorin koordinaatit suuntamatriisin A suhteen?
Matriisia ei voi yksiselitteisesti verrata vektoriin. Pystyvektori on aina pystyvektori = z-akseli = [0,0,1]T. Haluat luultavasti verrata pystyvektoria koneen koordinaatistossa esitettyyn painopiste-tukipiste-erotusvektoriin, c, tai yksinkertaisemmin vain lentosuuntaan (asentomatriisin l-rivi). Painovoiman aiheuttaman käännöksen saa laskettua käännöksenä globaaliin koordinaatistoon käännetyn c:n ja normaalin z-akselin muodostaman tason suhteen eli vektorin r ympäri:
r = (A*c x [0,0,1]T)0
missä x merkitsee vektorien ristituloa ja yläindeksi 0 vektorin normalisointia. Huomaa, että vektoria ei aina voi normalisoida, mikä käytännössä tarkoittaa, että lentokone on tällöin matkalla suoraan ylös tai alaspäin, jolloin painovoima ei käännä sitä mihinkään suuntaan.
Aihe on jo aika vanha, joten et voi enää vastata siihen.