Obiekty graficzne na przykładzie prostej gry- tylny bufor grafiki. Część 2

W tej części ćwiczeń dotyczących wykorzystani modułu graficznego zrealizujemy model wykorzystani tylnego bufora grafiki. Tylny bufor grafiki to metoda rysowania obiektów graficznych poza widoczną częścią ekranu. Cała grafika w tym kolejne klatki animacji, przesunięci a obiektów graficznych do nowych współrzędnych ekranowych odbywają się na przygotowanej wcześniej wirtualnej bitmapie.

Wirtualna bitmapa, to obiekt klasy Bitmap (w kodzie programu zmienna o nazwie bmpBuforEkranGry). Stosowanie metody rysowania w tylnym buforze zmniejsza nieporządane efekty migotania ekranu komputera podczas odświeżania grafiki (nie mówimy tu o synchronizacji pionowej). Tylny bufor powinien mieć rozmiary (wysokość, szerokość w pikselach) takie jak przygotowana widoczna w aplikacji powierzchnia do rysowania ruchomych grafik.

Cel ćwiczenia

Celem jest utworzenie aplikacji desktopowej, która wykorzysta metodę tylnego bufora do rysowania obiektu graficznego złożonego z kilku różnych klatek. Po narysowaniu obiektu gotowa grafika wysyłana jest na widoczny ekran graficzny

Jeżeli nie masz kody z poprzedniej lekcji to pobierz klikając na ten link: pobierz kod

Modyfikujemy klasę bitmap

Do utworzonej klasy bitmap w części pierwszej dodajemy te zmienne

Wskazówka:


 private Bitmap bmpBuforEkranGry;//bufor graficzny
 private Graphics gEkranGry;
 private int w, h,//szerkość klatki
			 ileKolumn, ileWierszy;//ilosc kolumn i wierszy swiata gry

Zmieniamy konstruktor klasy

W prowadzamy zmiany do konstruktora klasy. Do wywołania konstruktora dodamy dwa parametry związane z rozmiarem tworzonego świata gry. Wielkość świata gry podamy w ilości kolumn i wierszy widocznych klatek na ekranie. W pomyśle rozwiązania zakładamy, że tworzony świat nie będzie duży- nie będzie przekraczać rozmiaru ekranu komputera.

Wskazówka:


//konstruktor
public bitmapy(int ileK,int ileW,int _w,int _h)
{
	this.w = _w;
	this.h = _h;
	this.ileKolumn = ileK;
	this.ileWierszy = ileW;
	//przygotuj bufor rysowania ekranu gry
	bmpBuforEkranGry = new Bitmap(this.ileKolumn*this.w,
					   this.ileWierszy*this.h,
					   System.Drawing.Imaging.PixelFormat.Format32bppArgb);
	//skojarz moduł graficzny z buforem rysowania ekranu gry
	gEkranGry= Graphics.FromImage(bmpBuforEkranGry);
}

Wprowadzone zmiany w konstruktorze wymagają zmian w kodzie pliku głównej formatki (plik Form1.cs)

Wskazówka:


public Form1()
{
	InitializeComponent();
	//buduj swiat 6x8- 6 kolumn na 8 wierszy po 48x48 pikseli
	bmp=new bitmapy(6,8,48,48);
}

Zmieniamy zawartość kodu destruktora, w którym musimy dopisać instrukcje zwalniające przydzieloną pamięć na bufor graficzny i moduł rysujący

Wskazówka:


//destruktor
~bitmapy()
{
	//zwolnij pamięc zajmowaną przez zasoby graficzne 
	zasobyBmp.Dispose();
	foreach (Bitmap b in bitmaps) { b.Dispose(); }
	//zwolnij bufor rysowania ekranu gry
	bmpBuforEkranGry.Dispose();
	gEkranGry.Dispose();
}

Rysujemy grafikę w tylnym buforze

Tylny bufor został przygotowany w konstruktorze klasy bitmap. Moduł graficzny został w konstruktorze skojarzony z buforem instrukcją

gEkranGry= Graphics.FromImage(bmpBuforEkranGry);

Od tej pory wszystkie instrukcje rysowania będą się odbywać poza ekranem w zmiennej gEkranGry. Na tą chwilę napiszemy testowe rozwiązanie sprawdzające czy prawidłowo rysujemy w utworzonym buforze graficznym.

Wskazówka:


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

Teraz musimy napisać publiczną funkcję, która narysuje świat gry i wyśle go na ekran monitora w głównym oknie aplikacji

Wskazówka:


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

Pełny kod klasy bitmap wygląda jak poniżej

bitmapy.cs:


using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Security.Cryptography.X509Certificates;
using System.Text;
using System.Threading.Tasks;

namespace czolg_1
{
    internal class bitmapy
    {
        public Bitmap zasobyBmp;
        //lista klatek wycinanych bitmapek
        private List<Bitmap> bitmaps = new List<Bitmap>();
        private Bitmap bmpBuforEkranGry;//bufor graficzny
        private Graphics gEkranGry;
        private int w, h,//szerkość klatki
                    ileKolumn, ileWierszy;//ilosc kolumn i wierszy swiata gry

        public int W {//zwroc,ustaw szerokość klatki 
                       get { return w; } 
                       set { w = value;}
                     }
        public int H
        {   //zwroc,ustaw wysokość klatki
            get { return h; }
            set { h = value; }
        }

        //konstruktor
        public bitmapy(int ileK,int ileW,int _w,int _h)
        {
            this.w = _w;
            this.h = _h;
            this.ileKolumn = ileK;
            this.ileWierszy = ileW;
            //przygotuj bufor rysowania ekranu gry
            bmpBuforEkranGry = new Bitmap(this.ileKolumn*this.w,
                               this.ileWierszy*this.h,
                               System.Drawing.Imaging.PixelFormat.Format32bppArgb);
            //skojarz moduł graficzny z buforem rysowania ekranu gry
            gEkranGry= Graphics.FromImage(bmpBuforEkranGry);
        }

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

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

