3. díl - Kreslíme a píšeme v XNA

C# .NET XNA game studio Robotris Kreslíme a píšeme v XNA

V minulém dílu seriálu tutoriálů jsme si ukázali vložení obsahu XNA hry. Nyní máme tedy vše připraveno k tomu, abychom si práci s obsahem vyzkoušeli. Pojďme na to :)

Načtení obsahu

Obsah musíme nejprve načíst. Již víme, že to se dělá v metodě LoadContent(). Než se k metodě přesuneme, vytvoříme si potřebné proměnné. Neanimované sprity se v XNA ukládají do datového typu Texture2D. Pro SpriteFonty máme typ SpriteFont.

Přidáme tedy proměnné:

private Texture2D kostkyPozadi, mraky, pozadi;
public SpriteFont fontCourierNew, fontBlox, fontBloxMaly;

S modifikátory přístupu si nelamte hlavu, hudba a fonty jsou veřejné, protože je budeme v budoucnu sdílet mezi komponentami.

Přesuňme se konečně do metody LoadContent. Za vytvořením instance spriteBatch začneme načítat obsah. Již víme, že k tomu použijeme vlastnost Content, přesněji metodu Load. Metoda je generická, musíme tedy specifikovat typ obsahu, který načítáme. Zajímavé je, že nezadáváme příponu souboru. Načtěme sprity:

pozadi = Content.Load<Texture2D>(@"Sprity\pozadi_level");
kostkyPozadi = Content.Load<Texture2D>(@"Sprity\pozadi_kostky");
mraky = Content.Load<Texture2D>(@"Sprity\spr_mraky");

A fonty:

fontCourierNew = Content.Load<SpriteFont>(@"Fonty\font_courier_new");
fontBlox = Content.Load<SpriteFont>(@"Fonty\font_blox");
fontBloxMaly = Content.Load<SpriteFont>(@"Fonty\font_blox_maly");

Máme načteno, nebyla to žádná věda. Pojďme kreslit.

Vykreslování spritů

Přesuňme se do metody Draw. Kód budeme psát za vymazání zobrazovacího zařízení. Již jsme si říkali, že vykreslování spritů probíhá pomocí instance spriteBatch. Vykreslování musíme nejprve zahájit, to uděláme metodou Begin:

spriteBatch.Begin();

Následně vykreslíme sprity. Jistě jste si všimli, že jsou ve formátu PNG. To proto, že PNG obsahuje tzv. alfakanál a umožňuje udělat různé části obrázku různě průhlené. Toho jsem využil a pozadí levelu s robotem je nahoře průhledné. Pod něj vykreslíme pozadí kostek a mezi kostky a robota vykreslíme mraky.

Samotné vykreslení spritu probíhá pomocí metody Draw:

spriteBatch.Draw(kostkyPozadi, new Vector2(0, 0), Color.White);
spriteBatch.Draw(mraky, new Vector2(0, 0), Color.White);
spriteBatch.Draw(pozadi, new Vector2(0, 0), Color.White);

Vidíme, že první parametr metody je textura k vykreslení. Druhým parametrem je pozice a třetím barva. Metoda má další přetížení, která umožňují např. obrázek roztahovat nebo rotovat, ale ta v našem seriálu nevyužijeme.

Význam textury je jasný. Pozoruhodné může být, že souřadnice zadáváme pomocí vektoru. Ještě divnější může být, že to nejsou celá čísla, ale čísla desetinná, přesněji typu float. S vektory se v XNA pracuje proto, že umožňují velmi jednoduše řešit různé pohyby, směry, úhly, srážky, zjistit vzdálenost 2 objektů a podobně. My je v tomto seriálu budeme používat jen jako souřadnice, ale i zde je výhoda, že můžeme objektu např. zadat reálnou rychlost pohybu místo celočíselné a tuto rychlost ke složkám vektoru (souřadnicím objektu) přičítat. Barva určuje obarvení textury, bílá texturu nemění.

