IT rekvalifikácia. Seniorní programátori zarábajú až 6 000 €/mesiac a rekvalifikácia je prvým krokom. Zisti, ako na to!

2. diel - 3D bludisko v XNA - Kamera a mriežka

Vitajte po štrnástej. V tomto diele sa pripravíme na bludisko kameru, aj keď nie úplne presne tú, ktorú použijeme vo výsledku. Budeme sa potrebovať pri tvorbe v priestore pohybovať, prezerať výsledky a podobne. Potom si tiež vytvoríme debugovacie mriežku, ktorá sa bude hodiť aj do editora, ale aj pre meranie vzdialeností.

Free --camera-- kamera

Ako som naznačil v článku o kamerách, máme tú jednu, čo sa volá free a alebo tiež niekedy často fps kamera. Poznáte ju určite z každej strieľačky, funguje úplne ľahko. Postava sa pohybuje tam, kam kamera mieri, ak nepočítame rôzne úskoky bokom a podobne. My si pre naše skúšacie účely urobíme kameru, ktorá sa bude pohybovať hore aj dole bez akýchkoľvek obmedzení, nebude teda realistická. Pre potreby vývoja sa bez nej nezaobídeme. Pozerať sa okolo seba budeme myšou a pohybovať sa trebárs šípkami na klávesnici.

Ako základ použijeme našu triedu s target kamerou. Do zložky s kamerama si teda pridáme novú triedu a nazveme ju FreeCamera. Urobíme ju verejnú, upravíme menný priestor, ale na to ste si snáď už dúfam zvykli a budeme dediť od triedy TargetKamera. Vytvoríme si dve float premenné, kde budeme skladovať natočenie kamery. Nazveme je Yaw a Pitch, iste si pamätáte na obrázok s rotáciami, tak presne rovnako sa volali dve z nich, tretí nebudeme potrebovať:

float fYaw, fPitch;

public float Yaw{
  get{
    return fYaw;
  }
  set{
    fYaw = value;
  }
}

public float Pitch{
  get{
    return fPitch;
  }
  set{
    fPitch = value;
  }
}

V konstruktoru je vynulujeme:

public FreeKamera(GameScreen okno, Vector3 pozice, Vector3 target)
  : base(okno, pozice, target){
  fYaw = 0;
  fPitch = 0;
}

Teraz stačí už len a len kameru rozpohybovať. Tu príde na rad metóda Update a trocha tej vektorové matematiky. V tomto prípade nebudeme považovať triedu Vector 3 ako bod, ale ako vektor. Kto nevie čo to vektor je tak mu to nič moc nepovie, ale je to taká veličina, ktoré okrem svojej veľkosti moji tiež svoj smer. Pekným príkladom je napríklad sila pôsobiaca na teleso. Nielenže ju môžeme zmerať ako je veľká, ale tiež určiť akým smerom pôsobia. Presne toho využijeme. K modifikácii vektorov použijeme naše obľúbené matice. Je to veľmi výhodné, pretože nám potom odpadne všetky počítanie, o ktoré sa postarajú metódy vnútri XNA. Kód je to už trošku zložitejšie, ale dúfam, že to zvládnete. Prepíšme si teda metódu Update. Volanie metódy Update u nášho predka si ponecháme a budeme písať nad neho. Bude sa nám hodiť. Najprv si určíme natočenie kamery z pozícii myši. K tomu nám slúžia premenné DeltaX a DeltaY v triede Input, ktorú sme si predminulý vytvorili.

fYaw -= Parent.Engine.Input.DeltaX*0.01f;
fPitch -= Parent.Engine.Input.DeltaY * 0.01f;

Mínus je použité preto, aby pohyb korešpondoval s pohybom myši. Ďalej si vytvoríme maticu pre rotáciu. Metódu sme používali už skôr u natáčanie modelov, prekvapivo je to úplne rovnaké:

Matrix rotace = Matrix.CreateFromYawPitchRoll(fYaw, fPitch, 0);

Posledný parameter necháme na nule, pretože rotáciu okolo osi Z nebudeme používať. Prepočítame si tiež cieľ, kam sa kamera pozerá, pretože práve ten otáčaním kamery meníme. Nový cieľ získame tak, že k súčasnej pozícii kamery prirátame vytvorený natočený vektor:

Target = Pozice + Vector3.Transform(Vector3.Forward,rotace);

Vector3.Forward je vektor (0,0,-1) a keď sa znovu zamyslíte, tak akoby viedol od nás pred obrazovkou do nej, vedie teda dopredu. Vykonáme transformáciu našej maticou a to metódou Transform. Táto metóda jednoducho vynásobí daný vektor maticou a tým ho natočí. Otáčanie kamery máme hotovo, ale čo jej posun? Urobíme ho veľmi podobne. Hneď pod vytvorenie našej matice pridáme novú premennú posun a vynulujeme ju:

Vector3 posun = Vector3.Zero;

Potom už len skrz podmienky na stlačenie klávesy k tomuto vektora pripočítame jednotlivé smery, rovnako ako som to urobil ja:

if (Parent.Engine.Input.DrzenaKlavesa(Keys.Up)) posun += Vector3.Forward;
if (Parent.Engine.Input.DrzenaKlavesa(Keys.Down)) posun += Vector3.Backward;
if (Parent.Engine.Input.DrzenaKlavesa(Keys.Left)) posun += Vector3.Left;
if (Parent.Engine.Input.DrzenaKlavesa(Keys.Right)) posun += Vector3.Right;

Backward mieri smerom z obrazovky k nám, Left mieri doľava a Right doprava. Výsledný vektor ešte zväčšíme, respektíve zmenšíme a tým nastavíme rýchlosť pohybu:

posun *= 0.1f;

A výsledný vektor natočíme a pripočítame k súčasnej pozícii:

Pozice += Vector3.Transform(posun,rotace);

To je všetko. Teraz máme kameru vytvorenú a skúsime si ju pridať do nášho herného okna. Mal by tam byť od minula model koberca, čo sa nám akurát hodí. Namiesto target kamery dáme Free kameru a to je všetka zmena čo potrebujeme urobiť. Ak teraz hru spustíte, mala by kamera reagovať na pohyb myši na stlačenia klávesov s šípkami :-)

Opísané riešenie ale nie je stopercentne dobré. Táto kamera bude značne pri svojom pohybe reagovať na FPS, čo nie je práve žiaduce. Z tohto vplyvu sa môžeme ľahko vymaniť a to tak, že pri posúvaní zohľadníme čas, ktorý utiekol od posledného zavolanie metódy Update a to nasledovne:

posun *= 0.1f * (float)Parent.Engine.GameTime.ElapsedGameTime.TotalMilliseconds;

Blahoželám, kamera je hotová. Ďalším prvkom ktorý sa bude hodiť je ...

Pomocná mriežka

Hodí sa všade. Preto si ju urobíme aj my. Nie je to ale môj výmysel, zobral som to tu odtiaľ, upravil pre XNA, a mierne vytunil, ako je mojím dobrým zvykom. Urobme si teda triedu Mrizka v zložke s komponentmi. Opäť dedíme od komponenty, opäť je trieda verejná. Ako ostatne vždy. Vytvoríme si pole vertexov, kde si budeme ukladať jednotlivé body mriežky, použijeme VertexPositionColor. Potreba bude tiež inštancie triedy BasicEffect, pomocou ktorého všetko budeme vykresľovať ako v prvých dieloch nášho seriálu:

VertexPositionColor[] body;
BasicEffect effect;

V konstruktoru, ktorý bude prijímať počet čiar a ich vzdialenosť od seba, si toto pole inicializujeme:

int vzdalenost;
int pocet;

public Mrizka(int pocet, int vzdalenost){
  this.vzdalenost=vzdalenost;
  this.pocet = pocet;

  body=new VertexPositionColor[pocet*4];
}

Pýtate sa prečo 4x viac? Jeden počiatočný bod, jeden koncový pre každú priamku ak tomu pridajme že máme dva smery. Ďalej si prepíšeme metódu Load, kde si body vytvoríme. V algoritmu pre výpočet opäť použijeme vektory. Vytvoríme si inštanciu efektu a povolíme farby:

