Obiekty graficzne na przykładzie prostej gry- kolizje

Do ósmej części będziemy potrzebować kod z części siódmej, który można pobrać z tego linku kod części 7

W tej części zajmiemy się kolizjami. Co będziemy rozumieć przez kolizję w naszym rozwiązaniu? Przez kolizję będziemy rozumieć moment wejścia graficznego obrysu obiektu poruszającego się na planszy gry w obszar innego obiektu graficznego utworzonego świata gry.

Nie wszystkie obiekty graficzne widoczne w świecie gry muszą wykrywać kolizje. Na pewno kolizja musi być wykrywana dla pocisku w przypadku uderzenia w cel lub przeszkodę. Kolizja powinna być również wykrywana przy ruchu obiektów graczy. W naszym przypadku poruszające się czołgi do tej pory mogę przenikać przez siebie jaki i przejeżdżać przez przeszkody. W tej części postaramy się to rozwiązać.

Wykrywanie kolizji z przeszkodą

W przyjętym rozwiązaniu w indeksach graficznych bitmap kostek świata pod numerem 16 jest trawa- grunt po którym można jeździć, a pod numerem 17 jest obraz bitmapy przeszkody (skrzynki) przez którą ni powinno się dać przejechać. Patrz poniższy rysunek

zasoby gry 2d. Visual Studio C#

Aby zapewnić wykrywalność kolizji z przeszkodą należy odczytać czy w przewidzianej ruchem kolejnej klatce siatki mapy gry nie ma przeszkody. W pseudokodzie możemy zapisać taką instrukcję jako warunek przedstawiony poniższym schematem blokowym

wykrywanie kolizji w grze 2d. Visual Studio C#

Przechodzimy do pliku Czolg.cs i dodamy funkcję ograniczającą ruch do dozwolonych pól. W funkcji dodamy sprawdzenie czy gracz nie wyjechał poza planszę

Wskazówka:


private bool CzyMoznaZrobicRuch(int[,] plansza,int ileKlatekX,int ileKlatekY)
	{
		//tymczasowe współrzędne położenia
		float _x = x,
			  _y = y;
		//sprawdzaj możliwośc ruchu z dokładnością
		//do osmiokrotnego przesunięcia pozycji
		//wartośc nalezy dopasowac do obrysu duszka względem
		//pełengo rozmiaru klatki
		switch (idKlatka)
		{
			//na północ
			case 0: _y -= 8*v; break;
			//na wschód
			case 1: _x += 8*v; break;
			//na południe
			case 2: _y += 8*v; break;
			//na zachód
			case 3: _x -= 8*v; break;
		}
		int wiersz = (int)(_y / wys),
			kolumna= (int)(_x/szer);
		//nie wyjeżdzja poza planszę
		if (kolumna < 0 ||
			kolumna > ileKlatekX-1 ||
			wiersz < 0 ||
			wiersz > ileKlatekY-1) return false; 
		//klatka o indeksie 16 jest trawą-gruntem
		//po którym można jeździć, po pozostałych nie można
		if (plansza[wiersz,kolumna]!=16)return false;
		return true;
	}

Wywołanie funkcji zrobimy we wcześniej napisanej metodzie Ruch, którą zmodyfikujemy o podanie strzech argumentów: planszy świata gry oraz ilości kolumn i wierszy

Wskazówka:


public void Ruch(int[,] plansza, int ileKlatekX, int ileKlatekY)
{
	if (!fRuch||!CzyMoznaZrobicRuch(plansza, ileKlatekX, ileKlatekY)) 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;
	}
}

Przechodzimy do pliku Form1.cs i dokonujmy poprawek w ciele funkcji zegarGry. Patrz poniżej

Wskazówka:


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

Skompiluj program i sprawdź efekt działania. Obiekty nie powinny przejeżdżać przez przeszkody

kolizja w grze 2d. Visual Studio C#

Kolizja z przeciwnikiem

Wykrycie kolizji z przeciwnikiem w najprostszym rozwiązaniu może polegać na sprawdzeniu czy klatka pola obszaru świata gry jest pusta czy zajmują ją przeciwnik. Oznacza to, że wystarczy sprawdzić czy kolumna i wiersz jednego gracza i drugiego będą jednakowe w przewidywanym ruchu. Jeżeli będą jednakowe to występuje kolizja.

