článek

Vytvoř si svou bludišťovku - 6

Vytvoř si svou bludišťovku - 6

Po vskutku dlouhé pauze se tento seriál vrací mezi živé. Doba to byla dlouhá, a tak jsme se na oplátku rozhodli seriál poněkud zmodernizovat a vylepšit. Co nás tety bude čekat. V dnešním díle se podíváme na jednoduchý způsob řešení detekce kolizí, který vás může inspirovat k tvorbě způsobů komplexnějších.

Tímto také uzavřeme jednu kapitolu seriálu a začneme se věnovat dalšímu tématu, shaderům. Ukážeme si jejich kouzlo a flexibilitu, nahradíme jimi naše stávající transformace, osvětlíme si scénu pomoci per-pixel osvětlení a nakonec se letmo podíváme i na post-processing. Všechny shadery budeme psát v jazyce HLSL.



To bylo stručné představení, kam bude seriál směřovat, a nyní se vrhneme na slibované kolize, které budeme řešit pomocí vystřelování paprsků. Z pozice kamery vždy vystřelíme 4 paprsky a budeme zjišťovat kolik jich protíná zdi bludiště a v jaké vzdálenosti. Nyní malé vysvětlení proč právě čtyři paprsky. Pomineme-li fakt, že s kamerou můžeme rotovat, můžeme ji posouvat dopředu, dozadu, nahoru a dolů, tedy do čtyř stran. Proto pro každou stranu použijeme jeden kontrolní paprsek. Tento kříž navíc pootočíme o 45° a vyřešíme tak problémy s detekcí kolizí u hran zdí.



Další zamyšlení hodnou věcí je klouzaní podél zdí, které vyřešíme následovně. Naše funkce pro testování kolize bude vracet čtyři stavy:



  • Nedošlo ke kolizi.

  • Došlo ke kolizi pouze podél osy x.

  • Došlo ke kolizi pouze podél osy z.

  • Došlo ke kolizi podél více os.


Nejlepší způsob bude vytvořit si pro to enum.





enum CollisionTest
{
COLT_NONE,
COLT_XAXIS,
COLT_ZAXIS,
COLT_OTHER
};




A my můžeme konečně přistoupit k tvorbě funkce pro zjišťování kolizí, nazvěme ji třeba mapCollisionTest.


Ještě jsme ani nezačali psát a naskytla se první věc k zamyšlení. My mapu bludiště před vykreslením transformujeme (měníme její pozici, rotaci, velikost) což znamená, že její souřadnice jsou v jiném prostoru a kolizní test by vyhodnocoval naprosto nesmyslné výsledky. Pro nás bude ovšem nejjednodušší převést všechny 4 paprsky do stejného prostoru, jako je mapa.





static D3DXMATRIX matInverse;D3DXMatrixInverse(&matInverse, NULL, &(matRotationX * matRotationY *
matRotationZ * matScale * matTranslation));
static D3DXVECTOR3 rayObjOrigin;
D3DXVec3TransformCoord(&rayObjOrigin, &campos, &matInverse);




Vytvoříme si pomocnou inverzní matici a počáteční bod všech čtyř paprsků převedeme do prostoru model u mapy. Pokud máte nejasnosti, podívejte se jak transformujeme model mapy před vykreslením.



Dále si vytvoříme několik pomocných proměnných, jejichž význam vysvětlím později, a pole směrů, kterými míří paprsky.





static CollisionTest result = COLT_NONE;
CollisionTest lastResult = result;
BOOL hit = false;
float distance = 100.0f;
bool rayTestResults[4];
bool collision = false;

static D3DXVECTOR3 rayObjDirections[4];
rayObjDirections[0] = D3DXVECTOR3(1.0f, 0.0f, 1.0f);
rayObjDirections[1] = D3DXVECTOR3(-1.0f, 0.0f, -1.0f);
rayObjDirections[2] = D3DXVECTOR3(-1.0f, 0.0f, 1.0f);
rayObjDirections[3] = D3DXVECTOR3(1.0f, 0.0f, -1.0f);




Paprsky míří do kříže a jsou posunuté o 45° vůči osám x,z.


Dostáváme se ke klíčové funkci dnešního dílu, kterou je D3DXIntersect. Tato funkce slouží ke zjištění, jestli zadaný paprsek protíná objekt typu ID3DXMesh,reprezentující naše bludiště.




for(unsigned int i=0; i<4; i++)
{
D3DXVec3TransformNormal(&rayObjDirections[i], &rayObjDirections[i], &matInverse);
D3DXVec3Normalize(&rayObjDirections[i], &rayObjDirections[i]);
D3DXIntersect(meshMaze, &rayObjOrigin, &rayObjDirections[i], &hit,
NULL, NULL, NULL, &distance, NULL, NULL);
if(hit && distance < 1.1f)
{
rayTestResults[i] = true;
collision = true;
}
else rayTestResults[i] = false;}




Pro všechny čtyři paprsky platí, že nejdříve převedeme i jejich směr do prostoru mapy, normalizujeme vektory udávající jejich směr a vyzkoušíme, jestli se s mapou protnou. Pokud se tak stalo a vzdálenost protnutí od počátku paprsku je menší než udaná hodnota, poznamenáme si to do proměnné rayTestResults a pokud se v dané vzdálenosti protnul alespoň jeden paprsel, nastavíme proměnnou collision na true.



Nyní nám nezbývá, než naše poznatky nějak vyhodnotit. Začneme tím, že pokud je proměnná collision false, není co řešit a vrátíme, že ke kolizi nedošlo. Význam proměnných result a lastResult si vysvětlíme za chvíli.




