IT rekvalifikace s garancí práce. Seniorní programátoři vydělávají až 160 000 Kč/měsíc a rekvalifikace je prvním krokem. Zjisti, jak na to!
Hledáme nové posily do ITnetwork týmu. Podívej se na volné pozice a přidej se do nejagilnější firmy na trhu - Více informací.

7. diel - 3D bludisko v XNA - Škatule a guľa

Vitajte po sedemnástej. V tomto diele si pripravíme podhubie pre kolízne manažér. Vytvoríme si triedu, ktorá nám pomôže s vykresľovaním kolíznych guľou a krabíc. Tiež sa pokúsime kolízne krabice vydolovať z nahraných modelov. Pusťme sa teda do práce.

Krabice a gule

Veľmi bude potrebné niečo, čo nám vykreslí inak neviditeľné pomocné kolízne tvary. Spolu si tu rozoberieme iba časť pre krabice, teda pre BoundingBox. Časť pre guľu nechám na vás, ale nie je veľmi potrebné ju skúmať.

Začnime teda tradične vytvorením triedy. Ja som ju nazval BoundingRenderer. Nebude to komponenta, takže si ju necháme voľne v projekte s enginom. Opäť upravíme menný priestor a urobíme triedu verejnú. Krabica budeme vykresľovať drôte. Pridáme teda privátne premenné pre efekt a GraphicsDevice skrze ktoré budeme vykresľovať:

private static BasicEffect effect;
private static GraphicsDevice graphics;

Pridáme tiež statickú metódu Initialize, kde efekt vytvoríme a priradíme. Nastavíme mu nemenné parametre asi takto:

public static void Initialize(GraphicsDevice device){
  effect = new BasicEffect(device);
  effect.LightingEnabled = false;
  effect.VertexColorEnabled = true;

  graphics = device;
}

Zakážeme používanie svetiel a naopak povolíme farby u vertexov. Túto metódu zavoláme v konstruktoru triedy s enginom:

BoundingRenderer.Initialize(graphics);

Pridáme opäť statickú metódu Render, ale pomenujte si ju napríklad Draw alebo akokoľvek ako sa vám bude zdať príhodné. Práve cez ňu budeme neskôr vykresľovať. V parametroch odovzdáme našu krabicu, matice View a Projection a farbu, ktorú bude krabice vykreslená. Takže asi takto:

public static void Render(BoundingBox box, Matrix view, Matrix projection, Color color)

Z krabice možno získať body, ktoré ju ohraničujú skrze metódu GetCorners. Uložíme si ich do poľa a skrze for cyklus je naložíme do pripraveného poľa s vertexy, nezabudneme priradiť nami odovzdanou farbu:

Vector3[] corners = box.GetCorners();
for (int i = 0; i < 8; i++){
  krabiceVerts[i].Position = corners[i];
  krabiceVerts[i].Color = color;
}

Nesmieme si zabudnúť pole krabiceVerts vytvoriť:

private static VertexPositionColor[] krabiceVerts = new VertexPositionColor[8];

K tomu pridáme aj pole s indexmi, podľa ktorých sa budú body spájať čiarami, pole je ako ostatne všetko statické:

private static readonly int[] krabiceIndices = new int[]{
  0, 1,
  1, 2,
  2, 3,
  3, 0,
  0, 4,
  1, 5,
  2, 6,
  3, 7,
  4, 5,
  5, 6,
  6, 7,
  7, 4,
};

Len pre kontrolu je vidieť, že máme 12 hrán a práve toľko ich kocka má mať. Je dobre, že sme si prezieravo nacvičili kreslenie čiar na jednoduchých útvaroch, keby som to mal vysvetľovať teraz a na tomto, tak teda neviem, neviem. Efektu nastavíme obe naše matice View a Projection.

effect.View = view;
effect.Projection = projection;

A čiary vykreslíme:

effect.CurrentTechnique.Passes[0].Apply();
      graphics.DrawUserIndexedPrimitives(PrimitiveType.LineList,krabiceVerts,0,8,krabiceIndices,0,krabiceIndices.Length / 2);

