Lekce 5 - 3D bludiště v XNA - Dokončení editoru map
V minulém tutoriálu, 3D bludiště v XNA - Mapy, zdi a podlaha, jsme si vytvořili komponenty, ze kterých se bude naše bludiště skládat.
Dnes editor dokončíme a zprovozníme. Jako první dokončeme naši třídu Mapa, ve které nám chybí implementovat její vykreslení.
Vykreslení mapy
Metoda k vykreslení mapy bude tedy také ve třídě Mapa.cs. V parametru metody si předáme Graphics, na který se má kreslit. Stejně tak si předáme i velikost hrany políčka. Různé hodnoty nám umožní vykreslovat zvětšenou či zmenšenou mapu. Jelikož nejsme žádní začátečníci, ukažme si opět kód metody a záhy si jej popišme.
public void Vykresli(Graphics g, int hrana) { // mřížka for (int j = 0; j <= Vyska; j++) { for (int i = 0; i <= Sirka; i++) { g.DrawLine(Pens.Gray, new Point(0, j * hrana), new Point(Sirka * hrana - 1, j * hrana)); // - g.DrawLine(Pens.Gray, new Point(i * hrana, 0), new Point(i * hrana, Vyska * hrana - 1)); // | } } // Políčka for (int j = 0; j < Vyska; j++) { for (int i = 0; i < Sirka; i++) { switch (policka[i, j]) { // Zeď case 1: g.FillRectangle(Brushes.CornflowerBlue, i * hrana, j * hrana, hrana, hrana); break; // Start case 99: g.FillEllipse(Brushes.GreenYellow, i * hrana, j * hrana, hrana, hrana); g.DrawString("S", new Font("Arial", hrana / 2), Brushes.Green, i * hrana + (hrana / 6), j * hrana + (hrana / 6)); break; // Cíl case 100: g.FillEllipse(Brushes.Purple, i * hrana, j * hrana, hrana, hrana); g.DrawString("C", new Font("Arial", hrana / 2), Brushes.Red, i * hrana + (hrana / 6), j * hrana + (hrana / 6)); break; } } } }
První 2 cykly vykreslí na daný Graphics čtvercovou síť, aby se nám mapa lépe designovala. První kreslí vodorovné čáry, druhý svislé. Zde asi není co vysvětlovat.
Obdobně vykreslujeme v dalších 2 cyklech i políčka. Typ políčka rozlišíme switchem, zeď je vykreslena jako čtverec, políčka start a cíl jako barevná kolečka s nápisy S a C. Souřadnice nápisu na políčkách i velikost nápisu jsou pronásobeny velikostí hrany, aby při zoomování mapy zůstal text přibližně stejně velký a na stejné pozici.
Třídu Mapa můžeme prohlásit za hotovou.
Vizuální část
Logika je hotová, zprovozněme vizuální část editoru, tedy tu formulářovou.
Jako první si do třídy FormEditor přidejme proměnnou mapa, kde budeme mít uloženou instanci mapy, se kterou v editoru aktuálně pracujeme:
Mapa mapa;
Do konstruktoru formuláře přidejme vytvoření nové instance mapy velikosti 16x16. Pokud editor spustíme, bude v něm tedy připravena tato prázdná mapa:
mapa = new Mapa(16, 16);
Vybereme v designu mapaPictureBox a naklikneme mu událost Paint(). V té jednoduše zavoláme vykreslení mapy, kde v parametrech předáme Graphics PictureBoxu a šířku hrany z hranaNumericUpDown.
mapa.Vykresli(e.Graphics, Convert.ToInt32(hranaNumericUpDown.Value));
Můžeme spustit:

Vidíme vykreslenou prázdnou mapu, tedy mřížku 16x16. PictureBox však není roztáhnutý na velikost, kterou by měl mít. Tu můžeme jednoduše spočítat, pojďme přidat do formu metodu, kterou budeme mapu obnovovat. Ta kromě překreslení mapy ještě změní velikost PictureBoxu. Přidejme do FormEditor.cs metodu metodu PrekresliMapu():
private void prekresliMapu() { mapaPictureBox.Size = new Size(mapa.Sirka * Convert.ToInt32(hranaNumericUpDown.Value) + 1, mapa.Vyska * Convert.ToInt32(hranaNumericUpDown.Value) + 1); mapaPictureBox.Refresh(); }
Velikost mapy jednoduše spočítáme podle rozměrů mapy a velikosti hrany vykreslovaného políčka. Formuláři přidáme událost Load a v ní překreslíme mapu:
prekresliMapu();

PictureBox se nám krásně roztáhl a pokud je form menší, zobrazí se u panelu posuvníky a můžeme mapou skrolovat. To jsme přesně chtěli. Do události Load připišme výběr výchozího políčka v typPolickaComboBox:
typPolickaComboBox.SelectedIndex = 0;
hraNumericUpDown naklikneme událost ValueChanged a v ní překreslíme mapu:
prekresliMapu();
Nyní naklikneme na mapaPictureBox událost MouseClick. Do ní umístíme následující kód:
int hrana = Convert.ToInt32(hranaNumericUpDown.Value); int x = e.X / hrana; int y = e.Y / hrana; // Hodnoty v pořadí, jako jsou popisky políček v ComboBoxu int[] hodnoty = {1, 99, 100, 0}; // Kontrola správnosti souřadnic if ((x >= 0) && (y >= 0) && (x < mapa.Sirka) && (y < mapa.Vyska)) { // Levé myšítko přidá zeď if (e.Button == MouseButtons.Left) mapa.policka[x, y] = hodnoty[typPolickaComboBox.SelectedIndex]; // Pravé myšítko vymaže zeď if (e.Button == MouseButtons.Right) mapa.policka[x, y] = 0; prekresliMapu(); }
Prvně si zjistíme ze souřadnic myši políčko na mapě, na které se kliklo. To je jednoduché, stačí je jen vydělit velikostí políčka. Dále si připravíme pole hodnot, které bude představovat hodnoty pro jednotlivá políčka v typPolickaComboBox. Je důležité, aby hodnoty měly to samé pořadí, jako mají itemy v ComboBoxu. Vidíme, že zeď má hodnotu 1, start 99, cíl 100 a prázdno 0. Vypočítané souřadnice je nutno otestovat, jelikož uživatel mohl kliknout mimo mapu a sahali bychom tak za hranice pole. Nyní pouze otestujeme, které tlačítko bylo stisknuté. Pokud levé, vložíme na příslušené políčko na mapě hodnotu dle vybraného itemu v ComboBoxu. Pokud bylo stisknuto pravé tlačítko, vložíme prázdné políčko (pravým tlačítkem tedy mažeme). Nakonec mapu překreslíme a máme hotovo. MouseClick jsme použili z důvodu možnosti odlišení tlačítek narozdíl od události Click.
Můžete vyzkoušet. Bylo by skvělé, kdybychom mohli na mapu pokládat políčka i tažením myši, nejen klikáním. Stačí pouze, když metodu mapaPictureBox_MouseClick() vybereme i u události MouseMove. Nyní se mapa vytváří opravdu jednoduše:

Vytvoření nové mapy
Přejděme k formuláři pro vytvoření nové mapy. Do jeho třídy si přidáme veřejný atribut s instancí mapy.
public Mapa mapa;
Při kliknutí na tlačítko "Vytvořit" do atributu vytvoříme novou instanci mapy požadované velikosti a formulář zavřeme:
mapa = new Mapa(Convert.ToInt32(sirkaUpDown.Value), Convert.ToInt32(vyskaUpDown.Value));
Close();
Formu naklikneme událost Shown, kde vždy nastavíme tuto instanci na null:
mapa = null;
Při každém zobrazení dialogu se nám tedy mapa vymaže. Díky tomu poznáme, zda jsme formulář opustili kliknutím na tlačítko "Vytvořit" (v mapě je instance mapy) nebo jen zavřeli křížkem (v mapě je null).
Vytvoření formu a následné vytvoření mapy nakonec naklikneme do položky Nová v MenuStrip:
FormNovaMapa formNovaMapa = new FormNovaMapa(); formNovaMapa.ShowDialog(); if (formNovaMapa.mapa != null) { mapa = formNovaMapa.mapa; prekresliMapu(); }
Můžete si zkusit založit novou mapu.
Ukládání a načítání
Dokončení editoru vrcholí. Přidejme si z Toolboxu dialogy pro otevření a uložení souboru. Nejprve přidejme SaveFileDialog pojmenovaný mapaSaveFileDialog. Nastavíme mu DefaultExt (výchozí příponu) na map. To bude přípona našich souborů s mapou. Dále nastavíme Filter na:
Mapy (*.map)|*.map
Stejně přidáme i nastavíme mapaOpenFileDialog. U něj ještě vymažeme vlastnost FileName (necháme prázdnou hodnotu).
V MenuStrip rozklikneme položku Otevřít a vložíme logiku pro zobrazení dialogu a načtení mapy:
if (mapaOpenFileDialog.ShowDialog() == DialogResult.OK)
{
mapa.Nacti(mapaOpenFileDialog.FileName);
prekresliMapu();
}
Obdebně to uděláme i u položky Uložit:
if (mapaSaveFileDialog.ShowDialog() == DialogResult.OK)
{
mapa.Uloz(mapaSaveFileDialog.FileName);
prekresliMapu();
}
Jako symbolickou třešinku na dortu přidáme vyvolání MessageBoxu při kliknutí na položku "O programu":
MessageBox.Show("Editor k tutoriálu na 3D bludiště v C# XNA z programátorské sociální sítě www.itnetwork.cz");
Editor levelů pro bludiště je hotový Za domácí úkol si můžete
omezit, aby šlo vložit pouze jedno startovní a cílové pole, i když tato
ochrana není nutně potřeba.
Příště, 3D bludiště v XNA - Podlahy podruhé a kolize, si s oslíkem načtete vaší krásnou mapu do enginu a můžete se v ní projí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 193x (70.49 kB)
Aplikace je včetně zdrojových kódů v jazyce C# XNA