if(!collision)
{
result = COLT_NONE;
return result;
}




Pro další postup si budeme potřebovat zjistit, kolik paprsků se v dané vzdálenosti proťalo.





unsigned short numCollisions = 0;
for(unsigned int i=0; i<4; i++)
{
if(rayTestResults[i]) numCollisions++;
}




Nastal čas zamyslet se nad jádrem vyhodnocování kolizí. Bylo by dobré vyřešit alespoň v základech klouzání podél stěn. Máme tedy čtyři paprsky a zamyslíme-li se nad tím, že pokud kolizi zahlásí pouze dva z nich, musíme zákonitě jít podél stěny. V našem případě jsou všechny stěny podél os, takže máme čtyři možnosti výsledku (plus pátý, ani jedna z možností).





if(rayTestResults[0] && rayTestResults[3])
{
result = COLT_XAXIS; return result;
}
else if(rayTestResults[2] && rayTestResults[1])
{ result = COLT_XAXIS;
return result;
}
else if(rayTestResults[0] && rayTestResults[2])
{
result = COLT_ZAXIS;
return result;
}
else if(rayTestResults[1] && rayTestResults[3])
{
result = COLT_ZAXIS; return result;
}




Pokud se ani jedna z podmínek nevyhodnotí jako pravá, jednoduše vrátíme COLT_OTHER. Pokud byste ovšem teď funkci otestovali, narazili byste na nepříjemnou skutečnost. Kamera by se při vnějších rozích zasekávala. Příčina je v tom, že pokud se posouváte podél stěny až k rohu, dostanete se do fáze, kdy v dané vzdálenosti začne mapu protínat již pouze jeden paprsek. Na to je naštěstí jednoduché řešení, a sice zaznamenávat si poslední výsledek a při takovéto situaci prostě tento výsledek vracet, dokud nenastane jiný stav.




if(numCollisions == 1) return lastResult;
result = COLT_OTHER;
return result;












A tím je naše funkce pro zjišťování kolizí hotová, stačí již pouze poupravit kód pro ovládání kamery.


Jednoduše kameru posuneme na následující pozici a zkontrolujeme kolize. Pokud k ní dojde, vrátíme kameru na předchozí pozici na ose X, Z nebo na obou.




CollisionTest ctest;
ctest = mapCollisionTest();
if(ctest == COLT_OTHER)
{
campos -= pluspos;
}
else if(ctest == COLT_XAXIS)
{
campos.x -= pluspos.x;
}
else if(ctest == COLT_ZAXIS)
{
campos.z -= pluspos.z;
}




A to je konec dnešního snažení, zdrojové kódy a grafiku k projektu si můžete stáhnout níže a příště se můžete těšit na úvod do shaderů.



Kompletní zdrojové kódy (všechny díly seriálu): bludistovka_zdroje.zip



Související odkazy:








pavlík_petr

autor
/ pavlík_petr

Publikováno: 31.10.2008


další články této kategorie





diskuze

odeslat

Nejsi automat? Napiš výsledek 3 + 4 =

Drticka

Drticka | 26.12.18 v 09:53

učím se

Drticka

Drticka | 12.05.18 v 12:44

tak to je pro mě španělská vesnice

CoookieBurgerCZ

CoookieBurgerCZ | 07.07.15 v 11:29

dekuju moc za rady

phanka

phanka | 21.04.15 v 21:21

nejde mi to

Verun94

Verun94 | 03.11.14 v 13:51

Jojo, circle je drsná hra :-D

janice11

janice11 | 02.07.14 v 10:37

Já žádnou blidišťovku nepotřebuji, , já ji mám ve hře Circle chaos. Lítám v ní jak nudle v baňce.

Seto | 18.01.09 v 21:29

este nieco mam win vista...

Seto | 18.01.09 v 21:28

pri kompilacii mi to hadze chyby...
Compiling...
main.cpp
C:\Users\Seto\Desktop\c\bludistovka 6\main.cpp(33) : error C2146: syntax error : missing ';' before identifier 'din'
C:\Users\Seto\Desktop\c\bludistovka 6\main.cpp(33) : error C2501: 'LPDIRECTINPUT8' : missing storage-class or type specifiers
C:\Users\Seto\Desktop\c\bludistovka 6\main.cpp(33) : fatal error C1004: unexpected end of file found

meu pau no seu cú | 27.12.08 v 03:04

vai se fude seus finho das pulta e se naum gosto vem fazer na mau e vou esta louko para fazer vcs chupa o meu pau seus viados esse naum eo meu email o meu eo rafael_prostituto@hotmail.com seus maconheiro

ADAM PUČA | 21.12.08 v 12:16

ja sem debil debility:-D

Peuťou | 20.12.08 v 18:35

Zajímavý

lll | 05.12.08 v 20:11

lol jak si mam udelat bludiste:??

peter | 29.11.08 v 08:15

jo tak jo

veronika | 28.11.08 v 17:09

jasi chcete abych vam nestrapnila stranku tak mi tu hru poslete

veronika | 28.11.08 v 17:08

prosim prosim

peter | 24.11.08 v 16:41

je to lechky musitesi uĎelat bludište a boj z nestvurami!

patrik | 20.11.08 v 18:03

jeto pekni

Craw | 07.11.08 v 22:39

"Co nás tety bude čekat."

Tak nevim jestli to melo byt tedy, a nebo je-li clanek urcen tetam.