To je všetko. Skúsime si do herného okna prepašovať vykreslenie jednej pokusné krabice. Prepíšeme metódu Draw. Ponecháme volanie base metódy a naopak pridáme vykreslenie krabice asi takto:

BoundingBox box = new BoundingBox(Vector3.Zero, new Vector3(-20, 20, -20));
BoundingRenderer.Render(box, Kamera.View, Kamera.Projection, Color.Purple);

Ak teraz hru spustíte, mali by ste vidieť bludisko a vedľa neho ružovú drôtovú krabici. Ak nie, tak máte niekde chybu a nebojte sa ozvať v komentároch.

Ako som už naznačil, guľu tu podrobne rozoberať nebudem, ale funguje to úplne rovnako. Ak vás zaujíma, ako je urobíme, tak sa pozrite do zdrojového kódu, ktorý ako zvyčajne nájdete pod článkom.

Vytvárame krabice

Ako som už minule naznačil, budeme naše objekty obaľovať krabicami a hráča naopak guľou. Ako ale tá telesa získame? U našich modelov je to ľahké. Samy sú kockami. Ale pri zložitejších modelov bude potrebné sa k nim prepracovať skrze vrcholy. Pokúsim sa tu navrhnúť dva možné spôsoby, ako získať kolízne krabici, prípadne guľu. Môžeme ju pochopiteľne ručne určiť, ale to akosi nie je ono. Tí bystrejší z vás si možno všimli, že v triede ModelMesh je premenná BoundingSpher e. Tam nám už XNA předpočítalo kolízne guľu pre danú časť modelu. To je panečku servis. Stačí ich len posčítat a máme hotovo. Ak by sme to chceli robiť kódom, tak by to vyzeralo nejako takto:

public BoundingSphere VytvorBoundigSphere(Model mod){
  BoundingSphere sphere = new BoundingSphere(Vector3.Zero, 0);
  foreach (ModelMesh mesh in mod.Meshes){
    BoundingSphere transformed = mesh.BoundingSphere.Transform(transformace[mesh.ParentBone.Index]);
    sphere = BoundingSphere.CreateMerged(sphere, transformed);
  }
  return sphere;
}

Prejdeme všetky časti modelu. Vyzdvihneme si tam vypočítanú guľu. Transformujeme ju rovnako ako pri vykresľovaní. Guľa potom postupne skladáme do jednej veľkej. A tú vrátime. Táto guľa je potom spoločná pre všetkých, ale pred jej použitím ju ešte musíme znova transformovať. Konkrétne posunúť na miesto, kde model vo svete stojí a prípadne jej zmeniť mierku. Rotáciu uplatňovať nemusíme, je snáď jasno, že rotáciou guľa dostávame stále tú istú guľu. Kódom vykonané to vyzerá nasledovne:

Matrix transform = Matrix.CreateScale(Meritko) * Matrix.CreateTranslation(Pozice);
BoundingSphere transformed = VytvorBoundigSphere(Model).Transform(transform);

S týmto už potom môžeme nakladať ďalej, je ale potrebné guľu prepočítať zakaždým, keď sa zmení poloha a alebo mierka modelu. Tohto úkonu sa nezbavíme tak ako tak. Vždy bude potrebné posúvať kolízne tvar súčasne s modelom. Zmena polohy u guľa znamená len posunúť jej stred, je to teda výpočtovo veľmi ľahká úloha. Ak meníme mierka, tak len daným číslom vynásobíme polomer a ten sa buď zväčší a alebo zmenší podľa zadanej hodnoty. Pokiaľ sa ale pokúsime zobraziť si vypočítanú guľu potrebné pre onen kváder, ktorý poslúžil pre demonštráciu minule, budeme nemilo prekvapení. Ostatne posúďte sami:

Kolízne 3D gule pre kváder v C# XNA .NET - 3D bludisko v XNA

Celý model je pekne vnútri to síce áno, ale za akú cenu. Kolízia by tu bola detekovaná prevažne na miestach, kde model už dávno nie je. Rozhodne by som tento spôsob nerád hádzal do koša. Možno ho uplatniť, ale nie vždy. Pre výrazne hranatá telesá sa proste nehodí. Keď prepočítame guľu na krabici, tak si tiež veľa nepomôžeme. Vlastne si nepomôžeme vôbec. Schválne si to skúste.