        public void WczytajBmp(string plik)
        {
            //funkcja wczytuje bitmapę z pliku graficznego
            zasobyBmp = new Bitmap(plik);
            //wczytany plik zasobów potnij na klatki o wymiarach W x H
            //w programie przyjałem 48 x 48 pikseli
            int ileKolumn = zasobyBmp.Width / W;
            int ileWierszy= zasobyBmp.Height / H;
            for (int i = 0; i < ileWierszy; i++)
             for (int j = 0; j < ileKolumn; j++)
                {
                    bitmaps.Add(new Bitmap(w, h, System.Drawing.Imaging.PixelFormat.Format32bppArgb));
                    Graphics g = Graphics.FromImage(bitmaps[bitmaps.Count-1]);
                    g.DrawImage(zasobyBmp, new Rectangle(0, 0, w, h), 
                                           new Rectangle(j*w, i*h, w, h),
                                           GraphicsUnit.Pixel);
                    g.Dispose();
                }
        }
        public void testPokazBmp(Graphics g, int x, int y, int w, int h)
        {
            //funkcja testowa, pokazuje pierwszą klatkę ze współrzednych (0,0,0+w,0+h)
            g.DrawImage(zasobyBmp, new Rectangle(x,y,w,h), new Rectangle(0, 0, w, h), GraphicsUnit.Pixel);
        }
        public void pokazKlatke(Graphics g, int idKlatka, int x, int y)
        {
            //funkcja testowa, pokazuje dowolną klatkę 
            g.DrawImage(bitmaps[idKlatka], new Rectangle(x, y, w, h), new Rectangle(0, 0, w, h), GraphicsUnit.Pixel);
        }
        //destruktor
        ~bitmapy()
        {
            //zwolnij pamięc zajmowaną przez zasoby graficzne 
            zasobyBmp.Dispose();
            foreach (Bitmap b in bitmaps) { b.Dispose(); }
            //zwolnij bufor rysowania ekranu gry
            bmpBuforEkranGry.Dispose();
            gEkranGry.Dispose();
        }
    }
}

Wysyłamy tylny bufor na ekran monitora

Wyświetlenie utworzonej grafiki na widocznym ekranie jest już proste. W testowym rozwiązaniu wyślemy bieżącą klatkę czołgu we współrzędne ekranowe: x=10 pikseli, y= 10 pikseli. W kodzie głównej formatki modyfikujemy funkcję zdarzenia kliknięcia w klawisz zapisaną w części pierwszej

Wskazówka:


int id = 0;
private void button2_Click(object sender, EventArgs e)
{
	bmp.PokazEkranGry(panel1.Handle,id, 10, 10);
	id++;
	if(id>3)id = 0;
}

Pełny kod pliku Form1.cs zapisany jest poniżej

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;
        public Form1()
        {
            InitializeComponent();
            //buduj swiat 6x8- 6 kolumn na 8 wierszy po 48x48 pikseli
            bmp=new bitmapy(6,8,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 button1_Click(object sender, EventArgs e)
        {
            //test sprawdzajacy czy prawidłowo załadowano grafikę
            Graphics g = Graphics.FromHwnd(panel1.Handle);
            //osadx grafike we wspołrzędnych x=64, y=128
            //szerokośc i wysokośc klatki 48x48 pikseli
            bmp.testPokazBmp(g, 64, 128, 48, 48);
            g.Dispose();    
        }
        int id = 0;
        private void button2_Click(object sender, EventArgs e)
        {
            bmp.PokazEkranGry(panel1.Handle,id, 10, 10);
            id++;
            if(id>3)id = 0;
        }
    }
}

Skompiluj program i sprawdź efekt działania. Prawidłowo działająca aplikacja wyświetla na białym tle bieżąca klatkę czołgu, którą można zmieniać

wyświetlanie grafiki. Visual studio C#

Test prostego przesuwania obiektu graficznego

Utworzone funkcje w klasie bitmap pozwalają już przetestować ruch obiektu. Wystarczy, że będziemy modyfikować współrzędne X i Y wyświetlanego obiektu graficznego w buforze grafiki. W tym celu dodamy do głównego okna formatki klawisz zmiany współrzędnej X, tak aby obiekt poruszał się od lewej do prawej strony.

W oknie tworzonej formatki dodajemy kontrolkę Button (patrz poniższy rysunek)

przesuwanie obiektu graficznego Visual studio C#

W kodzie zdarzenia kliknięcia w dodany klawisz dopisujemy te instrukcje (zwróć uwagę, że została dodana zmienna lokalna wspX)

Form1.cs:


int wspX = 10;
private void button3_Click(object sender, EventArgs e)
{
	bmp.PokazEkranGry(panel1.Handle, id, wspX, 10);
	wspX++;
	if (wspX > 100) wspX=10;
}

Skompiluj program i sprawdź efekt działania. Prawidłowo działająca aplikacja pokazuje ruch obiektu wymazując obraz z poprzedniej pozycji. Na te samej zasadzie można dodać klawisze ruchu obsługujące pozostałe kierunki i zwroty ruchu

rysowanie grafiki Bitmap Visual studio C#

W następnej części zajmiemy się tłem graficznym gry- czyli światem prostej gry 2D utworzonej jako aplikacja desktopowa w Visual Studio C#.

Alkomat- wirtualny test

Alkomat- darmowa aplikacja na Androida

Pobierz ze sklepu Google Play
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