Obiekty graficzne na przykładzie prostej gry- strzelanie

W tej części dodamy rozwiązanie pozwalające strzelać graczom zgodnie z kierunkiem ustawionej lufy. Do siódmej części będziemy potrzebować kod z części szóstej, który można pobrać z tego linku (pobierz kod z części 6)

Właściciel pocisku- czyli kto strzela?

W klasie Pocisk musimy dodać zmienną przechowującą właściciela pocisku. To pole ułatwi nam rozwiązanie odejmowania punktów życia trafionego celu. Po prostu- zakładamy, że nie można w siebie samego strzelić. I po tym polu w czasie wykrywania kolizji pocisku z celami będziemy rozróżniać trafienia.

Przechodzimy do pliku Pocisk.cs i dodajemy wiersz kodu public Czolg wlasciciel;

Wskazówka:


namespace czolg_1
{
    internal class Pocisk
    {
        int[] klatki = {0,1,2,3};
        private int wys, szer;//wysokośc i szerokośc klatki świata gry
        public float x, y;//pozycja we współrzednych ekranowych
        private float x0, y0;//pozycja współrzednych wystrzału
        public int idKlatka = 0;
        public bool fZniszcz = false;
        //opóźnienie
        private const byte GRANICA_OPOZNIENIE = 3;
        private byte licznikOpoznienia = 0;
        private byte kierunek;
        private float v = 10.0f;//predkośc pocisku
        private float zasieg = 0;//zasięg pocisku
        //
        public Czolg wlasciciel;
        
