Chciałbym dzisiaj zaprezentować bardzo prostą i ciekawą grę. Rozwija ona pamięć i spostrzegawczość, zasady gry są bardzo proste. Gra składa się z planszy podzielonej na kwadraty, każdy kwadrat może być błędny lub poprawny. Należy zapamiętać, gdzie ułożone są poprawne kwadraciki i je wszystkie odsłonić. Gra do przetestowania dostępna do pobrania:
gra do przetestowania => neuron_visualmonsters-cba-pl
Projekt z tutoriala poniżej dostępny tutaj => neuron_visualmonsters-cba-pl
Tylko kod programu => neuron_visualmonsters-cba-pl
Nasza gra będzie trochę mniej złożona, zaczynamy od dodania elementów do głównej formy gry:
Rodzaj elementu | Nazwa elementu | ustawienia |
---|---|---|
Form | Form1 | Name: Form1 Text: Neuron VisualMonsters.cba.pl Size: 499; 594 |
Panel | Panel_glowny | Name: Panel_glowny Size: 459; 472 Location: 12; 12 BackColor: White Anchor: Top, Bottom, Left, Right |
Label | Label1 | Name: Label1 Text: Pozostało do odkrycia : Location: 12; 498 |
Label | Label2 | Name: Label2 Text: 0/0 Location: 137; 498 |
Label | Label3 | Name: Label3 Text: Punkty: Location: 271; 498 |
Label | Label4 | Name: Label4 Text: 0 Location: 320; 498 |
Główny silnik gry uruchamiał będzie się wraz z jej startem w Form_load, to on będzie generował planszę i dodawał główne elementy do listy. To w nim będzie zawarty poziom trudności i ilość punktów, jakie będzie można zdobyć, więc jego zrozumienie jest najważniejsze.
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 |
Public Class Form1 Dim kolorTla As Color = Color.Transparent 'kolor tła gry Dim obrazekBledu As Bitmap = My.Resources.blad Dim Obrazek As Bitmap = My.Resources.obrazek Dim ListaKwadratow As New List(Of Panel) 'Lista, przechowuje wygenerowane kwadraciki Dim punktyDoZdobycia As Integer 'Określa ilość punktów jaką można zdobyć Dim iloscelementowUsuwanych As Integer 'Zasady gry wymagają aby razem z błędnym kwadratem znikały kwadraty odsłonięte Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load 'Określamy wielkość planszy do gry Dim wielkoscPlanszy As Integer = 3 'Tworzy przezroczysty kolor dla obrazka (usuwa biały kolor) Obrazek.MakeTransparent(Color.White) obrazekBledu.MakeTransparent(Color.White) 'Tworzy panel w którym będą zawieszone nasze małe kwadraty Dim panel_gry As New Panel 'Wielkość naszego małego kwadratu, szerokość i wysokość Dim wielkoscKwadratu As Integer = 50 'Pętla dobiera wielkość kwadratów jeśli nie mieszczą się na planszy gry If 50 * wielkoscPlanszy + 5 > Panel_glowny.Width Then wielkoscKwadratu = (Panel_glowny.Width - 5) / wielkoscPlanszy End If If wielkoscKwadratu * wielkoscPlanszy + 5 > Panel_glowny.Height Then wielkoscKwadratu = (Panel_glowny.Height - 5) / wielkoscPlanszy End If 'Generuje wielkość i lokalizację głównego panelu zawierającego małe kwadraty panel_gry.Size = New Size(wielkoscPlanszy * wielkoscKwadratu + 1, wielkoscPlanszy * wielkoscKwadratu + 1) panel_gry.Location = New Point((Panel_glowny.Width - panel_gry.Width) / 2, (Panel_glowny.Height - panel_gry.Height) / 2) 'Generuje zestaw małych kwadratów i wyświetla go na planszy For k As Integer = 0 To wielkoscPlanszy - 1 For d As Integer = 0 To wielkoscPlanszy - 1 Dim panelPodstawowy As New Panel With panelPodstawowy .Location = New Point(1 + k * wielkoscKwadratu, 1 + d * wielkoscKwadratu) .BackColor = kolorTla .BackgroundImageLayout = ImageLayout.Stretch .Cursor = Cursors.Hand .Size = New Size(wielkoscKwadratu, wielkoscKwadratu) .BorderStyle = BorderStyle.FixedSingle End With '' AddHandler panelPodstawowy.Click, AddressOf pan_Click ListaKwadratow.Add(panelPodstawowy) panel_gry.Controls.Add(panelPodstawowy) Next Next Panel_glowny.Controls.Add(panel_gry) 'Określamy, ile można zdobyć maksymalnie punktów punktyDoZdobycia = wielkoscPlanszy * 10 Label4.Text = punktyDoZdobycia 'Losuje nasze obrazki '' losowanieZnaczkow(Math.Floor((wielkoscPlanszy ^ 2) / 2)) 'Jeśli klikniemy na zły kwadracik, razem z nim zniknie 'iloscelementowUsuwanych' znaczków odkrytych iloscelementowUsuwanych = 2 'Blokuje możliwość kliknięcia na kwadracik w momencie wykonywania instrukcji kontrolerKlikniecia = True End Sub End Class |
Program najpierw sprawdza, czy wybrany rozmiar kwadratów połączona z wielkością gry zmieści się w głównym oknie gry, bez sensu byłaby gra, w której nie widać większości pól. Jeśli plansza nie mieści się w głównym oknie, wielkość kwadracików zostanie zmieniona. Nie ważne czy kwadraciki nie zmieszczą się na szerokość, czy na wysokość, pętla ta dopasuje tak nasze kwadraciki, że będą się mieścić:
1 2 3 4 5 6 7 |
'Pętla dobiera wielkość kwadratów jeśli nie mieszczą się na planszy gry If 50 * wielkoscPlanszy + 5 > Panel_glowny.Width Then wielkoscKwadratu = (Panel_glowny.Width - 5) / wielkoscPlanszy End If If wielkoscKwadratu * wielkoscPlanszy + 5 > Panel_glowny.Height Then wielkoscKwadratu = (Panel_glowny.Height - 5) / wielkoscPlanszy End If |
Wizualnie wygląda to tak:
Formuła, nie jest bardzo skomplikowany, reszta elementów opisana jest w kodzie, a ciekawsze rzeczy będę jeszcze tłumaczył. Odblokowujemy teraz kolejny element generujący tym razem losowy dobór obrazków błędnych i poprawnych:
1 2 |
'Losuje nasze obrazki losowanieZnaczkow(Math.Floor((wielkoscPlanszy ^ 2) / 2)) |
Określamy w nim ile ma się znajdować poprawnych obrazków.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
Dim ran As New Random Dim listaboolean As New List(Of Boolean) Private Sub losowanieZnaczkow(ByVal iloscPoprawnychPol As Integer) For i As Integer = 0 To ListaKwadratow.Count - 1 listaboolean.Add(False) Next Dim iloscznaczkow As Integer = 0 'Pętla uruchamiana jest do momętu wybrania odpowiedniej liczby 'losowych elementów bez powtórzeń (iloscznaczkow) Do Dim mojawylosowanawartosc = ran.Next(0, listaboolean.Count) If listaboolean(mojawylosowanawartosc) = False Then listaboolean(mojawylosowanawartosc) = True iloscznaczkow += 1 End If If iloscznaczkow = iloscPoprawnychPol Then Exit Do End If Loop 'Wyświetla ilość wybranych elementów Label2.Text = "0/" + iloscPoprawnychPol.ToString End Sub |
Lista, która została stworzona na początku (listaboolean), przechowuje tylko informacje true/false kod wybiera nam losowo „iloscPoprawnychPol” i kończy pętle gdy „iloscznaczkow = iloscPoprawnychPol” najpierw lista wypełniana jest elementami false, a następnie losowo bez powtórzeń losujemy liczby i zmieniamy wybrane elementy listy na True. Na końcu wyświetlamy, ile jest pól do odkrycia w label2. Odblokujemy teraz:
1 |
AddHandler panelPodstawowy.Click, AddressOf pan_Click |
W pętli tworzącej kwadraty. Doda ona do każdego naszego kwadratu formułę uruchamianą po jego kliknięciu. Ta formuła 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 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 |
Dim panelWybrany As Panel Private WithEvents Timer1 As New Timer With {.Interval = 200} Private WithEvents Timer2 As New Timer With {.Interval = 1000} Dim kontrolerKlikniecia As Boolean = False Dim listaZlapanychObrazkow As New List(Of Panel) Public Sub pan_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) If kontrolerKlikniecia = True Then 'blokuje grę, jeśli kwadraty są odsłonięte 'rozpoczyna grę If Not Timer2.Enabled = True Then Timer2.Start() End If panelWybrany = DirectCast(sender, Panel) 'przechowuje wybrany przez nas panel w zmiennej Dim indeX As Integer = ListaKwadratow.IndexOf(DirectCast(sender, Panel)) 'wyszukuje indeks panelu w liście 'Jeśli w liście "listaboolean" pod indeksem naszego kwadratu kruje się true (nasz poprawny obrazek) wtedy If listaboolean(indeX) = True Then 'Obrazek poprawny 'wyświetla obrazek DirectCast(sender, Panel).BackColor = Color.FromArgb(30, Color.LimeGreen) DirectCast(sender, Panel).BackgroundImage = Obrazek DirectCast(sender, Panel).Enabled = False 'dodaje nasz panel do listy odkrytych paneli listaZlapanychObrazkow.Add(DirectCast(sender, Panel)) 'wyświetla ilość zdobytych punktów Label2.Text = listaZlapanychObrazkow.Count.ToString + "/" + Math.Floor(ListaKwadratow.Count / 2).ToString 'Kończy grę, jeśli odkryliśmy już wszystkie obrazki If listaZlapanychObrazkow.Count = Math.Floor(ListaKwadratow.Count / 2) Then For i As Integer = 0 To ListaKwadratow.Count - 1 ListaKwadratow(i).Enabled = False ListaKwadratow(i).BackColor = Color.FromArgb(100, Color.LimeGreen) If listaboolean(i) = True Then ListaKwadratow(i).BackgroundImage = Obrazek Else ListaKwadratow(i).BackgroundImage = obrazekBledu End If kontrolerKlikniecia = False Timer2.Stop() Next Else kontrolerKlikniecia = True End If Else 'obrazek niepoprawny DirectCast(sender, Panel).BackColor = Color.FromArgb(30, Color.Red) DirectCast(sender, Panel).BackgroundImage = obrazekBledu kontrolerKlikniecia = False Timer1.Start() End If End If End Sub |
Timer1 posłużą nam do wydłużenia czasu wyświetlania elementów, aby użytkownik mógł zdążyć, zapamiętać gdzie leży niepoprawny obrazek i które obrazki znikneły. Jeśli go nie użyjemy, to po kliknięciu nie pojawi się obrazek. Dlatego użyjemy zmiennej „KontrolerKlikniecia” która będzie blokowała możliwość kliknięcia, na czas działania Timera1 (użytkownik mógłby, kliknąć kilka pól co by spowodowało błąd gry). Timer2 będzie zmniejszał ilość punktów do zdobycia. Po kliknięciu, na kwadracik formuła sprawdza jego położenie w liście „ListaKwadratow” a następnie w „listaboolean” sprawdza, czy na określonym indeksie jest True, czy False. Jeśli True, wtedy doda nasz kwadrat do listy „ListaZlapanychObrazkow” i będziemy mogli go ukryć na późniejszym etapie gry. Jeśli obrazek jest poprawny, pętla pokażę, ile jest odkrytych obrazków i sprawdzi czy to już koniec gry. Jeśli to koniec gry, plansza zostanie odkryta czas zatrzymany a blokada włączona. Pętla:
1 2 3 4 |
'rozpoczyna grę If Not Timer2.Enabled = True Then Timer2.Start() End If |
Sprawdza, czy Timer2 jest włączony. Jeśli nie to go uruchamia (oficjale rozpoczęcie gry), to on będzie liczył nam punkty. Nasza form, ma już aktywne kwadraty:
Uwaga, jeśli trafimy na czerwony krzyżyk, to gra się nam zablokuje, więc się nie przestraszcie. Zajmiemy się teraz Timerem2, tak jak było już mówione, zlicza on nam punkty. Jego szybkość ustawiona jest na jedną sekundę. W każdej sekundzie ilość punktów do zdobycia będzie maleć o jeden. Ilość punktów do zdobycia powinna być zależna od poziomu rozgrywki.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
Private Sub Timer2_Tick(sender As Object, e As EventArgs) Handles Timer2.Tick 'zmniejsza punkty do zdobycia o jeden punktyDoZdobycia -= 1 'wyświetla te punkty w labelu na formie Label4.Text = punktyDoZdobycia.ToString 'kończy grę, jeśli nie ma już żadnych punktów do zdobycia If punktyDoZdobycia <= 0 Then 'pętla odkrywa wszystkie pola, podświetlając je na czerwono For i As Integer = 0 To ListaKwadratow.Count - 1 If listaboolean(i) = True Then ListaKwadratow(i).BackgroundImage = Obrazek Else ListaKwadratow(i).BackgroundImage = obrazekBledu End If ListaKwadratow(i).BackColor = Color.FromArgb(255, Color.Tomato) Next 'blokuje plansze kontrolerKlikniecia = False 'wyłącza timer Timer2.Stop() End If End Sub |
Oczywiście czas zmniejszania ilości punktów można zmienić tutaj:
1 |
Private WithEvents Timer2 As New Timer With {.Interval = 1000} |
Wielkość Intervalu na poziomie 1000 oznacza jedną sekundę (instrukcja wykonywana jest co sekundę). Ostatnim elementem gry jest Timer1:
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 |
Private Sub Timer1_Tick(sender As Object, e As EventArgs) Handles Timer1.Tick 'zatrzymuje timer Timer1.Stop() 'usówa obrazek z klikniętego panelu panelWybrany.BackgroundImage = Nothing panelWybrany.BackColor = kolorTla 'jeśli ilość elementów w złapanym obrazku jest większa od ilości elementów do usunięcia If listaZlapanychObrazkow.Count > iloscelementowUsuwanych Then Dim ilosc As Integer = 0 For i As Integer = 0 To iloscelementowUsuwanych - 1 'Wybierze losowy element Dim randomWybor As Integer = ran.Next(0, listaZlapanychObrazkow.Count) 'usunie jego obrazek listaZlapanychObrazkow(randomWybor).BackgroundImage = Nothing listaZlapanychObrazkow(randomWybor).BackColor = kolorTla 'usunie go z listy złapanych obrazków listaZlapanychObrazkow.RemoveAt(randomWybor) Next Else 'Jeśli obrazków jest mniej to usunie je wszystkie If Not listaZlapanychObrazkow.Count = 0 Then 'chyba, że lista jest pusta i nie ma co usuwać For i As Integer = listaZlapanychObrazkow.Count - 1 To 0 Step -1 'usuwa jego obrazek listaZlapanychObrazkow(i).BackgroundImage = Nothing listaZlapanychObrazkow(i).BackColor = kolorTla 'wywala z listy listaZlapanychObrazkow.RemoveAt(i) Next End If End If 'aktualizuje ilość elementów do wyszukania Label2.Text = listaZlapanychObrazkow.Count.ToString + "/" + Math.Floor(ListaKwadratow.Count / 2).ToString 'odblokowuje kliknięcie kontrolerKlikniecia = True End Sub |
Dlaczego zatrzymałem timer zaraz po jego uruchomieniu, otóż nasza formuła najpierw poczeka 200 milisekund a następnie, wykona instrukcje w sobie zawartą. Jeśli zwiększymy wartość w:
1 |
Private WithEvents Timer1 As New Timer With {.Interval = 200} |
do 500 wtedy program odsłoni obrazek, poczeka pół sekundy, a następnie wykona instrukcje. Na kolejnym etapie najpierw nasza formuła musi kliknięty obrazek zasłonić, użytkownik już wie, co się pod naszym panelem kryje, gdyż daliśmy mu, pół sekundy na zobaczenie krzyżyka i można go już zasłonić. Następna pętla zasłania dodatkowo odkryte kwadraty (tak jak w zasadach gry). Tutaj następuje dylemat, ponieważ mamy dwie sytuacje, pierwsza to taka, że złapanych obrazków jest więcej niż obrazków do zasłonięcia, a druga to obrazków złapanych jest mniej. W pierwszej sytuacji program losowo wybiera obrazki do zasłonięcia i usuwa je z listy, a w drugiej zasłania wszystkie odsłonięte elementy. To by było na tyle, jak by były jakieś pytania, to proszę pisać.