Vykreslování nakonec ukončíme metodou End:

spriteBatch.End();

Zkusíme si hru spustit, měli bychom vidět takovýto výsledek:

Robotris, ukázková hra v XNA Game Studio

Díky dodržení pořadí spritů při vykreslení jsou opravdu mraky vložené mezi 2 pozadí. Ještě u spritů chvilku zůstaneme, zkusíme si totiž mraky rozpohybovat. Založíme si novou privátní proměnnou pozice typu Vector2. V Initialize() ji nastavíme na počátek souřadného systému (levý horní roh obrazovky):

pozice = new Vector2(0, 0);

Nyní změníme řádek s vykreslením mraků. Jako pozici předáme proměnnou pozice a barvu vynásobíme 0.8f, tím způsobíme, že mraky budou z 20% průhledné:

spriteBatch.Draw(mraky, pozice, Color.White * 0.8f);

Nyní se mraky vykreslují na pozici, kterou určuje proměnná. V metodě Update() složku vektoru X vždy o 1 snížíme:

pozice.X--;

Po posunu ošetříme, zda jsme s mraky nevyjely z obrazovky. To uděláme tak, že se zeptáme, zda je X mraků menší než -šířka obrázku mraků. Tu získáme pomocí vlastnosti Width na textuře mraků:

if (pozice.X < -(mraky.Width))
        pozice.X = 0;

Pokud jste postupovali správně, budou mraky jezdit doleva a jakmile vyjedou z obrazovky, vrátí se na startovní pozici.

Nyní změníme vykreslování tak, aby se vykreslovalo několik mraků vedle sebe:

for (int i = 0; i < 6; i++)
     spriteBatch.Draw(mraky, new Vector2(pozice.X + i * mraky.Width, 0), Color.White * 0.8f);

Dostaneme pěkný efekt nekonečné, plující oblohy:

Robotris, ukázková hra v XNA Game Studio

Působivé, že? :)

Změna barvy spritů

Ještě si zkusíme měnit barvu mraků. Budeme jednoduše měnit její jednotlivé složky, určitě víme, že barvy v počítači jsou složeny z červené, zelené a modré složky. Udělat změnu barvy tak, abychom vystřídali celé spektrum je složité a efekt bude velmi podobný tomu, když budeme střídat barev jen několik. Do třídy přidáme 2 další proměnné, změnu a směr:

private int zmena;
private int smer;

Proměnná zmena se bude měnit mezi hodnotou 0 - 96, jako kyvadlo. Smer určuje směr změny této proměnné, 1 rostoucí, -1 klesající.

V Initialize() nastavíme změnu na 0 a směr na 1, tedy rostoucí.

zmena = 0;
smer = 1;

V Update() nyní přičteme směr ke změně. Tím docílíme, že se bude s kladným směrem zvyšovat a se záporným snižovat. Také změnu pomocí 2 podmínek udržíme mezi 0 a 96:

zmena += smer;
if (zmena >= 96)
        smer = -1;
if (zmena <= 0)
        smer = 1;

Hotovo. Tento kyvadlový princip si zapamatujte, bude se nám ve hrách hodit, my ho ještě minimálně 2x použijeme jinde. No a pojďme do metody Draw(). Tam si před for cyklem založíme proměnnou barva a změníme její složky pomocí proměnné zmena, ty můžeme zadat jednoduše v pořadí RGB (červená, zelená, modrá) do konstruktoru barvy. Poté barvu dosadíme do barvy vykreslení maraků:

Color barva = new Color(128 + zmena, 255 - zmena, 128 + zmena);
for (int i = 0; i < 6; i++)
        spriteBatch.Draw(mraky, new Vector2(pozice.X + i * mraky.Width, 0), barva * 0.8f);