        //konstruktor

Modyfikujemy konstruktora klasy Pocisk poprzez dodanie zmiennej typu Czolg jako kolejny argument w konstruktorze. Patrz poniższy kod

Wskazówka:


//konstruktor
public Pocisk(float _x, float _y, int _wys, int _szer, byte _kierunek, Czolg _w)
{
	wys = _wys;
	szer = _szer;
	//zapamietaj współrzędne strzalu
	x = _x;
	y = _y;
	x0 = _x;
	y0 = _y;
	//kierunek strzalu
	kierunek = _kierunek;
	//ustaw zasięg na 6 kafli świata
	zasieg = 6 * 48;
	wlasciciel = _w;
}

Ta zmiana spowoduje pojawienie się komunikatu błędu. Musimy przejść do pliku Form1.cs i dokonać zmiany w funkcji TestPocisku(). Zmiana dotyczy jednej linii kodu (swiat.listaPociskow.Add?)

Wskazówka:


private void TestPocisku()
{
	int x, y;//tymczasowe wspołrzedne testowych eksplozji
	Random los = new Random();
	x = los.Next(8, this.Width - 8);
	y = los.Next(8, this.Height - 8);
	//zero to strzal na pólnoc
	swiat.listaPociskow.Add(new Pocisk(x, y, 8, 8, 0,czolgA));
}

Można przetestować program. Jako takich zauważalnych zmian nie ma, testujemy tylko aby sprawdzić czy modyfikacje nie wprowadziły błędów.

Na bazie funkcji TestPocisku tworzymy nową o nazwie Strzal. Nowej funkcji wstępnie przypiszemy trzy argumenty: właściciel, współrzędna X i współrzędna Y. Na moment przejdziemy do pliku Czolg.cs i dopiszemy funkcje- metody zwracające współrzędne X,Y czołgu gracza oraz numer klatki obrazu bitmapy. Numer indeksu tablicy klatek wykorzystujemy do ustalenia kierunku ruchu (N,E,S,W)

Wskazówka:


//funkcja odczytu max predkosci
public float Vmax { get { return maxV; } }
//zwróć wpsółrzędne obiektu ze świata gry
public float X { get { return x; } }
public float Y { get { return y; } }
//zwróc klatke kierunku ruchu, potrzebne do strzelania
public byte IdKlatka { get { return (byte)idKlatka; } }

Dodajemy funkcję Strzal w pliku Form1.cs napisana na bazie funkcji TestPocisku

Wskazówka:


private void Strzal(Czolg cz)
{
	swiat.listaPociskow.Add(
	      new Pocisk(cz.X, cz.Y, 8, 8, cz.IdKlatka, cz)
		  );
}

Wywołanie funkcji strzelania wykonujemy w funkcji private void Form1_KeyDown(object sender, KeyEventArgs e) przez podmianę TestPocisku na Strzal. Patrz poniżej

Wskazówka:


//testowe eksplozje
if (e.KeyCode == Keys.T)
{
	TestEksplozji();
}
//testowe pociski
if (e.KeyCode==Keys.Enter)
{
	Strzal(czolgA);
}

Skompiluj program i sprawdź efekt działania. Czołg z lewego górnego narożnika może strzelać ale tylko na północ. W pozostałych kierunkach nie. Nie jest to błąd. Po prostu w części szóstej nie napisaliśmy kodu dla kierunków strzału innych niż kierunek północny

Strzelanie gra 2d . Visual Studio C#

Strzelamy w każdym kierunku

Przechodzimy do pliku Pocisk.cs i dopisujemy kod pozwalający strzelać w każdym kierunku. Zmiany robimy w funkcji Ruch()

Wskazówka:


public void Ruch()
{
	//kierunek pólnoc
	switch (kierunek)
	{
		//na Pólnoc
		case 0:y -= v;break;
		//na Wschod
		case 1:x += v;break;
		//na Południe
		case 2: y += v; break;
		//na Zachód
		case 3: x -= v; break;
	}
	if (Math.Abs(x - x0) > zasieg || Math.Abs(y - y0) > zasieg)
		fZniszcz = true;
}

Skompiluj program i sprawdź efekt działania. Czołg z górnego lewego rogu może strzelać w każdym kierunku gdy naciskamy klawisz ENTER.

Strzelanie w kazdym kierunku gra 2d. Visual Studio C#

Można zauważyć, że jest jeden mankament w czasie strzelania. Pociski wylatują ze środka wieży. Trzeba to poprawić tak, aby pociski wylatywały z końca lufy.

Justowanie pocisku względem końca lufy

Ustawienie pozycji wylotu pocisku z lufy jest zmienne w zależności od kierunku strzału. Współrzędne końca lufy są inne. Przesunięci najłatwiej jest wyznaczyć otwierając grafikę zasobów na przykład w programie GIMP i odpowiednim narzędziem GIMP?a zmierzyć wartość przesunięcia w pikselach.

Wprowadzamy zmiany w konstruktorze klasy Pocisk. Przechodzimy do pliku Pocisk.cs i przeprogramowujemy konstruktora klasy

Wskazówka:


//konstruktor
public Pocisk(float _x, float _y, int _wys, int _szer, byte _kierunek, Czolg _w)
{
	//przesuniecie dla konca lufy
	float dx = 0, dy = 0;
	wys = _wys;
	szer = _szer;
	//kierunek strzalu
	kierunek = _kierunek;
	switch (kierunek)
	{
		//Północ
		case 0:dy = -24.0f;break;
		//Wschód
		case 1:dx = 24.0f; break;
		//Polódnie
		case 2:dy = 24.0f;break;
		//Zachód
		case 3:dx = -24.0f;break;
	}
	//zapamietaj współrzędne strzalu
	x = _x + dx;
	y = _y + dy;
	x0 = _x;
	y0 = _y;
	
	//ustaw zasięg na 6 kafli świata
	zasieg = 6 * 48;
	wlasciciel = _w;
}

Skompiluj program i sprawdź efekt działania. Poniższy zrzut ekranu pokazuje efekt zmian. Widać, że pocisk wylatuje z lufy a nie ze środka wieży.

Pociski gra 2d. Visual Studio C#

Strzelamy na dwa czołgi

Aby czołg drugiego gracza mógł strzelać wystarczy dodać obsługę strzału dla innego klawisza niż ENTER. Ustalimy, że klawiszem SPACJA strzela gracz A, a klawiszem ENTER strzela gracz B. Przechodzimy do pliku Form1.cs i wprowadzamy zmiany w funkcji Form1_KeyDown

Wskazówka:


private void Form1_KeyDown(object sender, KeyEventArgs e)
{
	//klawiszologia
	//dla gracza A
	if (e.KeyCode == Keys.A) czolgA.Obracaj(-1);
	if (e.KeyCode == Keys.D) czolgA.Obracaj(1);
	if (e.KeyCode == Keys.W) 
	{ 
		czolgA.fRuch = true;
		czolgA.v= czolgA.Vmax;
	}
	if (e.KeyCode == Keys.S)
	{
		czolgA.fRuch = true;
		czolgA.v = -czolgA.Vmax;
	}
	//dla gracza B
	if (e.KeyCode == Keys.Left) czolgB.Obracaj(-1);
	if (e.KeyCode == Keys.Right) czolgB.Obracaj(1);
	if (e.KeyCode == Keys.Up) 
	{ 
	   czolgB.fRuch = true;
	   czolgB.v = czolgB.Vmax;
	}
	if (e.KeyCode == Keys.Down)
	{
		czolgB.fRuch = true;
		czolgB.v = -czolgB.Vmax;
	}
	//strzela czołg A
	if (e.KeyCode == Keys.Space)
	{
		Strzal(czolgA);
	}
	//strzela czołg B
	if (e.KeyCode==Keys.Enter)
	{
		Strzal(czolgB);
	}
}

Skompiluj program i sprawdź jak działa. Prawidłowo działający program pozwala strzelać obu graczom. Patrz poniższa ilustracja

strzelanie gra 2d, Visual Studio C#

I to tyle w tej części. Poniżej pełne kody aktualnych plików gry

Wskazówka:


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


namespace czolg_1
{
    public partial class Form1 : Form
    {
        bitmapy bmp;
        Swiat swiat;
        Czolg czolgA, czolgB;



