Obiekty graficzne na przykładzie prostej gry- animacja poklatkowa

Animacja poklatkowa polega na wyświetlaniu kolejnych statycznych gotowych obrazów (klatek animacji) sprawiających wrażenie ruchu. W tej części stworzymy model animacji poklatkowej eksplozji pocisku. Rozwiązanie umieścimy w osobnej klasie o nazwie Eksplozja.

Do tej części bedziemy potrzebować kodu z poprzedniej części (pobierz pełny kod z poprzeniej cześci)

Klatki animacji wybuchu

Kolejne obrazy wybuchu umieścimy w już istniejących zasobach graficznych. Dla ułatwienia i ujednolicenia kodowania klatki animacji będą mieć ten sam rozmiar co pozostałe, czyli 48 x 48 pikseli

Mój zasób graficzny wygląda jak poniżej

zasób graficzny gry 2D czołg. Visual studio C#

Indeksy klatek animacji wybuchu to 24,25? 30. Pokazuje to poniższa ilustracja

indeksy klatek animacji eksplozji. Visual studio C#

Te indeksy uwzględnimy w tablicy klatek animacji tworzonej klasy Eksplozja

Klasa Eksplozja

W projekcie inicjujemy klasę Eksplozja

Klasa Eksplozja. Visual studio C#

Obsługę grafiki zrobimy na tej samej zasadzie co obsługę grafiki czołgu w klasie Czolg. Indeksy kolejnych klatek przechowamy tablicy jednowymiarowej. Wstępna zawartość pliku tworzonej klasy ma postać jak poniżej

Wskazówka:


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
    }
}

Konstruktor klasy Eksplozja

Konstruktor tworzonej klasy Eksplozja będzie bardzo prosty. Do konstruktora przekażemy współrzędne wybuchu oraz rozmiar klatki animacji.

Kod konstruktora

Wskazówka:


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

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

Funkcja rysująca klatkę wybuchu

Rysowanie wybranej klatki wybuchu zrobimy tak samo jak rysowanie klatki czołgu. Możemy skopiować odpowiedni fragment kodu z klasy Czolg. Dodamy jedynie argument indeksu zmienianej klatki animacji

Wskazówka:


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);
}

Test eksplozji

Docelowo eksplozja powinna pojawić się w grze przy trafieniu pociskiem w cel. Na tym etapie tworzenia naszej prostej gry 2D sprawdzimy testowo jak jest inicjowana w grze animacja wybuchu. Eksplozje będziemy wywoływać przez wciśnięcie klawisza t. W tym celu w głównym kodzie utworzymy funkcje o nazwie TestEksplozji

Przyjęty model rysowania świata jest podobny do warstw, pomimo ze wykorzystywana jest jedna warstwa. Efekt warstw uzyskamy kolejnością rysowanych obiektów. W pierwszej kolejności rysowane jest tło, potem obiekty graczy, pociski, a na końcu eksplozje.

Eksplozji może być w jednym czasie kilka i trzeba je wszystkie obsłużyć. W tym celu użyjemy listy, która jest najwygodniejszym rozwiązaniem ze względu na jej elastyczność rozmiaru. W klasie Swiat dodajemy listę oraz funkcję, która obsłuży rysowanie eksplozji

Wskazówka:


internal class Swiat
{
	static int ileKlatekX = 20, ileKlatekY = 15;
	int[,] plansza=new int[ileKlatekY,ileKlatekX];
	public List<Eksplozja> listaEksplozji = new List<Eksplozja>(); 

	public int IleKlatekX
	{   //zwróc ilośc kolumn

Funkcja przeglądająca listę aktywnych eksplozji pod względem rysowania ma postać

Wskazówka:


public void RysujWszystkieEksplozje(Graphics g,List<Bitmap> bmp)
{
	foreach (var e in listaEksplozji)
	{
		e.Rysuj(g, bmp, 0);
	}
}

Wywołanie rysowania eksplozji wykonujemy w klasie bitmapy na końcu wszystkich rysowań. Przechodzimy do pliku bitmapy.cs i wprowadzamy zmiany w funkcji RysujSwiatGryBufor

Wskazówka:


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 eksplozje na koncu, tak jak na ostatniej warstwie
	//która przykrywa wszystkie obiekty graficzne
	swiat.RysujWszystkieEksplozje(gEkranGry, bitmaps);
}		

Testowanie eksplozji wykonamy w głównej klasie gry, przechodzimy do pliku Form1.cs i dodajemy poniższy kod

Wskazówka:


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));
}

Testowanie eksplozji wykonamy przez wciśnięcie klawisza t. Przechodzimy do funkcji Form1_KeyDown i dopisujemy ostatni linijki tego kodu

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;
	}
	//testowe eksplozje
	if (e.KeyCode == Keys.T)
	{
		TestEksplozji();
	}
}