Modyfikujemy wcześniej napisaną funkcję CzyMoznaZrobicRuch. Dodamy czwarty argument- czołg przeciwnika. W ciele funkcji wykonamy test sprawdzający kolumny i wiersze dla obu graczy

Wskazówka:


private bool CzyMoznaZrobicRuch(int[,] plansza,int ileKlatekX,int ileKlatekY,Czolg B)
	{
		//tymczasowe współrzędne położenia
		float _x = x,
			  _y = y;
		//sprawdzaj możliwośc ruchu z dokładnością
		//do osmiokrotnego przesunięcia pozycji
		//wartośc nalezy dopasowac do obrysu duszka względem
		//pełengo rozmiaru klatki
		switch (idKlatka)
		{
			//na północ
			case 0: _y -= 8*v; break;
			//na wschód
			case 1: _x += 8*v; break;
			//na południe
			case 2: _y += 8*v; break;
			//na zachód
			case 3: _x -= 8*v; break;
		}
		int wiersz = (int)(_y / wys),
			kolumna= (int)(_x/szer);
		//nie wyjeżdzja poza planszę
		if (kolumna < 0 ||
			kolumna > ileKlatekX-1 ||
			wiersz < 0 ||
			wiersz > ileKlatekY-1) return false;
		//czy kolizja z czolgiem przeciwnika
		int wierszB = (int)(B.Y / wys),
			kolumnaB = (int)(B.X / szer);
		if(wiersz==wierszB && kolumna==kolumnaB) return false;
		//klatka o indeksie 16 jest trawą-gruntem
		//po którym można jeździć, po pozostałych nie można
		if (plansza[wiersz,kolumna]!=16)return false;
		return true;
	}

Dodanie czwartego argument wymaga modyfikacji w kolejnej funkcji. Jest to funkcja Ruch

Wskazówka:


public void Ruch(int[,] plansza, int ileKlatekX, int ileKlatekY,Czolg B)
	{
		if (!fRuch||
			!CzyMoznaZrobicRuch(plansza, ileKlatekX, ileKlatekY,B))
			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;
		}
	}
Przechodzimy do pliku Form1.cs i wykonujemy kolejną modyfikację

Wskazówka:


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

Teraz kompilujemy program i sprawdzamy efekt działania. Prawidłowo wprowadzone modyfikacje zablokują możliwość przenikania obiektów graczy- czołgi nie wjeżdżają na siebie

kolizja w grze 2d. Visual Studio C#

Kolizja pocisku z przeszkodą i czołgiem przeciwnika

Problem wykrywani kolizji przez pocisk podzielimy na dwa podproblemy:

Kolizje z przeszkodą rozwiążemy w ten sam sposób co kolizję czołgu z przeszkodą z tą różnicą, że nie będziemy blokować ruchu tylko powodować eksplozję.

Przechodzimy do pliku Pocisk.cs i piszemy kod funkcji wykrywającej kolizję. Jej argument to: plansza świata gry oraz ilość klatek w poziomie i pionie w przyjętym świecie gry. Patrz poniżej

Wskazówka:


private bool CzyKolizja(int[,] plansza, int ileKlatekX, int ileKlatekY)
{
	//dzielimy przez 48 bo taka jest szerokośc
	//i wysokośc pola świata gry
	int wiersz = (int)(y / 48),
		kolumna = (int)(x / 48);
	//eksplozaj na grnicach swiata
	if (kolumna < 0 || wiersz < 0 ||
		kolumna >= ileKlatekX ||
		wiersz >= ileKlatekY) return true;
	//klatka o indeksie 16 jest trawą-gruntem
	//po którym można jeździć, po pozostałych nie można
	if (plansza[wiersz, kolumna] != 16) return true;
	return false;
}

Wywołanie tej funkcji robimy w warunku zniszczenia pocisku w funkcji Ruch, w której nagłówku dopisujemy trzy argumenty: int[,] plansza, int ileKlatekX, int ileKlatekY

Wskazówka:


public void Ruch(int[,] plansza, int ileKlatekX, int ileKlatekY)
{
	//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 (CzyKolizja(plansza, ileKlatekX, ileKlatekY)||
		Math.Abs(x - x0) > zasieg ||
		Math.Abs(y - y0) > zasieg)
		fZniszcz = true;
}

Wprowadzone zmiany w nagłówku funkcji Ruch wymagają modyfikacje w pliku Swiat.cs. Przechodzimy do tego pliku i uaktualniamy kod do postaci. Zmiana jest w jednej linijce. Było e.Ruch(), zmieniamy na e.Ruch(plansza, ileKlatekX, ileKlatekY);. Patrz poniżej

