Lekce 4 - 3D bludiště v XNA - Mapy, zdi a podlaha
V minulé lekci, 3D bludiště v XNA - Editor map, jsme si vytvořili první část editoru map.
Vítejte po patnácté. Jistě jste si všimli předchozího článku od zmijozeláka. Editor pro naše bludiště bude velmi potřeba, zrychlí tvorbu nových levelů. My se ale nebudeme zaobírat v tomto článku pokračováním v editoru, ale pohneme se hrou. Vytvoříme si komponentu s mapou a také komponenty pro zdi a podlahy. Pokusíme se také načíst soubor s mapou. Je to hodně práce, tak se do toho hned pustíme.
Zeď
První bude potřeba komponenta pro zdi. Nebude to nic zázračného, celý
kód je velmi velmi krátký. Vytvoříme si tedy složku pro komponenty v
naší hře. Zde si vytvoříme novou třídu, pojmenujme si ji třeba
Zed
. Učiníme ji veřejnou, upravíme jmenný prostor. Budeme
dědit od třídy Model3D, která se nám modely reprezentuje. Do obsahu hry
přidáme model podlaha, který budeme používat. Vytvoříme si
konstruktor:
public Zed(int x, int z): base(new Vector3(x*20+10,0,z*20+10),Matrix.Identity,new Vector3(1.34f),"zed"){ }
Jako parametr předáváme souřadnice x a y v poli s bludištěm, které pak přepočteme na skutečné ve světě. Rotace nechceme žádné, měřítko jsem pokusně stanovil na 1.34x, i když jsem v Cinemě 4D nastavil velikost na přesně 100, tak jsou zde velikosti jiné. Zeď je prozatím hotova. Věci pro kolize přidáme příště.
Podlaha
Další komponenta pro průchozí podlahy, opět vytvoříme třídu stejně
jako minule, uděláme ji veřejnou, dědíme od Model3D
a zase
vytvoříme konstruktor:
public Podlaha(int x, int z): base(new Vector3(x*20+10,0,z*20+10),Matrix.Identity,new Vector3(1.34f),"podlaha"){ }
Vše je prakticky stejné jako u zdi. Jen se změnilo jméno modelu. Jistě vás zaujal podivný výpočet polohy. Bude se hodit později. O tom, jak tento vzoreček funguje, se zmíním doleji, pokud na to nezapomenu.
Mapa
Třetí komponenta, se kterou budeme pracovat, je mapa. Mapa v sobě bude mít uložené všechny zdi a podlahy, bude je přidávat do herního okna a opět zase odebírat. Zkrátka bude mít na starosti podobu bludiště. Jak už zmijozelák naznačil ve svém článku, mapa levelu bludiště je uložená ve dvojrozměrném poli celých čísel. Každé číslo bude reprezentovat jeden typ políčka. V následující tabulce je souhrn:
0 | volno |
1 | zeď |
99 | startovní pole |
100 | cilové pole |
Startovní a cílové pole by se měly vyskytovat v každé mapě jen jednou. Jistě může být více cílů, jeden snadno dosažitelný a další hůře, ale to prozatím pro jednoduchost vynechme. Prostoru pro typy políček je tam mnoho. Můžete si vymyslet políčka, která hráče zbrzdí, násilně jej otočí, otočí mu kameru vzhůru nohama a pak zas naopak... Fantazii se meze nekladou. Pro začátek si vystačíme s tímto základem.
Opět si založíme soubor s třídou Mapa ve složce pro komponenty. Opět
dědíme od třídy Component
. Vytvoříme si seznam, ve kterém
budeme uchovávat námi přidané komponenty:
private List<Component> Komponenty;
V konstruktoru jej vytvoříme:
public Mapa(){ Komponenty = new List<Component>(); }
Vytvoříme si metodu Nacti
, kde budeme načítat bludiště ze
souboru. Jako parametr metody předáme cestu k souboru. Ale jelikož jsme
programátoři a jsme líní psát něco co už jsme napsali znovu, tak se
podíváme do zmijozelákova článku, najdeme stejně pojmenovanou metodu,
nakopírujeme si ji a docela ji promázneme. Asi tak jak jsem to udělal
já:
public void Nacti(string cesta){ string[] radky = File.ReadAllLines(cesta); for (int j = 0; j < radky.Length; j++){ // rozbití řádku podle separátoru string[] radek = radky[j].Split(','); // naparsování hodnot v řádku for (int i = 0; i < radek.Length; i++){ int typ = int.Parse(radek[i]); } } }
Prakticky zmizelo vše s ukládáním do pole a také si proparsovaný
výsledek ukládáme do proměnné. Není to úplně bezpečný způsob, co
když se nám tam dostane nějaký znak namísto číslice? Tak nám aplikace
pochopitelně spadne, to ošetříme změnou metody z Parse
na
TryParse
:
int typ=-1; int.TryParse(radek[i],out typ);
Jak to celé jinak funguje se dočtete v předcházejícím článku. Nám už jen zbývá přidat příslušnou komponentu na dané místo, to se bude dělat velmi snadno, použijeme switch, ač bych tam nejraději nacpal stromeček if-else, ale pro výukové potřeby se obětuji:
Component c=null; switch (typ){ case 0:{ c=new Podlaha(i, j); break; } case 1:{ c=new Zed(i, j); break; } }
A pokud bude proměnná c
rozdílná od null
, tak
ji přidáme do našeho seznamu a zároveň i do herního okna:
if (c != null){ Parent.AddComponent(c); Komponenty.Add(c); }
Tady je vidět mírná nedomyšlenost enginu jako takového. Bylo by
příhodnější a lepší, kdyby mohla mapa sama řídit vykreslování
součástí bludiště. Sama si je spravovat, přidávat a mazat aniž by musela
komponenty dávat do herního okna. Engine na to ale není připravený, ale
jelikož se bude hodit i pro jiné případy, tak si tuto vlastnost dopíšeme
někdy v nějakém jiném dílu. Ještě je tam jeden menší zádrhel.
Proměnná c
bude vytvářena pokaždé při průchodu vnitřním
cyklem. Pokud budeme mít bludiště 10x10, tak to bude 100x. Což není úplně
zdravé. Přesuneme deklaraci před oba cykly a v každém kolečku ji jen
vynulujeme. Potřeba bude také metoda, která nám promaže prvky bludiště.
Vytvoříme si tedy metodu Promaz
, která se nám o to
postará:
public void Promaz(){ foreach (Component c in Komponenty){ Parent.RemoveComponent(c); } Komponenty.Clear(); }
A tuto metodu zavoláme v metodě Nacti
jako úplně první
věc. To nám zajistí zmizení starého bludiště v případě načtení
nového. Bystřejší z Vás si všimli, že zatím nepřidáváme žádnou
startovní ani cílovou podlahu. Je to z toho důvodu, že vytvoření těchto
tříd v sobě skrývá jedno malé úskalí a proto si je ponechám na
příště.
Kompletujeme
Zde by mohl tento článek končit, ale jelikož chci ukázat, že naše
snaha nebyla zbytečná a že již v tuto chvíli si lze bludiště ve hře
projít, přemístíme se do našeho herního okna a promažeme vše kromě
kamery, mřížky a samozřejmě barevného pozadí. Vytvoříme si komponentu s
mapou, přidáme ji do seznamu našich komponent a až potom načteme soubor
přízračně nazvaný ddd.map
. Já jsem to jméno nevymyslel, to
jiní. Tento soubor si přemístíme do složky, kde se nalézá zkompilovaný
exe soubor, já jej mám ve složce Debug
.
Mapa mapa = new Mapa(); AddComponent(mapa); mapa.Nacti("ddd.map");
U mřížky upravíme rastr na 20 okének se šířkou také 20:
AddComponent(new Mrizka(20,20));
Kupodivu je vše hotovo. Pokud se nám vše povedlo udělat správně, tak by při spuštění měl být vidět takovýto nějaký výsledek:
Díry nám zatím zůstávají pro startovní a koncovou dlaždici.
Příště si doplníme startovní a koncovou dlaždici a začneme uvažovat nad tím, jak zabránit průchodu zdí. Bude potřeba si vytvořit jakýsi kolizní manažer, který je obslouží. Zdrojový kód s celým projektem najdete na obvyklém místě pod článkem. Navíc jsem do něj přidal měření FPS. Mě se drží kolem šedesátky, určitě napište kolik máte vy třeba v komentářích pod článkem, je jich totiž jako šafránu, zdá se tedy že všichni všemu rozumí, všechno chápou, což je super, jenže bez zpětné vazby se mi funguje špatně. Proto prosím, pokud tento článek čtěte a líbil se vám nebo se vám nelíbil tak to dole v komentářích vyjádřete.
Děkuji a těším se na vás v rámci příští lekce, 3D bludiště v XNA - Dokončení editoru map.
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 247x (1.74 MB)
Aplikace je včetně zdrojových kódů v jazyce C# XNA