Teraz skompiluj program i sprawdź efekt działania. Prawidłowo wykonane czynności prowadzą do uzyskania losowo wywołanych eksplozji. Na tą chwilę będzie to pierwsza klatka wybuchu

test anmicaji Eksplozja. Visual studio C#

Animacja eksplozji

Aby animować eksplozję musimy obsłużyć iterację klatek animacji po każdym cyklu przebiegu zegara gry. Dodatkowo trzeba pamiętać, że gdy licznik klatek przekroczy górną granicę należy zniszczyć bieżący obiekt eksplozji.

Modyfikujemy klasę Eksplozja. W pliku Eksplozja.cs wprowadzamy te zmiany

Wskazówka:


    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;

Dopisujemy funkcję licznika klatek

Wskazówka:

		
public void LicznikKlatek()
{
	idKlatka++;
	if (idKlatka > 6)
	{
		idKlatka = 0;
		fZniszcz = true;
	}
}		

Przechodzimy do pliku Swiat.cs i modyfikujemy funkcję odpowiedzialna za rysowani eksplozji

Wskazówka:


public void RysujWszystkieEksplozje(Graphics g,List<Bitmap> bmp)
{
	foreach (var e in listaEksplozji)
	{
		e.Rysuj(g, bmp, e.idKlatka);
		e.LicznikKlatek();
	}
}

Skompiluj program i sprawdź efekt działania. Teraz po każdorazowym wciśnięciu klawisza t pojawia się nowa eksplozja, która cyklicznie zmienia klatki animacji ale nigdy się nie kończy

animicaja wybuchu eksplozja. Visual studio C#

Zakończenie eksplozji

Czas życia eksplozji trwa do momentu narysowania ostatniej klatki. W tym przykładzie klatek jest 7. Licząc od indeksu 0, koniec będzie gdy licznik osiągnie wartość większą niż 6. Do klasy Eksplozja dodajemy zmienną logiczna o nazwie fZniszcz

Wskazówka:


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;

Modyfikujemy również funkcję LicznikKlatek

Wskazówka:

	
public void LicznikKlatek()
{
	idKlatka++;
	if (idKlatka > 6)
	{
		idKlatka = 0;
		fZniszcz = true;
	}
}	

Przeglądanie listy eksplozji w celu wyszukania tych eksplozji, które zakończyły animację wymaga zmodyfikowania funkcji RysujWszystkieEksplozje zapisanej w klasie Swiat. Przechodzimy do pliku Swiat.cs i wprowadzamy modyfikacje wspomnianej funkcji

Wskazówka:


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);
}	

Teraz możemy skompilować i uruchomić nasz program. Prawidłowo działająca aplikacja po wciśnięciu klawisza t będzie tworzyć w losowym miejscu wybuch, który będzie usuwany po skończeniu animacji

koniec animacji wybuchu eksplozji. Visual studio C#

Spowalniamy animację eksplozji

Animacja wybuchu działa dość szybko. Wywołanie poszczególnych klatek możemy spowolnić wprowadzając licznik opóźnień. Wartość licznika będzie się zwiększać przy każdorazowym obiegu zegara gry, i gdy osiągnie ustaloną wartość to zwiększy klatkę animacji, a sam się wyzeruje. Górną granice licznika opóźnień należy dobrać do własnych wrażeń wzrokowych i mocy używanego komputera. Ja w kodzie programu granicę ustaliłem równą 5.

Wprowadzamy zmiany w pliku Eksplozja.cs

Wskazówka:


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;

Modyfikujemy funkcję licznika klatek animacji eksplozji

Wskazówka:


public void LicznikKlatek()
{
	licznikOpoznienia++;
	if (licznikOpoznienia > GRANICA_OPOZNIENIE)
	{
		idKlatka++;
		licznikOpoznienia = 0;
	}
	if (idKlatka > 6)
	{
		idKlatka = 0;
		fZniszcz = true;
	}
}

Skompiluj program i sprawdź efekt działania. Prawidłowo działający program po każdorazowym wciśnięciu klawisza t wywoła eksplozje animowaną dużo wolniej niż poprzednio

gra czołg 2D część 5, Visual studio C#

W tej części to tyle. Poniżej pełne kody poszczególnych klas

Zawartośc pliku Eksplozja.cs

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;
            }
        }
    }
}

Zawartość pliku Swiat.cs

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 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);
        }
    }
}

Zawartość pliku bitmapy.cs

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;
        //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(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);
            //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 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();
        }
    }
}

Zawartość pliku Form1.cs

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));
        }

        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");
            
        }

        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;
            }
            //testowe eksplozje
            if (e.KeyCode == Keys.T)
            {
                TestEksplozji();
            }
        }
        
        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);
        }
    }
}

Zawartość pliku Czolg.cs

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; } }
        //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;
            }
        }
        
    }
}
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