Obiekty graficzne na przykładzie prostej gry- mapa świata gry. Część 3

W tej części cyklu o obiektach graficznych omówimy sposób ładowania prostego świata gry 2D zapisanego w pliku tekstowym w postaci numerów indeksów graficznych klatek wycinanych z obrazu zasobów gry. Do pracy potrzebny jest kod z poprzedniej części, którego jeżeli nie masz możesz pobrać z tego linku: pobierz kod

Cel ćwiczenia

Utwórz klasę odpowiedzialną za odczyt pliku świata gry oraz zmodyfikuj istniejący kod, tak aby można było wczytany świat pokazać na ekranie planszy gry

Modyfikujemy okno głównej formatki tak aby panel przewidziany na ekran gry zajmował całą przestrzeń okna

gra 2D Visual studio C#

Rozmiar głównego okna formatki ustawiamy na 976;758. Rozmiar ten pozwoli otrzymać świat wielkości 20x15 klatek po 48x48 pikseli każda

rozmiar okna formatki Visual studio C#

Kontrolce panel1 właściwość Dock ustawiamy na Fill

Modyfikujemy plik zasobów graficznych

W dowolnym programie graficznym na przykład GIMP?ie rysujemy nasze zasoby graficzne

sprite 2d Visual studio C#

Zasoby graficzne rysujemy tak aby były zorganizowane w kolejne klatki o przyjętym rozmiarze (tu 48x48 pikseli). Takie klatki będą mieć ustalone indeksy wyliczone z ich pozycji w pliku graficznym zasobów

sprite 2d Visual studio C#

Utworzonym plikiem graficznym podmieniamy plik graficzny zapisany w części 2 omawianego cyklu.

Tworzymy tekstowy plik zapisu planszy świata gry

Kompilator Visual Studio ma również możliwość tworzenia plików tekstowych. W takim pliku tekstowym zapiszemy indeksy tworzące graficzny wygląd danego poziomu gry.

Wybieramy opcję tworzenia pliku tekstowego jak poniżej

plik tekstowy txt Visual studio C#

Plik powinien zawierać 20 liczb zapisanych w 15 wierszach. Każda z liczb ma być oddzielona spacją, która to będzie separatorem kolejnych indeksów. Utworzony plik powinien być zapisany w folderze, który zawiera plik *.exe tworzonej aplikacji

plik tekstowy txt zasobów gry 2d Visual studio C#

Na tym etapie pisania prostej gry 2D mój plik zawiera tylko i wyłącznie liczbę 16, która jest indeksem graficznego kafelka z obrazem trawy

Tworzymy klasę Swiat

Klasa Swiat będzie odpowiedzialna za odczytanie pliku świata gry oraz jego narysowanie. Tworzymy w projekcie plik klasy Swiat.cs i wstępnie piszemy jej konstruktor. Patrz poniższy kod

Wskazówka:


using System;
using System.IO;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Drawing;

namespace czolg_1
{
    internal class Swiat
    {
        static int ileKlatekX = 20, ileKlatekY = 15;
        int[,] plansza=new int[ileKlatekY,ileKlatekX];

        public int IleKlatekX
        {   //zwróc ilośc kolumn
            get { return ileKlatekX; }
        }

        public int IleKlatekY
        {   //zwróc ilośc wierszy
            get { return ileKlatekY; }
        }

        //konstruktor
        public Swiat(string plik)
        {
            //odczytaj wskazany plik z mapą świata gry
            string[] txt=System.IO.File.ReadAllLines(plik);
        }
    }
}

Na tym etapie możemy sprawdzić czy tworzona klasa potrafi odczytać wskazany plik tekstowy. W kodzie głównej formatki wprowadzamy poniższe modyfikacje

Wskazówka:


