Vydělávej až 160.000 Kč měsíčně! Akreditované rekvalifikační kurzy s garancí práce od 0 Kč. Více informací.
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í.

Lekce 2 - 3D bludiště v XNA - Kamera a mřížka

Vítejte po čtrnácté. V minulé lekci, 3D bludiště v XNA - Bludiště poprvé, jsme si rozebrali ukázkovou hru.

V tomto díle se připravíme na bludiště kameru, i když ne úplně přesně tu, kterou použijeme ve výsledku. Budeme se potřebovat při tvorbě v prostoru pohybovat, prohlížet výsledky a podobně. Potom si také vytvoříme debugovací mřížku, která se bude hodit i do editoru, ale i pro měření vzdáleností.

Free --camera-- kamera

Jak jsem naznačil v článku o kamerách, máme tu jednu, co se jmenuje free a nebo také někdy často fps kamera. Znáte ji určitě z každé střílečky, funguje naprosto snadno. Postava se pohybuje tam, kam kamera míří, nepočítáme-li různé úskoky stranou a podobně. My si pro naše zkoušecí účely uděláme kameru, která se bude pohybovat nahoru i dolů bez jakýchkoliv omezení, nebude tedy realistická. Pro potřeby vývoje se bez ní neobejdeme. Dívat se kolem sebe budeme myší a pohybovat se třebas šipkami na klávesnici.

Jako základ použijeme naši třídu s target kamerou. Do složky s kamerama si tedy přidáme novou třídu a nazveme ji FreeCamera. Učiníme ji veřejnou, upravíme jmenný prostor, ale na to jste si snad už doufám zvykli a budeme dědit od třídy TargetKamera. Vytvoříme si dvě float proměnné, kde budeme skladovat natočení kamery. Nazveme je Yaw a Pitch, jistě si pamatujete na obrázek s rotacemi, tak přesně stejně se jmenovaly dvě z nich, třetí nebudeme potřebovat:

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;
}

Nyní stačí už jen a pouze kameru rozpohybovat. Zde přijde na řadu metoda Update a trocha té vektorové matematiky. V tomto případě nebudeme považovat třídu Vector3 jako bod, ale jako vektor. Kdo neví co to vektor je tak mu to nic moc neřekne, ale je to taková veličina, které kromě své velikosti mí také svůj směr. Pěkným příkladem je kupříkladu síla působící na těleso. Nejenže ji můžeme změřit jak je veliká, ale také určit jakým směrem působí. Přesně toho využijeme. K modifikaci vektorů použijeme naše oblíbené matice. Je to velmi výhodné, protože nám pak odpadne veškeré počítání, o které se postarají metody uvnitř XNA. Kód je to už trošku složitější, ale doufám, že to zvládnete. Přepišme si tedy metodu Update. Volání metody Update u našeho předka si ponecháme a budeme psát nad něj. Bude se nám hodit. Prvně si určíme natočení kamery z pozici myši. K tomu nám slouží proměnné DeltaX a DeltaY ve třídě Input, kterou jsme si předminule vytvořili.

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

Mínus je použito proto, aby pohyb korespondoval s pohybem myši. Dále si vytvoříme matici pro rotaci. Metodu jsme používali již dříve u natáčení modelů, překvapivě je to úplně stejné:

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

Poslední parametr necháme na nule, protože rotaci kolem osy Z nebudeme používat. Přepočteme si také cíl, kam se kamera dívá, protože právě ten otáčením kamery měníme. Nový cíl získáme tak, že k současné pozici kamery přičteme vytvořený natočený vektor:

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

Vector3.Forward je vektor (0,0,-1) a když se znovu zamyslíte, tak jakoby vedl od nás před obrazovkou do ní, vede tedy dopředu. Provedeme transformaci naší maticí a to metodou Transform. Tato metoda jednoduše vynásobí daný vektor maticí a tím jej natočí. Otáčení kamery máme hotovo, ale co její posun? Uděláme jej velmi podobně. Hned pod vytvoření naší matice přidáme novou proměnnou posun a vynulujeme ji:

Vector3 posun = Vector3.Zero;

Pak již jen skrze podmínky na stisknutí klávesy k tomuto vektoru přičteme jednotlivé směry, stejně jako jsem to udělal já:

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 míří směrem z obrazovky k nám, Left míří doleva a Right doprava. Výsledný vektor ještě zvětšíme, respektive zmenšíme a tím nastavíme rychlost pohybu:

posun *= 0.1f;

A výsledný vektor natočíme a přičteme k současné pozici:

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

