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:
Ž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#