Dzisiaj pokażę wam jak stworzyć, bardzo dobrze wam znaną grę w piłkarzyki. Na pewno każdy z was w momencie nudy na lekcjach skorzystał z okazji pogrania w taką prostą i sympatyczną grę.
Gra do pobrania tutaj: Pilkarzyki 1.2
Prezentacja gry i kodu źródłowego:
My zrobimy sobie trochę okrojoną wersję, ale najważniejsze elementy gry zostaną zachowane. Forma do gry nie będzie miała za dużo elementów, gdyż będą one generowane automatycznie w czasie trwania gry:
Rodzaj elementu | Nazwa elementu | ustawienia |
---|---|---|
Form | Form1 | Name: Form1 Text: Piłkarzyki VisualMonsters.pl Size: 654; 468 |
Panel | Panel1 | Name: Panel1 Size: 613; 407 Location: 12; 12 BackColor: SeaGreen Anchor: Top, Bottom, Left, Right |
PictureBox | Panel_glowny | Name: Panel_glowny Size: 100; 50 Location: 58; 0 BackColor: SeaGreen |
Tak jak pisałem wcześniej, wszelkie elementy będą generowane na bieżąco, więc wystarczą nam tylko trzy elementy. Głównym elementem będzie PictureBox, to na nim będzie odbywała się rozgrywka, a panel pod nim to tylko estetyczne tło.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
Public Class Form1 'wielkość planszy Dim wysokosc As Integer = 7 Dim szerokosc As Integer = 11 'zmienna przechowuje aktualnie wybrany panel Dim panelAktualnieWybrany As Panel 'zmienna przechowująca grafikę gry Dim BitmapaBoiska As Bitmap Dim GrafikaBoiska As Graphics Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load 'ustawiamy wielkość i pozycję pola do gry Panel_glowny.Size = New Size(szerokosc * 49 + 50, wysokosc * 49 + 50) Panel_glowny.Location = New Point((Panel1.Width - Panel_glowny.Width) / 2, (Panel1.Height - Panel_glowny.Height) / 2) 'tworzymy bitmapę i generujemy na niej grafikę BitmapaBoiska = New Bitmap(Panel_glowny.Width, Panel_glowny.Height) GrafikaBoiska = Graphics.FromImage(BitmapaBoiska) 'generuje grafikę boiska '' generujLinieBoiska() 'generuje aktywne elementy gry (punkty na planszy do klikania '' generujPaneleGlowne() 'wybiera środek planszy jako punkty startowy '' panelAktualnieWybrany = listaLokalizacji(Math.Ceiling((szerokosc - 1) / 2))(Math.Ceiling((wysokosc) / 2)) 'uaktywnia punkty do których możemy przejść/podać piłkę '' odblokujPanele(panelAktualnieWybrany) 'dodaje panele bramek '' DodajBramki() Dim start As New System.Drawing.SolidBrush(System.Drawing.Color.Black) ' kolor punktu startowego '' GrafikaBoiska.FillEllipse(start, New Rectangle(panelAktualnieWybrany.Location.X + 5, panelAktualnieWybrany.Location.Y + 5, 10, 10)) 'dodaje grafikę punktu startowego 'nadaje tło naszemu pictureboxowi (naszą grafikę) Panel_glowny.Image = BitmapaBoiska End Sub End Class |
Kod jest chyba zrozumiały, najpierw ustawiamy wielkość PictureBoxa i jego pozycję, aby była na środku, następnie generujemy grafikę i elementy klikalne na planszy. Zaczniemy od generowania siatki boiska, odblokuj element:
1 2 |
'generuje grafikę boiska generujLinieBoiska() |
Elementy boiska rysować będziemy za pomocą:
1 2 |
GrafikaBoiska.DrawLine("kolor","punkt startowy","punkt końcowy") - rysuje linie GrafikaBoiska.FillEllipse("kolor", New Rectangle("położenie X", "położenie Y", "szerokość", "wysokość")) -rysuje wypełnione koło |
Kod tego elementu wygląda tak:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 |
#Region "Generuje grafikę boiska" Private Sub generujLinieBoiska() 'śreodek boiska Dim srodekBoiska_naWysokosc As Integer = Math.Floor((wysokosc) / 2) Dim srodekBoiska_naSzerokosc As Integer = Math.Ceiling((szerokosc) / 2) 'Linie boiska Dim CienkaLinia As Pen = New Pen(Color.FromArgb(191, 218, 229), 1) 'cienka linia Dim GrubaLinia As Pen = New Pen(Color.White, 3) 'gruba linia 'generuje tło boiska Dim KolorBoiska As New System.Drawing.SolidBrush(System.Drawing.Color.MediumSeaGreen) GrafikaBoiska.FillRectangle(KolorBoiska, New Rectangle(0, 0, BitmapaBoiska.Width, BitmapaBoiska.Height)) 'generuje linie boiska For i As Integer = 0 To szerokosc + 1 For j As Integer = 0 To wysokosc + 1 GrafikaBoiska.DrawLine(CienkaLinia, 0, i * 49, Panel_glowny.Width, i * 49) GrafikaBoiska.DrawLine(CienkaLinia, i * 49, 0, i * 49, Panel_glowny.Height) Next Next 'generuje linie autu GrafikaBoiska.DrawLine(GrubaLinia, 49, 0, Panel_glowny.Width - 49, 0) GrafikaBoiska.DrawLine(GrubaLinia, 49, (wysokosc + 1) * 49, Panel_glowny.Width - 49, (wysokosc + 1) * 49) GrafikaBoiska.DrawLine(GrubaLinia, 49, 0, 49, srodekBoiska_naWysokosc * 49) GrafikaBoiska.DrawLine(GrubaLinia, Panel_glowny.Width - 49, 0, Panel_glowny.Width - 49, srodekBoiska_naWysokosc * 49) GrafikaBoiska.DrawLine(GrubaLinia, 49, (wysokosc + 1 - srodekBoiska_naWysokosc) * 49, 49, (wysokosc + 1) * 49) GrafikaBoiska.DrawLine(GrubaLinia, Panel_glowny.Width - 49, (wysokosc + 1 - srodekBoiska_naWysokosc) * 49, Panel_glowny.Width - 49, (wysokosc + 1) * 49) GrafikaBoiska.DrawLine(GrubaLinia, 0, srodekBoiska_naWysokosc * 49, 49, srodekBoiska_naWysokosc * 49) GrafikaBoiska.DrawLine(GrubaLinia, 0, (srodekBoiska_naWysokosc + 2) * 49, 49, (srodekBoiska_naWysokosc + 2) * 49) GrafikaBoiska.DrawLine(GrubaLinia, Panel_glowny.Width - 49, srodekBoiska_naWysokosc * 49, Panel_glowny.Width, srodekBoiska_naWysokosc * 49) GrafikaBoiska.DrawLine(GrubaLinia, Panel_glowny.Width - 49, (srodekBoiska_naWysokosc + 2) * 49, Panel_glowny.Width, (srodekBoiska_naWysokosc + 2) * 49) GrafikaBoiska.DrawLine(GrubaLinia, srodekBoiska_naSzerokosc * 49, 0, srodekBoiska_naSzerokosc * 49, (wysokosc + 1) * 49) 'generuje punkty na boisu Dim pola As New System.Drawing.SolidBrush(Color.FromArgb(150, Color.White)) Dim aut As New System.Drawing.SolidBrush(System.Drawing.Color.White) For i As Integer = 1 To szerokosc For j As Integer = 0 To wysokosc + 1 If i = 1 Or i = szerokosc Or j = 0 Or j = (wysokosc + 1) Then If j = Math.Ceiling(wysokosc / 2) Then GrafikaBoiska.FillEllipse(pola, New Rectangle(i * 49 - 5, j * 49 - 5, 10, 10)) Else GrafikaBoiska.FillEllipse(aut, New Rectangle(i * 49 - 5, j * 49 - 5, 10, 10)) End If Else GrafikaBoiska.FillEllipse(pola, New Rectangle(i * 49 - 5, j * 49 - 5, 10, 10)) End If Next Next 'generuje czerwone punkty bramek z obu stron Dim bramka As New System.Drawing.SolidBrush(System.Drawing.Color.Red) GrafikaBoiska.FillEllipse(bramka, New Rectangle(0 - 5, (srodekBoiska_naWysokosc + 1) * 49 - 5, 10, 10)) GrafikaBoiska.FillEllipse(bramka, New Rectangle((szerokosc + 1) * 49 - 5, (srodekBoiska_naWysokosc + 1) * 49 - 5, 10, 10)) End Sub #End Region |
Jest to czyste rysowanie, ja to robiłem tak, że wpisywałem kod, odpalałem program i patrzyłem, gdzie co jak się pojawiało :]. Po uruchomieniu widać już pola jak na kartce w kratkę:
Teraz to już trochę przypomina piłkarzyki. Możecie poeksperymentować i pozmieniać kolory, dodać koła boiska jakieś pola karne, aby uprzyjemnić ten widok. Teraz dodamy sobie aktywne elementy planszy, aby to zrobić, musimy je trochę podzielić:
-panele przy bramkach (trzy, które umożliwiają strzał na bramkę (na żółto))
-panele autów (te na krańcach boiska (na zielono))
-punkty boiska (na czerwono)
Po dodaniu kolorów do naszych paneli ja otrzymałem taki efekt:
Do punktów boiska będą należały wszystkie panele, a panele w wybranych grupach będą miały swoje specjalne właściwości, przyda to się komuś, kto będzie chciał rozbudować grę. Kod do tego elementu prezentuje poniżej:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 |
#Region "Generuje punkty na planszy" 'przechowuje listę wszystkich pól Dim listaLokalizacji As New List(Of List(Of Panel)) 'przechowuje listę tylko autów Dim listaLokalizacjiaSpecjalnych As New List(Of Panel) 'przechowuje panele specjalne przy bramce Dim miejscaSpecjalnePrzyPramce_lewa As New List(Of Panel) Dim miejscaSpecjalnePrzyPramce_prawa As New List(Of Panel) Private Sub generujPaneleGlowne() For i As Integer = 1 To szerokosc Dim listapaneli As New List(Of Panel) For j As Integer = 0 To wysokosc + 1 Dim panelPodstawowy As New Panel With panelPodstawowy .Location = New Point(i * 49 - 10, j * 49 - 10) .Size = New Size(20, 20) .BackColor = Color.Transparent .Name = "panL_" + i.ToString + j.ToString .Cursor = Cursors.Hand '' AddHandler .Click, AddressOf pan_Click End With If i = 1 Or i = szerokosc Or j = 0 Or j = (wysokosc + 1) Then If j = Math.Ceiling(wysokosc / 2) Then listapaneli.Add(panelPodstawowy) 'dodaje pojedyńczy panel przy bramce (jasny zielony kolor na obrazku w tutorialu) Else 'dodaje panele autów (zielony kolor na obrazku w tutorialu) If (i = 1 And j = 0) Or (i = 1 And j = (wysokosc + 1)) Or (i = szerokosc And j = 0) Or (i = szerokosc And j = (wysokosc + 1)) Then panelPodstawowy.Visible = False panelPodstawowy.Enabled = False 'jeśli panel znajduje się na rogu planszy to nie dodajemy go do listy End If listaLokalizacjiaSpecjalnych.Add(panelPodstawowy) listapaneli.Add(panelPodstawowy) End If Else listapaneli.Add(panelPodstawowy) 'dodaje wszystkie panele (czerwony kolor na obrazku w tutorialu) End If 'dodaje miejsca przy bramce (żółty kolor na obrazku w tutorialu) If i = 1 Or i = szerokosc Or j = 0 Or j = (wysokosc + 1) Then If j = Math.Ceiling(wysokosc / 2) Or j = Math.Ceiling(wysokosc / 2) - 1 Or j = Math.Ceiling(wysokosc / 2) + 1 Then If i = 1 Then miejscaSpecjalnePrzyPramce_lewa.Add(panelPodstawowy) Else miejscaSpecjalnePrzyPramce_prawa.Add(panelPodstawowy) End If End If End If Panel_glowny.Controls.Add(panelPodstawowy) ' Umieszcza elemeny na planszy Next listaLokalizacji.Add(listapaneli) Next End Sub #End Region |
Można teraz aktywować element w Form_Load:
1 2 3 4 |
'wybiera środek planszy jako punkty startowy panelAktualnieWybrany = listaLokalizacji(Math.Ceiling((szerokosc - 1) / 2))(Math.Ceiling((wysokosc) / 2)) (...) GrafikaBoiska.FillEllipse(start, New Rectangle(panelAktualnieWybrany.Location.X + 5, panelAktualnieWybrany.Location.Y + 5, 10, 10)) 'dodaje grafikę punktu startowego |
Wyznaczy nam to panel środka boiska i doda do gry piłkę (czarną kropkę).
Panele bramek dodamy sobie osobno to z powodu blokady, którą utworzymy, jeśli zbliżymy się wystarczająco blisko bramki, pole w bramce zostanie aktywowane i będziemy mogli strzelić gola. Odblokuj z Form_Load
1 2 |
'dodaje panele bramek DodajBramki() |
Kod który doda panele bramek to:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
#Region "Dodaj panele bramek" Dim bramka_1 As New Panel Dim bramka_2 As New Panel Private Sub DodajBramki() Dim srodek As Integer = Math.Floor((wysokosc) / 2) With bramka_1 .Location = New Point(0 - 10, (srodek + 1) * 49 - 10) .Size = New Size(20, 20) .BackColor = Color.Transparent .Name = "gol1" .Cursor = Cursors.Hand .Visible = False AddHandler .Click, AddressOf Bramka_Click End With With bramka_2 .Location = New Point((szerokosc + 1) * 49 - 10, (srodek + 1) * 49 - 10) .Size = New Size(20, 20) .BackColor = Color.Transparent .Name = "gol2" .Cursor = Cursors.Hand .Visible = False AddHandler .Click, AddressOf Bramka_Click End With Panel_glowny.Controls.Add(bramka_1) Panel_glowny.Controls.Add(bramka_2) End Sub Public Sub Bramka_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Panel_glowny.Enabled = False 'blokuje panel główny gry '' rysujlinie(DirectCast(sender, Panel)) 'rysuje linie strzału MsgBox("Wygrałeś!!") ' informacj ao wygranej End Sub #End Region |
Bramki to te panele podświetlone na żółto
Mamy bramki, mamy pola, mamy piłkę więc czas zacząć grę. Odblokuj wszędzie opcję:
1 |
AddHandler .Click, AddressOf pan_Click |
I odblokuj metodę:
1 |
rysujlinie(DirectCast(sender, Panel)) |
Nasza metoda pan_click będzie wyglądała tak:
1 2 3 4 5 |
Public Sub pan_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) '' odblokujPanele(DirectCast(sender, Panel)) DirectCast(sender, Panel).Visible = False rysujlinie(DirectCast(sender, Panel)) End Sub |
Metoda rysująca linie jest dosyć skomplikowana:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 |
Dim listaLini As New List(Of Tuple(Of Panel, Panel)) Dim zliczaj As Integer Private Sub rysujlinie(ByVal pane As Panel) BitmapaBoiska = New Bitmap(Panel_glowny.Width, Panel_glowny.Height) GrafikaBoiska = Graphics.FromImage(BitmapaBoiska) panelAktualnieWybrany.Visible = False generujLinieBoiska() '''''''''''''''''''''''''''''''''''''''''''''''''''''''''' 'Odblokowuje bramkę, jeśli piłka znajduje się w jej okolicy. If miejscaSpecjalnePrzyPramce_prawa.Contains(pane) Then bramka_2.Visible = True Else bramka_2.Visible = False End If If miejscaSpecjalnePrzyPramce_lewa.Contains(pane) Then bramka_1.Visible = True Else bramka_1.Visible = False End If '''''''''''''''''''''''''''''''''''''''''''''''''''''''''' If listaLokalizacjiaSpecjalnych.Contains(pane) Then 'blouje elementy specjalne, gdy piłka znajduje się na aucie, niektóre elementy są blokowane Dim mojindeks As Integer = listaLokalizacjiaSpecjalnych.IndexOf(pane) If Not mojindeks - 2 < 0 Then listaLokalizacjiaSpecjalnych(mojindeks - 2).Visible = False End If listaLokalizacjiaSpecjalnych(mojindeks + 2).Visible = False listaLokalizacjiaSpecjalnych(mojindeks - 1).Visible = False listaLokalizacjiaSpecjalnych(mojindeks + 1).Visible = False zliczaj -= 2 End If '''''''''''''''''''''''''''''''''''''''''''''''''''''''''' listaLini.Add(Tuple.Create(panelAktualnieWybrany, pane)) ' Blokuje panele powiązane (czyli te linie co już są dodane) For i As Integer = 0 To listaLini.Count - 1 If listaLini(i).Item1.Name = pane.Name Or listaLini(i).Item2.Name = pane.Name Then listaLini(i).Item2.Visible = False listaLini(i).Item1.Visible = False zliczaj -= 1 End If Next '''''''''''''''''''''''''''''''''''''''''''''''''''''''''' Dim myPen As Pen = New Pen(Color.Black, 2) ' kolor lini już dodanych 'rysuje dodane już linie For i As Integer = 0 To listaLini.Count - 1 GrafikaBoiska.DrawLine(myPen, listaLini(i).Item1.Location.X + 10, listaLini(i).Item1.Location.Y + 10, listaLini(i).Item2.Location.X + 10, listaLini(i).Item2.Location.Y + 10) Next GrafikaBoiska.DrawLine(myPen, panelAktualnieWybrany.Location.X + 10, panelAktualnieWybrany.Location.Y + 10, pane.Location.X + 10, pane.Location.Y + 10) '''''''''''''''''''''''''''''''''''''''''''''''''''''''''' 'rysuje piłkę GrafikaBoiska.FillEllipse(New System.Drawing.SolidBrush(Color.Gray), New Rectangle(pane.Location.X + 5, pane.Location.Y + 5, 10, 10)) '''''''''''''''''''''''''''''''''''''''''''''''''''''''''' Panel_glowny.Image = BitmapaBoiska panelAktualnieWybrany = pane '''''''''''''''''''''''''''''''''''''''''''''''''''''''''' If zliczaj <= 1 Then MessageBox.Show("Pat, przegrałeś!!", " Pat !!") Panel_glowny.Enabled = False End If End Sub |
Najpierw, generujemy grafikę i blokujemy panel, w którym aktualnie się znajdujemy. Następnie dodajemy grafikę boiska. Sprawdzamy, czy gracz znajduje się blisko bramki i odpowiednią bramkę odblokowuje. Kolejnym etapem jest lokalizacja specjalna, która ma dodatkowe ograniczenia (nie można się poruszać wzdłuż linii autu, ale można się odbijać od linii (przydatne, gdy podzielimy grę na dwóch graczy)). Kolejny etap to dodanie poprowadzonej przez nas linii do listy i blokada wybranych już paneli (poprowadzonych linii), dodanie poprowadzonych już linii do grafiki i linii aktualnie wykonanej. Na samym końcu dodajemy piłkę i zmieniamy grafikę boiska na aktualną. Efekt:
Gra nie ma jeszcze swoich ograniczeń, co powoduje wiele błędów, jednym z nich jest dostępność wszystkich pól, więc linie można robić, jak się żywnie podoba. Nasze ograniczenie siedzi w metodzie:
1 |
odblokujPanele(DirectCast(sender, Panel)) |
Która ogranicza zasięg dostępnych pól, zmniejsza miganie planszy i pozwala na wprowadzenie zasad gry w życie.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
Private Sub odblokujPanele(ByRef WybranyPanel As Panel) Dim pozycjax As Integer = 0 Dim pozycjay As Integer = 0 zliczaj = 0 'pobiera aktualną pozycję naszego panelu For i As Integer = 0 To listaLokalizacji.Count - 1 For j As Integer = 0 To listaLokalizacji(i).Count - 1 If listaLokalizacji(i)(j).Name = WybranyPanel.Name Then pozycjax = i pozycjay = j End If Next Next 'odblokowuje pobliskie panele, następnie metoda rysujlinie- wprowadza ograniczenia For i As Integer = 0 To listaLokalizacji.Count - 1 For j As Integer = 0 To listaLokalizacji(i).Count - 1 If i >= pozycjax - 1 And i <= pozycjax + 1 Then If j >= pozycjay - 1 And j <= pozycjay + 1 Then listaLokalizacji(i)(j).Visible = True zliczaj += 1 Else listaLokalizacji(i)(j).Visible = False End If Else listaLokalizacji(i)(j).Visible = False End If Next Next End Sub |
Jak zauważyliście metoda odblokujPanele, jest przed metodą rysujlinie, spowodowane jest to tym, że pierwsza metoda blokuje wszystkie panele i odblokowuje wszystkie panele +1 i -1, a następnie metoda rysujlinie wprowadza ograniczenia. Dwie metody połączone umożliwiają grę :]
W taką grę można sobie pograć z kolegą lub pójść o krok dalej i stworzyć AI, które będzie grało z nami. Wszystko jest w dostępne w programie, aby bez problemu dodać podział na tury z uwzględnieniem obijania się od ścian i od linii. Zostawiam to wam do zrobienia, jeśli będziecie mieli problem ze stworzeniem takiego podziału, to piszcie.
Pełen kod dostępny jest tutaj: Pilkarzyki_visualmonsters.cba.pl (2017-08-18)
kod źródłowy: kod_zrodlowy_pilkarzyki (2017-08-17)