Červená složka bude mít základ 128 + změna, bude se tedy měnit mezi 128 - 224. Zelená mezi 255 - 159 a modrá opět 128 - 225. Můžete si s tím pohrát, ale pozor, aby hodnota nebyla vyšší než 255 nebo nižší než 0. Program by nespadl s chybou, ale barva by se měnila podivně.

Pro lepší efekt bychom si v praxi uložili několik pěkných barev do pole a potom na ně přecházeli metodou Lerp(). Mraky by také neměly být modré, aby to do barvy nezasahovalo. Pro naše účely ukázky nám to však takto stačí.

Vykreslení textu

Víme, že i text vykreslujeme SpriteBatchem, přesněji metodou DrawString(). Ta má opět několik přetížení pro text orotovaný a podobně, ale ta si určitě sami prohlídnete. Přesuňme se do metody Draw() a přidejme před spriteBatch.End(); následující řádky:

spriteBatch.DrawString(fontBlox, "nadpis velky", new Vector2(100, 100), Color.Yellow);
spriteBatch.DrawString(fontBloxMaly, "nadpis maly", new Vector2(100, 180), Color.Yellow);
spriteBatch.DrawString(fontCourierNew, "Příliš žluťoučký kůň úpěl ďábelské ódy", new Vector2(100, 240), Color.Red);

Výsledek:

Robotris, ukázková hra v XNA Game Studio

Parametry jsou stejné, jako u spritů, pouze je tu jeden navíc s textem. Nezapomínejte, že font Blox neumí české znaky! Ve fontu Courier je to již v pořádku.

Text může být občas špatně vidět, pokusíme se s tím něco udělat, vykreslíme pod něj stín. Abychom nemuseli stín kreslit stále znovu a znovu, uděláme si na text se stínem metodu. No a abychom se procvičili, přidáme ji přímo do spriteBatch, přesněji SpriteBatch podědíme.

Přidejte si k projektu Robotris novou třídu s názvem LepsiSpriteBatch, dědící z SpriteBatch. Nejprve nahoru přídáme potřebné usingy a třídu opatříme modifikátorem public:

...
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;

namespace Robotris
{
    public class LepsiSpriteBatch : SpriteBatch
    {

...

Při dědění třídy musíme vytvořit konstruktor (ač prázdný), volající konstruktor třídy nadřazené:

public LepsiSpriteBatch(GraphicsDevice graphicsDevice): base(graphicsDevice)
{
}

Teď přidáme naši metodu TextSeStinem, která vykreslí zadaný text 2x. jednou černě s trochou průhlednosti a kousek doprava dolů a podruhé normálně, jak jsme zvyklí. Parametry opíšeme z původní metody DrawString.

public void TextSeStinem(SpriteFont spriteFont, string text, Vector2 position, Color color)
{
        DrawString(spriteFont, text, new Vector2(position.X + 2, position.Y + 2), Color.Black * 0.8f);
        DrawString(spriteFont, text, position, color);
}

Nyní se vrátíme do třídy Hra a změníme typ SpriteBatch (hned na začátku v definici atributů třídy) na LepsiSpriteBatch:

LepsiSpriteBatch spriteBatch;

Podobnou změnu uděláme ještě na začátku metody LoadContent():

spriteBatch = new LepsiSpriteBatch(GraphicsDevice);

V metodě Draw změníme DrawString na TextSeStinem a spustíme. Mnohem lepší, ne?

Metody ve hře jsou trochu českoanglické, ale v programování to jinak nejde a alespoň trochu češtiny jsem hře chtěl dodat, když už dělám české tutoriály :) Ti pokročilí z vás si to mohou psát anglicky.

To by nám ke spritům stačilo.

Příště se podíváme na hudbu, zvukové efekty a reakci na klávesy.


 

Stáhnout

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

 

