Hlavní navigace

Hra Tanky v Open Inventoru

21. 5. 2007
Doba čtení: 5 minut

Sdílet

V předchozím článku jsme si v Open Inventoru vytvořili malé bludiště a jeden tank, se kterým se můžeme nyní v bludišti vesele prohánět. Tanky projíždějící zdmi nejsou ale to pravé pro opravdovou hru. Dnes si tedy přidáme detekci kolizí a rovněž trochu zábavných efektů v podobě střel a explozí.

Obsah dílu:

1. základy detekce kolizí
2. kolize ve hře
3. střílení
4. exploze

1. Základy detekce kolizí

Nejčastější úlohou, pro kterou detekci kolizí používáme, je zajistit, aby tělesa ve virtuální scéně skrze sebe volně neprocházela. V každém případě mají algoritmy detekce kolizí tendenci být výkonově velmi náročně a téměř nikdy se neobejdeme bez optimalizací.

Nejzákladnější optimalizací pro kolizní algoritmy je použití modelů s velmi redukovaným počtem trojúhelníků. Modely, které použijeme, jsou zobrazemy na následujících obrázcích:

Tanky tank

Proč je počet trojúhelníků tak kritický? Protože při hledání kolize se hledají průniky každého trojúhelníku s každým trojúhelníkem druhého tělesa. Zredukujeme-li počet trojúhelníků desetkrát pro kolizní tělesa, získáme stokrát nižší časovou náročnost.

V praxi existují i určité optimalizace. Například Inventor obaluje všechny tělesa „bounding boxem“ (čti baunding box), česky: ohraničující kvádr, či obalové těleso. Inventor nejprve testuje tato obalová tělesa a teprve, když zjistí jejich průnik, porovnává tělesa trojúhelník po trojúhelníku.

Použití uvedených dvou optimalizací, tedy obalových těles a kolizních těles s redukovaným počtem trojúhelníků, redukovalo časovou náročnost kolizních algoritmů do té míry, že další optimalizace už nebyly nutné.

Byl však objeven jeden chyták: Při testování jedné aplikace nám detekce kolizí neustále hlásila, že ke kolizi došlo, přestože na obrazovce se vše zdálo v pořádku a všechna tělesa byla od sebe dostatečně daleko, aby nebylo nejmenších pochyb, že bylo něco přehlédnuto. Po chvilce debugování bylo zjištěno, že všechny kolidující dvojce trojúhelníků nepatří dvěma různým tělesům, jak by se očekávalo, ale byly to trojúhelníky téhož tělesa. Po chvilce zmatení se objevilo vysvětlení: těleso bylo složeno z více So(Indexed)Tri­angleStripSetu uspořádaných tak, že mezi některými trojúhelníky docházelo ke kolizi. Přesněji řečeno: trojúhelníky patřící do jednoho So(Indexed)Tri­angleStripSetu kolizi nezpůsobí, pouze trojúhelníky patřící různým So(Indexed)Tri­angleStripSetům, které se protínají. Samozřejmě se daná vlastnost netýká pouze So(Indexed)Tri­angleStripSet, ale všech tříd odvozených od SoShape.

Výše řečený chyták se objevil i u modelu bludiště. Zde je problém ošetřen „filtrovací funkcí“, kterou si ukážeme později a která zabrání detekovat kolize mezi So(Indexed)Tri­angleStripSety patřící jednomu tělesu. Alternativní řešení by bylo překonvertovat celou geometrii tělesa do jediného So(Indexed)Tri­angleStripSetu.

Pro detekci klizí slouží třída SoIntersection­DetectionActi­on, která je odvozena od SoAction. Základní metodou pro její použití je, podobně jako u všech akcí, metoda apply:

void SoIntersectionDetectionAction::apply(SoNode *root);

Tato metoda projde celý graf scény, jehož kořen je dán parametrem root. Přitom to nutně nemusí být graf totožný s rendrovacím grafem, ale může to být graf speciálně zoptimalizovaný pro učely kolizních algoritmů.

Metoda apply hledá v grafu všechny trojúhelníky a testuje jejich průnik. V Inventoru byla zavedena konvence, že pouze třídy odvozené od SoShape mohou obsahovat geometrii těles. Každá takováto třída přitom umí překonvertovat svůj obsah na trojúhelníkovou podobu a to i třídy, které normálně trojúhelníkovou reprezentaci neuchovávají (NURBS plochy a podobně). Získané trojúhelníky jsou pak použity pro detekci kolizí. A jak již bylo naznačeno dříve, trojúhelníky jednoho SoShape objektu nejsou testovány na kolize, pouze trojúhelníky různých SoShape objektů.