        private void TestEksplozji()
        {
            int x, y;//tymczasowe wspołrzedne testowych eksplozji
            Random los= new Random();
            x = los.Next(48, this.Width - 48);
            y = los.Next(48, this.Height - 48);
            swiat.listaEksplozji.Add(new Eksplozja(x,y,bmp.H,bmp.W));
        }

        private void TestPocisku()
        {
            int x, y;//tymczasowe wspołrzedne testowych eksplozji
            Random los = new Random();
            x = los.Next(8, this.Width - 8);
            y = los.Next(8, this.Height - 8);
            //zero to strzal na pólnoc
            swiat.listaPociskow.Add(new Pocisk(x, y, 8, 8, 0,czolgA));
        }

        private void Strzal(Czolg cz)
        {
            swiat.listaPociskow.Add(
			    new Pocisk(cz.X, cz.Y, 8, 8, cz.IdKlatka, cz)
				);
        }

        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);
            //rob czolg zwrocony na południe
            czolgA = new Czolg(2,0, 0, bmp.H, bmp.W);
            czolgA.LadujKlatkiKierunku(0,1,2,3);
            //rób czołg zwrócony na połnoc
            czolgB = new Czolg(0,14, 19, bmp.H, bmp.W);
            czolgB.LadujKlatkiKierunku(8, 9, 10, 11);
        }

        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");
            bmp.WczytajBmpPocisku("./g/pocisk.png",8,8);
        }

        private void Form1_Paint(object sender, PaintEventArgs e)
        {
            bmp.PokazEkranGry(this.Handle, swiat, czolgA, czolgB);
        }

