V dnešnom dieli sa pozrieme na vykresľovanie na obrazovku.
Úvod
Napriek tomu, že robíme len konzolovú aplikáciu, si spravíme triedu, ktorá obstará vykresľovanie na obrazovku.
Vykresľovanie
Na vykresľovanie budeme používať samostatnú triedu, ktorú nazveme Renderer. Keďže budeme vykresľovať obsah len do jednej konzoly, bude nám stačiť jedna inštancia tejto triedy, takže ju spravíme statickú. V nej si budeme ukladať, čo sa ma vykresľovať, veľkosť okna a robiť všetky grafické operácie.
Premenné
Na začiatok budeme potrebovať premenné, do ktorých si budeme všetko ukladať. Nasledujúce tri definujte ako súkromné – Private. Ako prvé budeme potrebovať dvojrozmerné pole pixelov, pričom jeho veľkosť som nastavil na 80×30 a pomenoval som ho Display. Ďalej budete potrebovať premennú označujúcu veľkosť okna – nazval som ju Size a ako základné údaje som mu dal X 80 a Y 30. Na koniec ešte definujeme premennú určujúcu farbu pozadia – BackroundColor, ktorá bude typu ConsoleColor.
private static Pixel[,] Display = new Pixel[80, 30]; private static Vector2 Size = new Vector2(80, 30); private static ConsoleColor BackgroundColor = ConsoleColor.Black;
Ďalej si pridáme jednu verejnú – Public vlastnosť, ktorá bude slúžiť na nastavovanie veľkosti okna. Jej getter bude vracať hodnotu premennej Size. V setteri najprv vytvoríme nové pole Pixelov podľa novej hodnoty a do neho skopírujeme staré pole. Pokiaľ sa pole zväčšilo, zaplníme ho novými pixelmi farbi BackgroundColor, až po tomto nastavíme premennú Size.
public static Vector2 WindowSize { get { return Size; } set { Pixel[,] DisplayNew = new Pixel[value.X, value.Y]; //Vytvoríme nové pole pixelov for (int x = 0; x < value.X; x++) { for (int y = 0; y < value.Y; y++) { if ((x >= Size.X) || (y >= Size.Y)) //Ak je nová veľkosť väčšia ako stará { DisplayNew[x, y] = new Pixel() { Color = BackgroundColor }; //Vytvoríme nový pixel } else { DisplayNew[x, y] = Display[x, y]; //Inak ponecháme starý } } } Display = DisplayNew; //Uložíme nové pole Size = value; //Priradíme novú veľkosť } }
Inicializácia a čistenie
Nasleduje inicializovanie všetkých premenných, ktoré prebehne ako prvá vec po zapnutí hry. V tom nastavíme veľkosť okna, zakážeme viditeľnosť kurzora a pripravíme všetky pixeli.
public static void Init() { Console.SetWindowSize(Size.X, Size.Y); Console.CursorVisible = false; for (int x = 0; x < Size.X; x++) { for (int y = 0; y < Size.Y; y++) { Display[x, y] = new Pixel() { Color = BackgroundColor, Changed = false }; } } }
Potom si ešte pripravíme základnú metódu na vyčistenie obrazovky. V nej nahradíme všetky pixeli prázdnymi, ktorých farba bude mať hodnotu BackroundColor. Okrem toho však skontrolujeme, či sa zmenili oproti originálu a podľa toho im nastavíme premennú Changed na true, alebo hodnotu akú mali pôvodne.
public static void Clean() { for (int x = 0; x < Size.X; x++) { for (int y = 0; y < Size.Y; y++) { Pixel p = new Pixel(); p.Color = BackgroundColor; if (p != Display[x, y]) p.Changed = true; else p.Changed = Display[x, y].Changed; Display[x, y] = p; } } }
Čo sa týka čistenia, spravíme si ešte pomocnú metódu, ktorá nastaví farbu pozadia.
public static void Clean(ConsoleColor Background) { BackgroundColor = Background; Clean(); }
Vykresľovanie do premennej Display
Teraz už prichádzajú štyri metódy, ktoré budú obstarávať vykresľovanie do premennej Display. Ako prvé si pripravíme jednoduché vykreslenie jedného bodu. Toto skontroluje, či sa nesnažíme vykresliť bod mimo obrazovku a následne ho zapíšeme.
public static void DrawPoint(Vector2 Point, ConsoleColor color) { if ((Point.X >= 0) && (Point.Y >= 0) && (Point.X < Size.X) && (Point.Y < Size.Y)) //Ak nieje mimo obrazovku { Pixel p = new Pixel(); //Vytvorí nový bod p.Color = color; //Nastaví mu farbu Display[Point.X, Point.Y] = p; //A priradí ho. Nový bod ma automaticky nastavené Changed na true } }
Vykreslenie jedného bodu je jednoduché, a podobne je to aj z obdĺžnikom. Pri ňom musíme však prejsť všetky body, ktoré obdĺžnik obsiahne.
public static void DrawRectangle(Rectangle Rectangle, ConsoleColor color) { for (int X = Rectangle.X; X < Rectangle.X + Rectangle.Width; X++) { for (int Y = Rectangle.Y ; Y < Rectangle.Y + Rectangle.Height; Y++) { //Prejde všetky body obdĺžnika if ((X < Size.X) && (X >= 0) && (Y < Size.Y) && (Y >= 0)) //Skontroluje, či sú na obrazovke { Pixel p = new Pixel(); p.Color = color; Display[X, Y] = p; //A vykreslí ich } } } }
Na záver už prichádza len dvojica metód na vykreslenie textu. Pri nich podľa dĺžky vstupného stringu vykreslíme body, ktorým priradíme vlastnosti – farby písma, charakter, informácia o zmenie a prípadne aj farba pozadia.
public static void DrawString(string str, Vector2 Point, ConsoleColor fontColor) { for (int i = 0; i < str.Length; i++) //Pre každý bod dĺžky textu { if ((Point.X + i >= 0) && (Point.Y >= 0) && (Point.X + i < Size.X) && (Point.Y < Size.Y)) //Overíme či sa nachádza na obrazovke { Display[Point.X + i, Point.Y].Character = str[i]; //Priradíme znak Display[Point.X + i, Point.Y].CharColor = fontColor; //Farbu písma Display[Point.X + i, Point.Y].Changed = true; //A informujeme o zmene } } } public static void DrawString(string str, Vector2 Point, ConsoleColor fontColor, ConsoleColor backColor) { for (int i = 0; i < str.Length; i++) { if ((Point.X + i >= 0) && (Point.Y >= 0) && (Point.X + i < Size.X) && (Point.Y < Size.Y)) { Display[Point.X + i, Point.Y].Character = str[i]; Display[Point.X + i, Point.Y].CharColor = fontColor; Display[Point.X + i, Point.Y].CharColor = backColor; //Nastavíme farbu pozadia Display[Point.X + i, Point.Y].Changed = true; } } }
Týmto sme obsiahly vykresľovanie tvarov a textu. Teraz pri triede Renderer zostáva už len posledná vec.
Vykresľovanie do konzoly
Pri vykresľovaní do konzoly budeme kopírovať obsah premennej Display na obrazovku, pričom zohľadníme zmenu pixelu.
Na začiatku zabezpečíme, aby používateľ nemohol manuálne zmeniť veľkosť okna, presunieme kurzor na ľaví horný roh a tiež ho spravíme neviditeľným. Nasledujúci kód vložíme do statickej metódy Draw.
Console.SetWindowSize(Size.X, Size.Y); Console.CursorTop = 0; Console.CursorLeft = 0; Console.CursorVisible = false;
Následne prebehne vykresľovanie pixelu za pixelom. Zároveň budeme kontrolovať, či je premenná Changed nastavená na true.
for (int x = 0; x < Size.X; x++) { for (int y = 0; y < Size.Y; y++) { if (Display[x, y].Changed) //Ak sa pixel zmenil { Console.BackgroundColor = Display[x, y].Color; //Nastavíme farbu popredia Console.ForegroundColor = Display[x, y].CharColor; //Nastavíme farbu pozadia Console.CursorTop = y; Console.CursorLeft = x; //Presunieme kurzor na pozíciu Console.Write(Display[x, y].Character); //A zapíšeme pixel Display[x, y].Changed = false; //Na záver pixelu nastavíme Changed na false } } }
Vykresľovanie samotné prebieha konzolovou metódou Write, ktorá vypíše jeden znak. Ak je pixel prázdny, je to medzera – čiže vidno len pozadie. Nastavením hodnoty Changed zabezpečíme, aby sa pixel nemusel zbytočne vykreslovať, ak sa nezmenil.
Na koniec už len znova presunieme kurzor do ľavého horného rohu, vďaka čomu nebude blikať riadok pod hrou.
Console.CursorTop = 0; Console.CursorLeft = 0;
Integrovanie triedy Renderer
Aby všetko bolo pripravené, musíme triedu Renderer integrovať do nášho kódu. To spravíme v triede Program, kde na začiatku metódy Main pridáme Renderer.Init(); a na koniec našej hernej slučky Renderer.Draw(); .
V triede Game môžete ešte do metódy Draw pridať Renderer.Clean(); . To je vhodné vložiť aj hneď po inicializácií rendereru v triede Program.
Teraz už môžete Renderovanie odskúšať v metóde Draw triedy Game. Nabudúce sa už pozrieme na tvorbu hada.