článek

Vytvoř si svou bludišťovku - 6
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:
- Vytvoř si svou bludišťovku - 6
- Vytvoř si svou bludišťovku - 5
- Vytvoř si svou bludišťovku - 4
- Vytvoř si svou bludišťovku - 3
- Vytvoř si svou bludišťovku - 2
- Vytvoř si svou bludišťovku - 1
další články této kategorie
diskuze

NS1NO | 31.05.22 v 13:07
text příspěvku

Drticka | 26.12.18 v 09:53
učím se

Drticka | 12.05.18 v 12:44
tak to je pro mě španělská vesnice

CoookieBurgerCZ | 07.07.15 v 11:29
dekuju moc za rady

phanka | 21.04.15 v 21:21
nejde mi to

Verun94 | 03.11.14 v 13:51
Jojo, circle je drsná hra :-D

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.