        private void Form1_KeyDown(object sender, KeyEventArgs e)
        {
            //klawiszologia
            //dla gracza A
            if (e.KeyCode == Keys.A) czolgA.Obracaj(-1);
            if (e.KeyCode == Keys.D) czolgA.Obracaj(1);
            if (e.KeyCode == Keys.W) 
            { 
                czolgA.fRuch = true;
                czolgA.v= czolgA.Vmax;
            }
            if (e.KeyCode == Keys.S)
            {
                czolgA.fRuch = true;
                czolgA.v = -czolgA.Vmax;
            }
            //dla gracza B
            if (e.KeyCode == Keys.Left) czolgB.Obracaj(-1);
            if (e.KeyCode == Keys.Right) czolgB.Obracaj(1);
            if (e.KeyCode == Keys.Up) 
            { 
               czolgB.fRuch = true;
               czolgB.v = czolgB.Vmax;
            }
            if (e.KeyCode == Keys.Down)
            {
                czolgB.fRuch = true;
                czolgB.v = -czolgB.Vmax;
            }
            //strzela czołg A
            if (e.KeyCode == Keys.Space)
            {
                Strzal(czolgA);
            }
            //strzela czołg B
            if (e.KeyCode==Keys.Enter)
            {
                Strzal(czolgB);
            }
        }
        
        private void Form1_KeyUp(object sender, KeyEventArgs e)
        {
            //klawiszologia
            //dla gracza A
            if (e.KeyCode == Keys.W ||
                e.KeyCode == Keys.S)
                czolgA.fRuch = false;
            //dla gracza B
            if (e.KeyCode == Keys.Up ||
                e.KeyCode == Keys.Down)
                czolgB.fRuch = false;
            
        }

        private void timer1_Tick(object sender, EventArgs e)
        {
            zegarGry();
        }

        private void zegarGry()
        {
            czolgA.Ruch();
            czolgB.Ruch();
            bmp.PokazEkranGry(this.Handle, swiat, czolgA, czolgB);
        }
    }
}

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 List<Eksplozja> listaEksplozji = new List<Eksplozja>();
        public List<Pocisk> listaPociskow = new List<Pocisk>();

        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);
                }
        }
        public void RysujWszystkieEksplozje(Graphics g,List<Bitmap> bmp)
        {
            foreach (var e in listaEksplozji)
            {
                e.Rysuj(g, bmp, e.idKlatka);
                e.LicznikKlatek();
            }
            //przeglądnij listę i usun te które zakończyły animację
            for(int i = 0; i < listaEksplozji.Count; i++)
                if(listaEksplozji[i].fZniszcz)listaEksplozji.RemoveAt(i);
        }
        public void RysujWszystkiePociski(Graphics g, List<Bitmap> bmp)
        {
            foreach (var e in listaPociskow)
            {
                e.Rysuj(g, bmp, e.idKlatka);
                e.LicznikKlatek();
                e.Ruch();
            }
            //przeglądnij listę i usun te które zakończyły zycie
            //czyli trafiły w cel, przeszkodę lub osiągneły maksymalny zasięg
            for (int i = 0; i < listaPociskow.Count; i++)
                if (listaPociskow[i].fZniszcz)
                {
                    listaEksplozji.Add(new Eksplozja(listaPociskow[i].x,
                                                     listaPociskow[i].y, 48, 48));
                    listaPociskow.RemoveAt(i);
                }
        }
    }
}

Wskazówka:


using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace czolg_1
{
    internal class Pocisk
    {
        int[] klatki = {0,1,2,3};
        private int wys, szer;//wysokośc i szerokośc klatki świata gry
        public float x, y;//pozycja we współrzednych ekranowych
        private float x0, y0;//pozycja współrzednych wystrzału
        public int idKlatka = 0;
        public bool fZniszcz = false;
        //opóźnienie
        private const byte GRANICA_OPOZNIENIE = 3;
        private byte licznikOpoznienia = 0;
        private byte kierunek;
        private float v = 10.0f;//predkośc pocisku
        private float zasieg = 0;//zasięg pocisku
        //
        public Czolg wlasciciel;
        
        //konstruktor
        public Pocisk(float _x, float _y, int _wys, 
		              int _szer, byte _kierunek, Czolg _w)
        {
            //przesuniecie dla konca lufy
            float dx = 0, dy = 0;
            wys = _wys;
            szer = _szer;
            //kierunek strzalu
            kierunek = _kierunek;
            switch (kierunek)
            {
                //Północ
                case 0:dy = -24.0f;break;
                //Wschód
                case 1:dx = 24.0f; break;
                //Polódnie
                case 2:dy = 24.0f;break;
                //Zachód
                case 3:dx = -24.0f;break;
            }
            //zapamietaj współrzędne strzalu
            x = _x + dx;
            y = _y + dy;
            x0 = _x;
            y0 = _y;
            
            //ustaw zasięg na 6 kafli świata
            zasieg = 6 * 48;
            wlasciciel = _w;
        }

        public void Rysuj(Graphics g, List<Bitmap> bmp, int id)
        {
            g.DrawImage(bmp[klatki[id]],
                        new Rectangle((int)x - szer / 2, (int)y - wys / 2, szer, wys),
                        new Rectangle(0, 0, szer, wys),
                        GraphicsUnit.Pixel);
        }
        public void Ruch()
        {
            //kierunek pólnoc
            switch (kierunek)
            {
                //na Pólnoc
                case 0:y -= v;break;
                //na Wschod
                case 1:x += v;break;
                //na Południe
                case 2: y += v; break;
                //na Zachód
                case 3: x -= v; break;
            }
            if (Math.Abs(x - x0) > zasieg || Math.Abs(y - y0) > zasieg)
                fZniszcz = true;
        }
        public void LicznikKlatek()
        {
            licznikOpoznienia++;
            if (licznikOpoznienia > GRANICA_OPOZNIENIE)
            {
                idKlatka++;
                licznikOpoznienia = 0;
            }
            if (idKlatka > 3)
            {
                idKlatka = 0;
                //flagę zniszcenia trzeba wywołac gdy trafiono w cel
                //przeszkodę lub osiągnieto maksymalny zasięg
                //fZniszcz = true;
            }
        }
    }
}

Wskazówka:


using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace czolg_1
{
    internal class Czolg
    {
        int[] klatki = new int[4];
        static float maxV = 2.0f;
        private int idKlatka;
        private int wiersz,kolumna;//pozycja w klatkach swiata gry
        private int wys, szer;//wysokośc i szerokośc klatki świata gry
        private float x, y;//pozycja we współrzednych ekranowych
        public float v = maxV;//predkosc
        public bool fRuch = false;
        
        
        
        public int Wiersz
        {//zwroc,ustaw pozycje w klatkach swiata gry 
            get { return wiersz; }
            set { wiersz = value; }
        }
        public int Kolumna
        {//zwroc,ustaw pozycje w klatkach swiata gry 
            get { return kolumna; }
            set { kolumna = value; }
        }
        //funkcja odczytu max predkosci
        public float Vmax { get { return maxV; } }
        //zwróć wpsółrzędne obiektu ze świata gry
        public float X { get { return x; } }
        public float Y { get { return y; } }
        //zwróc klatke kierunku ruchu, potrzebne do strzelania
        public byte IdKlatka { get { return (byte)idKlatka; } }
        //konstruktor
        public Czolg(int _idKlatka,int _wiersz,int _kolumna,int _wys,int _szer)
        {
            idKlatka = _idKlatka;
            Wiersz = _wiersz;
            Kolumna = _kolumna; 
            wys =_wys;
            szer = _szer;
            //oblicz współrzedne startu
            x = Kolumna * szer + szer/2;
            y = Wiersz * wys + wys/2;
        }

        public void LadujKlatkiKierunku(int _N,int _E,int _S,int _W)
        {
            klatki[0] = _N;//na północ
            klatki[1] = _E;//na wschód
            klatki[2] = _S;//na południe
            klatki[3] = _W;//na zachód
        }

        public void Rysuj(Graphics g, List<Bitmap> bmp)
        {
            g.DrawImage(bmp[klatki[idKlatka]],
                        new Rectangle((int)x-szer/2, (int)y-wys/2, szer, wys),
                        new Rectangle(0, 0, szer, wys),
                        GraphicsUnit.Pixel);
        }

        public void Obracaj(int i)
        {
            idKlatka += i;
            if (idKlatka < 0) idKlatka = 3;
            if (idKlatka > 3) idKlatka = 0;
        }
        public void Ruch()
        {
            if (!fRuch) return;
            switch (idKlatka){
                //na północ
                case 0:y -= v;break;
                //na wschód
                case 1: x += v; break;
                //na południe
                case 2: y += v; break;
                //na zachód
                case 3: x -= v; break;
            }
        }
        
    }
}