Wskazówka:


public void RysujWszystkiePociski(Graphics g, List<Bitmap> bmp)
{
	foreach (var e in listaPociskow)
	{
		e.Rysuj(g, bmp, e.idKlatka);
		e.LicznikKlatek();
		e.Ruch(plansza, ileKlatekX, ileKlatekY);
	}
	//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);
		}
}

Kompilujemy program i sprawdzamy efekt działania. Przy prawidłowo wprowadzonych zmianach zauważysz eksplozje gdy pocisk trafi w przeszkodę

kolizja pocisku w grze 2d. Visual Studio C#

Kolizja z czołgiem przeciwnika

Kolizje z czołgiem przeciwnika wykryjemy na zasadzie porównania współrzędnych pocisku i czołgu przeciwnika. Porównywać będziemy pewien obszar wokół współrzędnych czołgu przeciwnika.

Jeżeli otworzymy plik zasobów graficznych, to po zmierzeniu obrysu rysunku czołgu otrzymujemy, że obrys ma wymiary 32 x 32 piksele

obszar kolizji pocisku w grze 2d. Visual Studio C#

Obszar do wykrywania kolizji z czołgiem będzie prostokątem o bokach 32 x 32 piksele rozciągniętym wokół punktu zaczepienia. Punktem zaczepienia są współrzędne czołgu przeciwnika. Przechodzimy do nagłówka klasy Pocisk i dodajemy zmienną związaną z czołgiem przeciwnika