Ako teda krabicu vytvoriť a pritom ju neurčovať ručne? Vezmeme všetky body, z ktorých sa model skladá a škatuľu z nich zložíme. Nájdeme najmenšie súradnicu X a Y a tiež Z. nájdeme maxima súradníc az nich krabici poskladáme. Znie to veľmi ľahko a tiež to tak ľahké je. Spôsob nie je z mojej hlavy. Požičal som si ho odtiaľ. Tu nebolo bohužiaľ nič moc k upravovanie. Len iba extrakcia dát mi nefungovala a preto som ju trochu upravil. Namiesto riadku:

part.VertexBuffer.GetData(part.VertexOffset * stride, vertexData, 0, part.NumVertices, 1);

som dal tento:

part.VertexBuffer.GetData<byte>(vertexData);

Inak všetko zostalo bez zmeny. Oba prístupy som zakomponoval do pomocnej triedy Utility, ktorú sme si vytvorili už skôr. Možno vás pri obhliadke kódu zaujme, že ako parameter odovzdávam aj pole s transformáciami. Je to taká príprava pre animácie, neskôr sa nám to bude veľmi hodiť. Ešte dlhujem metódu ako s krabicou manipulovať. Tá je o trochu viac zložitejšie, než u gule. Posun krabice na správne miesto a jej zmenšenie je prakticky rovnaké ako u gule. Opäť si vytvoríme transformačný maticu a aplikujeme jej na minimálne a maximálne bod krabice:

Matrix transform = Matrix.CreateScale(Meritko) * Matrix.CreateTranslation(Pozice);
BoundingBox transformed = VytvorBoundingBox(Model);
transformed.Min=Vector3.Transform(transformed.Min, transform);
transformed.Max = Vector3.Transform(transformed.Max, transform);

Potiaľ je to jasné. Problémy ale čakajú za dverami. Rotovať krabicou, ako sme si napokon ukázali už minule, nie je tak ľahké. Nestačí iba aplikovať rotáciu na oba dva body, ktorými je krabica určená. Schválne si to skúste. Rotáciu je potrebné aplikovať na všetkých osem vrcholov. Preto je teda rovnako ako pri vykresľovaní získame, pootočíme az nich vytvoríme novú krabici:

Vector3[] body = new Vector3[8];
transformed.GetCorners(body);
for (int i = 0; i < body.Length; i++){
  body[i] = Vector3.Transform(body[i], Rotace);
}

transformed = BoundingBox.CreateFromPoints(body);

Ako to vyzerá keď sa krabice rotuje, to ste mohli vidieť na videu u minulého článku. Obe metódy zatiaľ nikde v našom engine nepoužijeme, budeme ich ale potrebovať neskôr.

To by bolo tak asi pre dnešok všetko. Nabudúce si vytvoríme kolízne manažér, ktorý sa nám o vykonaní kolízií postará. Dovtedy skúšajte editor, hrajte sa s tým čo už máte a komentujte, kritizujte a alebo taky chváľte, to už ponechám na vás.


 

Mal si s čímkoľvek problém? Stiahni si vzorovú aplikáciu nižšie a porovnaj ju so svojím projektom, chybu tak ľahko nájdeš.

Stiahnuť

Stiahnutím nasledujúceho súboru súhlasíš s licenčnými podmienkami

Stiahnuté 211x (1.05 MB)
Aplikácia je vrátane zdrojových kódov v jazyku C#

 

Predchádzajúci článok
3D bludisko v XNA - Podlahy druhýkrát a kolízie
Všetky články v sekcii
3D bludisko v XNA
Preskočiť článok
(neodporúčame)
3D bludisko v XNA - Kolízia prvýkrát
Článok pre vás napísal vodacek
Avatar
Užívateľské hodnotenie:
Ešte nikto nehodnotil, buď prvý!
Vodáček dělá že umí C#, naplno se již pět let angažuje v projektu ŽvB. Nyní studuje na FEI Upa informatiku, ikdyž si připadá spíš na ekonomice. Není mu také cizí PHP a SQL. Naopak cizí mu je Java a Python.
Aktivity