  Aktivity (1)

Článek pro vás napsal David Čápka
Avatar
Autor pracuje jako softwarový architekt a pedagog na projektu ITnetwork.cz (a jeho zahraničních verzích). Velmi si váží svobody podnikání v naší zemi a věří, že když se člověk neštítí práce, tak dokáže úplně cokoli.
Unicorn College Autor se informační technologie naučil na Unicorn College - prestižní soukromé vysoké škole IT a ekonomie.

Jak se ti líbí článek?
Celkem (5 hlasů) :
4.84.84.84.84.8


 


Miniatura
Předchozí článek
Vložení obsahu XNA hry
Miniatura
Všechny články v sekci
Od nuly k tetrisu v XNA game studio
Miniatura
Následující článek
Zvuky, hudba, klávesnice a myš v XNA

 

 

Komentáře
Zobrazit starší komentáře (31)

Avatar
vodacek
Redaktor
Avatar
vodacek:

no a co to píše za chybu?

 
Odpovědět 7.8.2013 11:21
Avatar
Juraj Mlich
Redaktor
Avatar
Odpovídá na vodacek
Juraj Mlich:

Používaš MonoGame? Pretože tam to musíš najskôr prekonvertovať!

Odpovědět 7.8.2013 11:52
Vždy je lepšie učiť sa z cudzích chýb, než z vlastných chýb.
Avatar
KlimiCZ
Člen
Avatar
Odpovídá na vodacek
KlimiCZ:

Teď sem to zapl a celé se to zhroutilo takže to budu muset udělat znovu pak dám error. čeky

Odpovědět 7.8.2013 18:40
Nesnaž se zakrýt něco, co jsi provedl úmyslně. Svět je tak malý, že dotyčný se to stejně dozví.
Avatar
anticary
Člen
Avatar
anticary:

Co mám napsat když chci, aby se ty mraky pohybovaly na místo té "pozice" protože pořád se mi to červeně podtrhává a píše to že to nezná.

 
Odpovědět 11.3.2014 16:38
Avatar
anticary
Člen
Avatar
 
Odpovědět 11.3.2014 17:01
Avatar
petr.vidrman
Člen
Avatar
petr.vidrman:

Nevleze se to na obrazovku mobilu :(
Vidím jen půlku obrazovky. Měním šířku na šířku mobilu
graphics.Prefe­rredBackBuffer­Width = 320;
graphics.Prefe­rredBackBuffer­Height = 480;

Zkouším změnu orientace obrazu
// Allow portrait mode as well
graphics.Suppor­tedOrientations = DisplayOrienta­tion.Portrait |
DisplayOrienta­tion.Landscape­Left |
DisplayOrienta­tion.Landscape­Right;

a stejně to nepomáhá...

V čem je problém...?

Editováno 28.5.2014 21:18
 
Odpovědět 28.5.2014 21:15
Avatar
CallMany Vyhlídal:

ahoj potřeboval bych poradit jenom když si zkopíruju tohle

for (int i = 0; i < 6; i++)
        spriteBatch.Draw(mraky, new Vector2(pozice.X + 1 * mraky.Width, 0), Color.White * 0.8f);

Tak mi to normálně funguje .. Ale když si to prostě jen opíšu a nezkopíruju tak mi to vykreslí pouze jeden mrak :/

 
Odpovědět 18.8.2015 11:44
Avatar
Pjanus
Člen
Avatar
Odpovídá na CallMany Vyhlídal
Pjanus:

V okopírovaném je pozice.X + i zatím co v opsaným máš pozice.X + 1

 
Odpovědět 18.8.2015 12:35
Avatar
Odpovídá na Pjanus
CallMany Vyhlídal:

Ajo :D Já jsem blbec díky moc ^^

 
Odpovědět 18.8.2015 13:03
Děláme co je v našich silách, aby byly zdejší diskuze co nejkvalitnější. Proto do nich také mohou přispívat pouze registrovaní členové. Pro zapojení do diskuze se přihlas. Pokud ještě nemáš účet, zaregistruj se, je to zdarma.

Zobrazeno 10 zpráv z 41. Zobrazit vše