effect = new BasicEffect(Parent.Engine.GraphicsDevice);
effect.VertexColorEnabled = true;

Určíme farbu mriežky, tú si napokon môžeme posielať aj z konstruktoru, ale pre jednoduchosť:

Color Color = Color.Black;

Založíme si tiež pomocnú premennú k, ktorá nám bude počítať index v poli s vrcholmi:

int k = 0;

Nasleduje celý kód pre generovanie:

for (int i = 0; i < pocet; i++){
  Vector3 tmp = Vector3.Left * vzdalenost * i;
  if (pocet / 2 == i){
    body[k] = new VertexPositionColor(tmp, Color.Red);
    body[k + 1] = new VertexPositionColor(tmp + Vector3.Forward * (vzdalenost * (pocet - 1)), Color.Red);
    tmp = Vector3.Forward * vzdalenost * i;
    body[k + 2] = new VertexPositionColor(tmp, Color.Green);
    body[k + 3] = new VertexPositionColor(tmp + Vector3.Left * (vzdalenost * (pocet - 1)), Color.Green);
  }
  else{
    body[k] = new VertexPositionColor(tmp, Color);
    body[k + 1] = new VertexPositionColor(tmp + Vector3.Forward * (vzdalenost * (pocet - 1)), Color);
    tmp = Vector3.Forward * vzdalenost * i;
    body[k + 2] = new VertexPositionColor(tmp, Color);
    body[k + 3] = new VertexPositionColor(tmp + Vector3.Left * (vzdalenost* (pocet - 1)), Color);
  }

  k += 4;
}

Kód je zložitejšie ako u kamery, napriek tomu ho možno ľahko rozobrať. A viete čo? Skúste si na prísť sami, ako to vlastne funguje. Ostáva nám už len a len vykreslenie. Prepíšeme si teda metódu Draw, tentoraz ale tu s viacerými parametrami.

public override void Draw(Matrix View, Matrix Projection, Vector3 CameraPosition)

Vnútri nastavíme nášmu efektu nevyhnutné matice View a Projection:

effect.View = View;
effect.Projection = Projection;

Ako obvykle pošleme nastavenie do grafickej karty:

effect.CurrentTechnique.Passes[0].Apply();

A všetko známou metódou vykreslíme, používame zoznam čiar, preto teda počet vykreslovaných objektov je o polovicu menší než je kapacita poľa:

Parent.Engine.GraphicsDevice.DrawUserPrimitives<VertexPositionColor>(PrimitiveType.LineList, body, 0, body.Length / 2);

Hotovo, pridáme si našu mriežku do herného okna:

AddComponent(new Mrizka(20,30));

20 čiar bude stačiť a budú od seba vzdialené 30 bodov. Ak hru spustíme, tak by mal byť výstup podobný tomuto:

3D pomocná mriežka v C# .NET a XNA - 3D bludisko v XNA

Že nie? Naozaj nie. Mriežka nie je pod kobercom, ale za ním. Máme pár možností ako to vyriešiť. Môžeme sa obligátne tváriť, že je všetko v poriadku. Môžeme to zatajiť alebo zamlčať. Môžeme všetky body prepočítať a posunúť do stredu súradníc, ale to je nudné. Čo keď budeme chcieť polohu mriežky zmeniť? Najľahšie to vykonáme maticou World. Takže do metódy Draw umiestnime posun stredu mriežky do nuly:

effect.World = Matrix.CreateTranslation(new Vector3(pocet * (vzdalenost / 2),0, pocet * (vzdalenost / 2)));

Skvele. Teraz je to už konečne hotové.

Nabudúce sa pokúsime vytvoriť si bludisko z jednoduchých kociek. Opäť očakávam komentáre dole pod články a prípadné otázky, otázky, nejasnosti mi určite zdvihnú náladu, takže sa ich nebojte písať.


 

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é 330x (1.71 MB)
Aplikácia je vrátane zdrojových kódov v jazyku C#

 

Predchádzajúci článok
3D bludisko v XNA - Bludisko prvýkrát
Všetky články v sekcii
3D bludisko v XNA
Preskočiť článok
(neodporúčame)
3D bludisko v XNA - Editor máp
Č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