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.
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ć:
'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:
'Losuje nasze obrazki
losowanieZnaczkow(Math.Floor((wielkoscPlanszy ^ 2) / 2))
Określamy w nim ile ma się znajdować poprawnych obrazków.
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:
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:
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:
'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.
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:
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:
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:
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ć.








