Kirjautuminen

Haku

Tehtävät

Keskustelu: Ohjelmointikysymykset: C: Lentokoneen ohjattavuus

Sivun loppuun

Touho [17.06.2007 18:02:52]

#

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

Tumpelo [17.06.2007 18:16:37]

#

En ole ihan varma mitä ajat takaa, mutta tarkistapas tämä ketju läpi: https://www.ohjelmointiputka.net/keskustelu/15200-directx-9-first-person-näkymä, itselläni oli sama / saman tapainen ongelma ja se ratkesi tuolla.

Touho [17.06.2007 22:15:58]

#

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);

Metabolix [19.06.2007 00:49:07]

#

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.

Touho [19.06.2007 02:02:46]

#

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);

Markus [19.06.2007 16:02:24]

#

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))

Touho [19.06.2007 16:24:07]

#

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.

Markus [19.06.2007 16:55:09]

#

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/Main_Page

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.

Touho [19.06.2007 23:01:08]

#

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.

Metabolix [19.06.2007 23:06:04]

#

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...

Touho [20.06.2007 17:22:43]

#

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.

os [20.06.2007 17:31:33]

#

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.

Touho [20.06.2007 19:42:11]

#

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.

Markus [21.06.2007 11:31:36]

#

Touho kirjoitti:

matriisimatematiikka onkin täysin uusi OpenGL:n mukana tullut asia minulle.

Lue artikkelini "Matriisimatematiikkaa peliohjelmoijille":
http://www.suomipelit.com/files/artikkelit/56/MatriisimatematiikkaaPeliohjelmoijille.zip
Sillä pääsee mukavasti alkuun.

Touho [21.06.2007 14:56:28]

#

Kiitos kaikille. Nyt sain lentokoneeni ohjattavuuden kuntoon. Artikkelisi on varmasti minulle tarpeen.

os [21.06.2007 17:27:24]

#

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.


Sivun alkuun

Vastaus

Aihe on jo aika vanha, joten et voi enää vastata siihen.

Tietoa sivustosta