Wskazówka:


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,
                      zasobyBmpPocisk;
        //lista klatek wycinanych bitmapek
        private List<Bitmap> bitmaps = new List<Bitmap>();
        private List<Bitmap> bitmapsPocisk = 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(Swiat swiat,Czolg czolgA,Czolg czolgB)
        {
            swiat.RysujSwiat(gEkranGry, bitmaps,W,H);
            //na teraz testowe rysowania czolgu graczaA
            czolgA.Rysuj(gEkranGry, bitmaps);
            //na teraz testowe rysowania czolgu graczaB
            czolgB.Rysuj(gEkranGry, bitmaps);
            //rysuj pociski
            swiat.RysujWszystkiePociski(gEkranGry, bitmapsPocisk);
            //ryuj eksplozje na koncu, tak jak na ostatniej warstwie
            //która przykrywa wszystkie obiekty graficzne
            swiat.RysujWszystkieEksplozje(gEkranGry, bitmaps);
        }

        public void PokazEkranGry(IntPtr uchwyt, Swiat swiat, 
		                          Czolg czolgA, Czolg czolgB) 
        {
            Graphics g = Graphics.FromHwnd(uchwyt);
            RysujSwiatGryBufor(swiat, czolgA, czolgB);
            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 WczytajBmpPocisku(string plik,int _w,int _h) 
        {
            //funkcja wczytuje bitmapę pocisku z pliku graficznego
            zasobyBmpPocisk = new Bitmap(plik);
            //wczytany plik zasobów potnij na klatki o wymiarach _w x _h
            //w programie przyjałem 8 x 8 piksele
            int ileKolumn = zasobyBmpPocisk.Width / _w;
            int ileWierszy = zasobyBmpPocisk.Height / _h;
            for (int i = 0; i < ileWierszy; i++)
                for (int j = 0; j < ileKolumn; j++)
                {
                    bitmapsPocisk.Add(new Bitmap(_w,_h,
					                             System.Drawing.Imaging.PixelFormat.Format32bppArgb)
									);
                    Graphics g = Graphics.FromImage(bitmapsPocisk[bitmapsPocisk.Count - 1]);
                    g.DrawImage(zasobyBmpPocisk, 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();
            zasobyBmpPocisk.Dispose();
            foreach (Bitmap b in bitmaps) { b.Dispose(); }
            //zwolnij bufor rysowania ekranu gry
            bmpBuforEkranGry.Dispose();
            gEkranGry.Dispose();
        }
    }
}

Wskazówka:



using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace czolg_1
{
    internal class Eksplozja
    {
        int[] klatki = {24,25,26,27,28,29,30};
        private int wys, szer;//wysokośc i szerokośc klatki świata gry
        private float x, y;//pozycja we współrzednych ekranowych
        public int idKlatka = 0;
        public bool fZniszcz = false;
        //opóźnienie
        private const byte GRANICA_OPOZNIENIE = 5;
        private byte licznikOpoznienia = 0;

        //konstruktor
        public Eksplozja(float _x,float _y, int _wys, int _szer)
        {
            wys = _wys;
            szer = _szer;
            //zapamietaj współrzędne wybuchu
            x = _x;
            y = _y;
        }

        public void Rysuj(Graphics g, List<Bitmap> bmp, int id)
        {
            g.DrawImage(bmp[klatki[id]],
                        new Rectangle((int)x - szer / 2, (int)y - wys / 2, szer, wys),
                        new Rectangle(0, 0, szer, wys),
                        GraphicsUnit.Pixel);
        }

        public void LicznikKlatek()
        {
            licznikOpoznienia++;
            if (licznikOpoznienia > GRANICA_OPOZNIENIE)
            {
                idKlatka++;
                licznikOpoznienia = 0;
            }
            if (idKlatka > 6)
            {
                idKlatka = 0;
                fZniszcz = true;
            }
        }
    }
}
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