To je vše. Nyní máme kameru vytvořenu a zkusíme si jí přidat do našeho herního okna. Měl by tam být od minula model koberce, což se nám akorát hodí. Namísto target kamery dáme Free kameru a to je veškerá změna co potřebujeme udělat. Pokud nyní hru spustíte, měla by kamera reagovat na pohyb myši na stisky kláves s šipkami :-)

Popsané řešení ale není stoprocentně dobré. Tato kamera bude značně při svém pohybu reagovat na FPS, což není zrovna žádoucí. Z tohoto vlivu se můžeme snadno vymanit a to tak, že při posouvání zohledníme čas, který utekl od posledního zavolání metody Update a to následovně:

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

Blahopřeji, kamera je hotová. Dalším prvkem který se bude hodit je...

Pomocná mřížka

Hodí se všude. Proto si ji uděláme i my. Není to ale můj výmysl, sebral jsem to tady odtud, upravil pro XNA, a mírně vytunil, jak je mým dobrým zvykem. Udělejme si tedy třídu Mrizka ve složce s komponentami. Opět dědíme od komponenty, opět je třída veřejná. Jako ostatně vždycky. Vytvoříme si pole vertexů, kde si budeme ukládat jednotlivé body mřížky, použijeme VertexPositionColor. Potřeba bude také instance třídy BasicEffect, pomocí kterého vše budeme vykreslovat jako v prvních dílech našeho seriálu:

VertexPositionColor[] body;
BasicEffect effect;

V konstruktoru, který bude přijímat počet čar a jejich vzdálenost od sebe, 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];
}

Ptáte se proč 4x víc? Jeden počáteční bod, jeden koncový pro každou přímku a k tomu přidejme že máme dva směry. Dále si přepíšeme metodu Load, kde si body vytvoříme. V algoritmu pro výpočet opět použijeme vektory. Vytvoříme si instanci efektu a povolíme barvy:

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

Určíme barvu mřížky, tu si ostatně můžeme posílat i z konstruktoru, ale pro jednoduchost:

Color Color = Color.Black;

Založíme si také pomocnou proměnnou k, která nám bude počítat index v poli s vrcholy:

int k = 0;

Následuje celý kód pro generování:

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 složitější než u kamery, přesto jej lze snadno rozebrat. A víte co? Zkuste si na přijít sami, jak to vlastně funguje. Zbývá nám už jen a jenom vykreslení. Přepíšeme si tedy metodu Draw, tentokrát ale tu s více parametry.

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

Uvnitř nastavíme našemu efektu nezbytné matice View a Projection:

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

Jako obvykle pošleme nastavení do grafické karty:

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

A vše známou metodou vykreslíme, používáme seznam čar, proto tedy počet vykreslovaných objektů je o polovinu menší než je kapacita pole:

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

Hotovo, přidáme si naši mřížku do herního okna:

AddComponent(new Mrizka(20,30));

20 čar bude stačit a budou od sebe vzdáleny 30 bodů. Pokud hru spustíme, tak by měl být výstup podobný tomuto:

3D pomocná mřížka v C# .NET a XNA - 3D bludiště v XNA

Že ne? Opravdu ne. Mřížka není pod kobercem, ale za ním. Máme pár možností jak to vyřešit. Můžeme se obligátně tvářit, že je vše v pořádku. Můžeme to zatajit nebo zamlčet. Můžeme všechny body přepočítat a posunout do středu souřadnic, ale to je nudné. Co když budeme chtít polohu mřížky změnit? Nejsnadněji to provedeme maticí World. Takže do metody Draw umístíme posun středu mřížky do nuly:

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

Skvěle. Nyní je to už konečně hotové.

Příště, 3D bludiště v XNA - Editor map, se pokusíme vytvořit si bludiště z jednoduchých kostek. Opět očekávám komentáře dole pod články a případné otázky, dotazy, nejasnosti mi určitě zvednou náladu, takže se je nebojte psát.


 

Měl jsi s čímkoli problém? Stáhni si vzorovou aplikaci níže a porovnej ji se svým projektem, chybu tak snadno najdeš.

Stáhnout

Stažením následujícího souboru souhlasíš s licenčními podmínkami

Staženo 329x (1.71 MB)
Aplikace je včetně zdrojových kódů v jazyce C# XNA

 

Předchozí článek
3D bludiště v XNA - Bludiště poprvé
Všechny články v sekci
3D bludiště v XNA
Přeskočit článek
(nedoporučujeme)
3D bludiště v XNA - Editor map
Článek pro vás napsal vodacek
Avatar
Uživatelské hodnocení:
3 hlasů
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