namespace czolg_1
{
    public partial class Form1 : Form
    {
        bitmapy bmp;
        Swiat swiat;
        public Form1()
        {
            InitializeComponent();
            swiat = new Swiat("swiat1.txt");
            //buduj swiat 20x15- 20 kolumn na 15 wierszy po 48x48 pikseli
            bmp =new bitmapy(swiat.IleKlatekX,swiat.IleKlatekY,48,48);
        }

W pliku kodu klasy Swiat ustawimy pułapkę debugera w miejscu wskazanym poniżej. Po skompilowaniu i uruchomieniu projektu w podglądzie stany zmiennych możemy sprawdzić czy plik z zapisem świata gry jest odczytywany

debuger Visual studio C#

Odczytujemy indeksy kafli graficznych

Klasę Swiat modyfikujemy do odczytu kolejnych indeksów kafli graficznych. Odczytane indeksy zapiszemy do tablicy dwuwymiarowej. Kod konstruktora wygląda jak poniżej

Wskazówka:


//konstruktor
public Swiat(string plik)
{
	//odczytaj wskazany plik z mapą świata gry
	string[] txt=System.IO.File.ReadAllLines(plik);
	for(int i= 0; i < txt.Count(); i++)
	{
		//odczytaj wiersze i ustaw znak separatora na spację
		string[]wiersz=txt[i].Split(' ');
		//czytaj liczby zapisane w wierszach
		for (int j = 0; j < wiersz.Length; j++)
		{
			int k = int.Parse(wiersz[j]);
			//przypisz odczyatne indeksy klatek do
			//tablicy planszy mapy świata gry
			plansza[i, j] = k;
		}
	}
}

Dodajemy funkcję rysującą odczytany świat planszy gry

Wskazówka:


public void RysujSwiat(Graphics g, List<Bitmap> bmp,int w,int h)
{
	for(int i=0;i< ileKlatekY;i++)
	for (int j = 0; j < ileKlatekX; j++)
		{
			int idKlatka = plansza[i,j];
			g.DrawImage(bmp[idKlatka],
					new Rectangle(j*w, i*h, w, h),
					new Rectangle(0, 0, w, h),
					GraphicsUnit.Pixel);
		}
}

Utworzoną funkcję rysującą planszę świata gry wywołamy w klasie bitmapy w taki sposób, że będzie również spełniać późniejszą rolę mazania/ odświeżania stanu grafiki gry. Poniżej zmodyfikowany kod funkcji RysujSwiatGryBufor

Wskazówka:


private void RysujSwiatGryBufor(Swiat swiat,int idKlatka,int _x,int _y)
{
	//na ten czas prób czyść mapę swiata kolorem
	//gEkranGry.Clear(System.Drawing.Color.White);
	swiat.RysujSwiat(gEkranGry, bitmaps,W,h);
	//na teraz testowe rysowania jednego czolgu gracza
	pokazKlatke(gEkranGry, idKlatka,_x,_y);
}

Ta modyfikacja wymusza modyfikację argumentów funkcji PokazEkranGry

Wskazówka:


public void PokazEkranGry(IntPtr uchwyt, Swiat swiat, int idKlatka, int _x, int _y) 
{
	Graphics g = Graphics.FromHwnd(uchwyt);
	RysujSwiatGryBufor(swiat,idKlatka,_x,_y);
	g.DrawImage(bmpBuforEkranGry, 0, 0);
	g.Dispose();
}

Aby sprawdzić czy świat jest prawidłowo odczytany i rysowany w kodzie głównej formatki w zdarzeniu Paint wprowadzimy wywołanie

Wskazówka:


private void Form1_Paint(object sender, PaintEventArgs e)
        {
            bmp.PokazEkranGry(panel1.Handle, swiat, 1, 0, 0);
        }

Skompiluj program i sprawdź efekt działania. Na tę chwilę powinieneś otrzymać poniższy obraz

rysowanie świata gry 2d Visual studio C#

Projektujemy wygląd świata gry

W tej chwili mamy gotową obsługę metody rysowania świata gry w tylnym buforze i po jego narysowaniu wysyłamy gotową grafikę na ekran. Tera zostaje nam zaprojektowania układu planszy gry. Gra w założeniach ma być prosta. Gracze poruszają się czołgami na przykład w układzie korytarzy.

gra 2d Visual studio C#

Za ładne to nie jest, ale jak na razie na te potrzeby wystarczy. Poniżej pełne kody

Plik Form1.cs:


using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace czolg_1
{
    public partial class Form1 : Form
    {
        bitmapy bmp;
        Swiat swiat;
        public Form1()
        {
            InitializeComponent();
            swiat = new Swiat("swiat1.txt");
            //buduj swiat 20x15- 20 kolumn na 15 wierszy po 48x48 pikseli
            bmp =new bitmapy(swiat.IleKlatekX,swiat.IleKlatekY,48,48);
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            //wczytaj bitmape na sztwyno z pliku
            //zakładamy, że plik grafiki istnieje
            //więc nie musimy sprawdzać czy istnieje
            bmp.WczytajBmp("./g/czolg.png");
        }

        private void Form1_Paint(object sender, PaintEventArgs e)
        {
            bmp.PokazEkranGry(panel1.Handle, swiat, 1, 0, 0);
        }
    }
}

Plik Swiat.cs:


using System;
using System.IO;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Drawing;

namespace czolg_1
{
    internal class Swiat
    {
        static int ileKlatekX = 20, ileKlatekY = 15;
        int[,] plansza=new int[ileKlatekY,ileKlatekX];