Je-li kolize detekována, je zavolán callback. V něm může uživatel získat informaci o kolidujících trojúhelnících, nebo například nastavit boolean indikující existenci kolize ve scéně.

2. Kolize ve hře

V Tancích kvůli kolizím používáme dva grafy scény:

  SoSeparator *rootkolize;
  SoSeparator *bludisteKolize;

Graf scény pak konstruujeme dvakrát – jednou pro renderování a jednou pro kolize. Často se liší pouze v názvu souboru, ze kterého byla načtena geometrie tělesa:

  this->model->name.setValue("models/tank.wrl");
  this->modelKolize->name.setValue("models/tankkolize.wrl");

Některé nody jsou mezi grafy sdíleny, jako například translace tanku:

  // posunuti tanku
  this->trans = new SoTranslation;
  this->root->addChild(this->trans);
  this->rootKolize->addChild(this->trans);

Další detaily již najdeme ve zdrojácích.

Pro kolize používáme tři různé akce:

  SoIntersectionDetectionAction *ida; //testovani kolizi tanku s bludistem
  SoIntersectionDetectionAction *ida2; //testovani kolizi strely s bludistem a tanky
  SoIntersectionDetectionAction *ida3; //testovani kolizi mezi tanky

Teoreticky by stačila pouze jedna třída, ale celá věc byla implementována jinak. Detaily můžeme najít ve funkci nazvané prepocitejScenu. Ta pracuje v těchto krocích:

  1. uschovej aktuální pozici tanku
  2. aktualizuj pozici a rotaci tanku podle toho, které klávesy uživatel stiskl
  3. testuj kolize mezi pohyblivými objekty (střely a tanky); došlo-li ke kolizi, obnov minulou pozici tanku
  4. testuj kolizi tanku a bludiště; došlo-li ke kolizi a jedná se o náraz do zdi pod šikmým úhlem, zkus klouzat při zdi; jinak obnov předchozí pozici tanku
  5. aktualizuj pozici střely
  6. došlo-li ke kolizi, vytvoř objekt výbuchu a smaž objekt střely

3. Střely

Je-li stisknuta klávesa pro střílení, je při aktualizaci scény vytvořen nový objekt střely. Inicializace střely je provedena v metodě Strela::start() a její pseudo-kód je následující:

  modelStrely->setPosition(tankPosition);
  modelStrely->setDirection(tankRotation, hlavenSmer);
  korenSceny->addChild(modelStrely);

Dále už je pozice střely pouze aktualizována podle rovnice balistické střely v metodě Strela::refresh().

4. Exploze

Žádná hra se neobejde bez efektů jako jsou například výbuchy. Je mnoho metod, jak realizovat výbuch. Dvě metody jsou asi nejčastější – částicové systémy a animovaný billboard. Druhý z nich je méně výkonově náročný a také jej použijeme. Technika billboardingu se v počítačové grafice používá často. Je to objekt, nejčastěji čtverec, který se neustále otáčí ke kameře. Na následujícím obrázku můžeme vidět příklad billboardu.

Billboard

Naše exploze bude vytvořena billboardem, na který bude nanesena animovaná textura. Ve skutečnosti se jedná o jedinou texturu obsahující 16 obrázků animované exploze. Tuto texturu si můžete sami vytvořit na stránkách www.geocities­.com/starline­sinc/index.html

Explode

Pro efekt animované exploze potřebujeme texturu korektně namapovat na náš billboard. Celé střídání jednotlivých obrázků je realizováno změnou texturovacích souřadnic. Textury měníme v metodě Vybuch::refresh(). Nastavují-li se korektně, textury se postupně střídají a vytvářejí efekt exploze.

Závěr

Zájemci o další věci jako zvuk, síťová hra, umělá inteligence, bump-mapping, ovládání myší a další věci, jejichž předzvěst ukazuje následující screenshot:

TankySF

se mohou podívat na oficiální stránky projektu. K tomu je přidána i kopa dalších užitečných linků:

root_podpora

  • Tanky – tanky.sourcefor­ge.net – síťová hra napsaná v Open Inventoru, na základě které vznikl tento tutoriál
  • Open Inventor Tools – sada velmi užitečných utilit pro práci s 3D modely
  • různé projekty vytvořené na FIT v roce 2005 a 2006

Tímto tento tutoriál končí a bude následovat tutoriál o OSG nebo-li OpenSceneGrafu, což je jiná knihovna pro 3D grafiku.

Ke stažení

Zdrojáky: 5–2-Tanky-2.zip
Tutoriál: tutorial13.zip

Byl pro vás článek přínosný?