Wskazówka:


    internal class Pocisk
    {
        //
        public Czolg wlasciciel,przeciwnik;

Zmieniamy konstruktora klasy Pocisk

Wskazówka:


//konstruktor
public Pocisk(float _x, float _y, int _wys, 
			  int _szer, byte _kierunek, 
			  Czolg _w,Czolg _p)
{
	//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;
	przeciwnik = _p;
}

Modyfikujemy wcześniej napisaną funkcję kolizji pocisku z przeszkodą.

Wskazówka:


private bool CzyKolizja(int[,] plansza, int ileKlatekX, int ileKlatekY)
{
	//kolizja z czołgiem przeciwnika
	if(x > przeciwnik.X - 16 &&
	   x < przeciwnik.X + 16 &&
	   y > przeciwnik.Y - 16 &&
	   y < przeciwnik.Y + 16) return true;
	//dzielimy przez 48 bo taka jest szerokośc
	//i wysokośc pola świata gry
	int wiersz = (int)(y / 48),
		kolumna = (int)(x / 48);
	//eksplozaj na grnicach swiata
	if (kolumna < 0 || wiersz < 0 ||
		kolumna >= ileKlatekX ||
		wiersz >= ileKlatekY) return true;
	//klatka o indeksie 16 jest trawą-gruntem
	//po którym można jeździć, po pozostałych nie można
	if (plansza[wiersz, kolumna] != 16) return true;
	return false;
}

Wprowadzone zmiany w klasie Pocisk wymuszają zmiany w innych plikach. Przechodzimy do pliku Form1.cs. Modyfikujemy poniżesz funkcje tak jak poniżej

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

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

Oraz ostatnie linijki funkcji Form1_KeyDown

Wskazówka:


//strzela czołg A
if (e.KeyCode == Keys.Space)
{
	Strzal(czolgA, czolgB);
}
//strzela czołg B
if (e.KeyCode==Keys.Enter)
{
	Strzal(czolgB, czolgA);
}

Kompilujemy program i sprawdzamy efekt działania. Prawidłowo działający program pozwala trafić w czołg przeciwnika

piszemy grę 2d w Visual Studio C#

I to byłoby na tyle w tej części cyklu lekcji związanych z klasami, obiektami i funkcjami graficznymi. Można dalej projekt rozwijać, na przykład mając już podane do pocisku zmienne typu Czolg właściciela pocisku i czołg przeciwnika łatwo dodać jest system naliczania punktów, tracenia punktów życia. Można pokusić się o zdobywanie zasobów typu amunicja, punkty życia itp. Model prostego silnika gry 2d został tu przedstawiony.

Poniżej pełne kody plików użytych klas

Wskazówka:


using czolg_1;
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,czolgB));
        }

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

        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;
                //czolgA.fRuch = czolgA.CzyMoznaZrobicRuch(swiat.plansza);
            }
            if (e.KeyCode == Keys.S)
            {
                czolgA.fRuch = true;
                czolgA.v = -czolgA.Vmax;
                //czolgA.fRuch = czolgA.CzyMoznaZrobicRuch(swiat.plansza);
            }
            //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, czolgB);
            }
            //strzela czołg B
            if (e.KeyCode==Keys.Enter)
            {
                Strzal(czolgB, czolgA);
            }
        }
        
        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(swiat.plansza,swiat.IleKlatekX,swiat.IleKlatekY,czolgB);
            czolgB.Ruch(swiat.plansza,swiat.IleKlatekX,swiat.IleKlatekY,czolgA);
            bmp.PokazEkranGry(this.Handle, swiat, czolgA, czolgB);
        }
    }
}

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;
        }
        private bool CzyMoznaZrobicRuch(int[,] plansza,int ileKlatekX,
		                                int ileKlatekY,Czolg B)
        {
            //tymczasowe współrzędne położenia
            float _x = x,
                  _y = y;
            //sprawdzaj możliwośc ruchu z dokładnością
            //do osmiokrotnego przesunięcia pozycji
            //wartośc nalezy dopasowac do obrysu duszka względem
            //pełengo rozmiaru klatki
            switch (idKlatka)
            {
                //na północ
                case 0: _y -= 8*v; break;
                //na wschód
                case 1: _x += 8*v; break;
                //na południe
                case 2: _y += 8*v; break;
                //na zachód
                case 3: _x -= 8*v; break;
            }
            int wiersz = (int)(_y / wys),
                kolumna= (int)(_x/szer);
            //nie wyjeżdzja poza planszę
            if (kolumna < 0 ||
                kolumna > ileKlatekX-1 ||
                wiersz < 0 ||
                wiersz > ileKlatekY-1) return false;
            //czy kolizja z czolgiem przeciwnika
            int wierszB = (int)(B.Y / wys),
                kolumnaB = (int)(B.X / szer);
            if(wiersz==wierszB && kolumna==kolumnaB) return false;
            //klatka o indeksie 16 jest trawą-gruntem
            //po którym można jeździć, po pozostałych nie można
            if (plansza[wiersz,kolumna]!=16)return false;
            return true;
        }
        
        public void Ruch(int[,] plansza, int ileKlatekX, 
		                        int ileKlatekY,Czolg B)
        {
            if (!fRuch||
                !CzyMoznaZrobicRuch(plansza, ileKlatekX, ileKlatekY,B))
                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.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 pocisku
        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,przeciwnik;
        
        //konstruktor
        public Pocisk(float _x, float _y, int _wys, 
                      int _szer, byte _kierunek, 
                      Czolg _w,Czolg _p)
        {
            //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;
            przeciwnik = _p;
        }

        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);
        }
        private bool CzyKolizja(int[,] plansza, int ileKlatekX, int ileKlatekY)
        {
            //kolizja z czołgiem przeciwnika
            if(x > przeciwnik.X - 16 &&
               x < przeciwnik.X + 16 &&
               y > przeciwnik.Y - 16 &&
               y < przeciwnik.Y + 16) return true;
            //dzielimy przez 48 bo taka jest szerokośc
            //i wysokośc pola świata gry
            int wiersz = (int)(y / 48),
                kolumna = (int)(x / 48);
            //eksplozaj na grnicach swiata
            if (kolumna < 0 || wiersz < 0 ||
                kolumna >= ileKlatekX ||
                wiersz >= ileKlatekY) return true;
            //klatka o indeksie 16 jest trawą-gruntem
            //po którym można jeździć, po pozostałych nie można
            if (plansza[wiersz, kolumna] != 16) return true;
            return false;
        }
        public void Ruch(int[,] plansza, int ileKlatekX, 
                         int ileKlatekY)
        {
            //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 (CzyKolizja(plansza, ileKlatekX, 
                           ileKlatekY) ||
                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.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;
        public int[,] plansza = new int[ileKlatekY, ileKlatekX];
        public List<Eksplozja> listaEksplozji = new List<Eksplozja>();
        public List<Pocisk> listaPociskow = new List<Pocisk>();

        /*
        public int[,] Plansza
        {
            get{return plansza;}
        }
        */
       

        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(plansza, ileKlatekX, ileKlatekY);
            }
            //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 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