        public int IleKlatekX
        {   //zwróc ilośc kolumn
            get { return ileKlatekX; }
        }

        public int IleKlatekY
        {   //zwróc ilośc wierszy
            get { return ileKlatekY; }
        }

        //konstruktor
        public Swiat(string plik)
        {
            //odczytaj wskazany plik z mapą świata gry
            string[] txt=System.IO.File.ReadAllLines(plik);
            for(int i= 0; i < txt.Count(); i++)
            {
                //odczytaj wiersze i ustaw znak separatora na spację
                string[]wiersz=txt[i].Split(' ');
                //czytaj liczby zapisane w wierszach
                for (int j = 0; j < wiersz.Length; j++)
                {
                    int k = int.Parse(wiersz[j]);
                    //przypisz odczyatne indeksy klatek do
                    //tablicy planszy mapy świata gry
                    plansza[i, j] = k;
                }
            }
        }
        public void RysujSwiat(Graphics g, List<Bitmap> bmp,int w,int h)
        {
            for(int i=0;i< ileKlatekY;i++)
            for (int j = 0; j < ileKlatekX; j++)
                {
                    int idKlatka = plansza[i,j];
                    g.DrawImage(bmp[idKlatka],
							new Rectangle(j*w, i*h, w, h),
							new Rectangle(0, 0, w, h),
							GraphicsUnit.Pixel);
                }
        }
    }
}

Plik bitmapy.cs:


using System;
using System.IO;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Drawing;

namespace czolg_1
{
    internal class Swiat
    {
        static int ileKlatekX = 20, ileKlatekY = 15;
        int[,] plansza=new int[ileKlatekY,ileKlatekX];

        public int IleKlatekX
        {   //zwróc ilośc kolumn
            get { return ileKlatekX; }
        }

        public int IleKlatekY
        {   //zwróc ilośc wierszy
            get { return ileKlatekY; }
        }

        //konstruktor
        public Swiat(string plik)
        {
            //odczytaj wskazany plik z mapą świata gry
            string[] txt=System.IO.File.ReadAllLines(plik);
            for(int i= 0; i < txt.Count(); i++)
            {
                //odczytaj wiersze i ustaw znak separatora na spację
                string[]wiersz=txt[i].Split(' ');
                //czytaj liczby zapisane w wierszach
                for (int j = 0; j < wiersz.Length; j++)
                {
                    int k = int.Parse(wiersz[j]);
                    //przypisz odczyatne indeksy klatek do
                    //tablicy planszy mapy świata gry
                    plansza[i, j] = k;
                }
            }
        }
        public void RysujSwiat(Graphics g, List<Bitmap> bmp,int w,int h)
        {
            for(int i=0;i< ileKlatekY;i++)
            for (int j = 0; j < ileKlatekX; j++)
                {
                    int idKlatka = plansza[i,j];
                    g.DrawImage(bmp[idKlatka],
					        new Rectangle(j*w, i*h, w, h),
							new Rectangle(0, 0, w, h),
							GraphicsUnit.Pixel);
                }
        }
    }
}
Układ okresowy- kod qr
Układ okresowy

Układ okresowy pierwiastków- darmowa aplikacja na Androida

Pobierz ze sklepu Google Play
Alkomat- wirtualny test kod qr
Alkomat- wirtualny test

Alkomat- darmowa aplikacja na Androida

Pobierz ze sklepu Google Play
Taklarz- olinowanie stałe kod qr
Olinowanie stałe- kalkulator średnic

Olinowanie stałe- darmowa aplikacja na Androida

Pobierz ze sklepu Google Play
przepis na gogfry

Przepis na gofry

zobacz
przepis na bitą śmietanę

Przepis na bitą śmietanę

zobacz