Magiczna ukladanka

Widziałem ostatnio w telewizji zajawkę fajnej gry planszowe, nazywa się Magic Block Game i naprawdę wyglądała bardzo interesująco. Postanowiłem więc napisać tutorial jak taką grę napisać w języku vb.net. Oczywiście będzie to jedna z tych lekcji, w której pokażę wam jak to zrobić, jak to działa, a resztę zostawię waszej wyobraźni. Czy zrobicie grę na czas, czy rywalizację na jednym komputerze a może w sieci to już zostawiam wam, gra polega na przesuwaniu kwadracików tak, aby w środku głównej planszy utworzyć wygenerowany wzór, przy czym można przesuwać tylko te elementy sąsiadujące z pustym polem, gra będzie wyglądać tak:

Dodatkową zaletą będzie to, że będziemy mogli bez strat edytować jej rozmiar. Zaczniemy od przygotowania formy. Tutaj za dużo roboty nie będzie, ponieważ wszystkie elementy będą generowały się automatycznie:

 

Rodzaj elementu Nazwa elementu Ustawienia
Form Form1 Name: Form1
Text: Magiczne klocki
Size: 675; 669
Splitter1 Splitter1  Size: 659; 188
Panel wzor  Size: 282; 152
BackColor: Black
Location: (dowolna na Splitterze)
Panel PlanszaGlowna  Size: 282; 152
BackColor: Black
Location: (dowolna)

 

 

 

 

Dodatkowo musimy dysponować sześcioma kafelkami. Nasza plansza główna będzie miała 25 pól, czyli 6*4=24 + puste pole. Kafelki do pobrania: kafelki

Należy je rozpakować do folderu i montujemy je tak jak na gifie:

Dobra, wszystko jest. Zaczniemy od dodania zmiennych, a następnie będziemy dodawać metody, których działanie będziemy sobie omawiać.

Imports System.Drawing.Drawing2D

Public Class Form1
    'struktura głównych elementów przesuwalnych
    Private Structure polaGryS
        Dim kolor As Integer 'kolor jako index na liście listaKolorow
        Dim rect As Rectangle
        Dim x As Integer
        Dim y As Integer
        Dim img As Image
    End Structure

    Private Structure polaWzoruS
        Dim kolor As Integer
        Dim x As Integer
        Dim y As Integer
        Dim img As Image
    End Structure

    'tablica wzoru
    Private polaWzoru(3, 3) As Rectangle
    'główna tablica gry
    Private polaGlowne(4, 4) As Rectangle
    Private polaGlowneBool(4, 4) As Boolean

    Dim GlownaMapa As Bitmap 'bitmapa planszy gry (jako tło panelu)
    Dim WzorMapa As Bitmap 'bitmapa wzoru (jako tło panelu)

    Dim WielkoscPolaWzor As Integer 'wielkość pola wzoru
    Dim WielkoscPola As Integer 'wielkość pola głównej planszy

    Dim poczatek As Boolean = False 'zmienna oddzielająca generowanie wzoru od zmiany wielkości formy
    Dim listaKoncaGry As New List(Of Integer) 'lista sprawdzająca koniec gry

    Dim listaKolorow As New List(Of Image) ' lista obrazków (w tym wypadku kolorów)

    Dim kolekcjaWzor As New Collection 'kolekcja przechowuje elementy pól wzoru

    'przechouje listę pól gry (lista a nie kolekcja, ponieważ lista jest bardziej elastyczna)
    Dim kolekcjapol As New List(Of polaGryS)

    Dim ran As New Random

    Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load

    End Sub
End Class

Musimy zaimportować bibliotekę System.Drawing.Drawing2D której element wykorzystamy do dostosowania wielkości obrazka do wielkości pola. Zaczniemy od przygotowania naszych kafelków, należy je dodać do listy „listaKolorow”, w tym celu utworzymy metodę, która zrobi to przed generowaniem wzoru i pól. Dodaj do projektu:

    Private Sub wybierzKolory()
        'pobiera obrazki ruchomych kwadracików
        listaKolorow.Add(My.Resources.czerwony2)
        listaKolorow.Add(My.Resources.bialy2)
        listaKolorow.Add(My.Resources.niebieski)
        listaKolorow.Add(My.Resources.pomarancz)
        listaKolorow.Add(My.Resources.zielony)
        listaKolorow.Add(My.Resources.zolty)
    End Sub

    Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
        'ustawia grę
        wybierzKolory()
        ZmianaRozmiaru()
        poczatek = True
    End Sub

Metoda „wybierzKolory()” zapełni nam naszą listę kafelkami. Należy dostosować ilość elementów listy do takiej liczby, aby jej przemnożenie przez ilość możliwości wyboru dawało 24. Teraz możliwość wyboru jednego kafelka to 4, ponieważ 4*6 =24, jeśli chcielibyśmy zwiększyć lub zmniejszyć ilość kafelków musielibyśmy robić to w następujący sposób 6*4=24 lub 8*3=24, 3*8 =24, 2*12=24, 1*24=24 itp. Kiedy załadujemy już sobie listę kafelków, przechodzimy do ustawienia planszy wzoru i głównej planszy gry. Należy dostosować ich wielkości, wielkość planszy wzoru będzie zależna od wielkości Splittera1 a planszy gry od wielkości formy, dodaj metodę do projektu:

    Private Sub ZmianaRozmiaru()
        'Ustawia wielkości pól gry i wzoru
        wzor.Size = New Size(Splitter1.Height - 10, Splitter1.Height - 10) 'wielkość planszy
        wzor.Location = New Point((Splitter1.Width - wzor.Width) / 2, 5) ' plansza wzoru zawsze na środku
        'Wielkość planszy gry
        If Me.Width > (Me.Height - Splitter1.Height) Then
            PlanszaGlowna.Size = New Size((Me.Height - Splitter1.Height) - 60, (Me.Height - Splitter1.Height) - 60)
        Else
            PlanszaGlowna.Size = New Size((Me.Width) - 20, (Me.Width) - 20)
        End If
        'lokalizacja na środku
        PlanszaGlowna.Location = New Point((Me.Width - PlanszaGlowna.Width) / 2, Splitter1.Height + 10)
        'generuj pola (wzoru i planszy główenj)
        GeneratorPol()
    End Sub

Wygląda to tak:

Za zmianę w czasie rzeczywistym odpowiedzialna będzie metoda:

    Private Sub Form1_Resize(sender As Object, e As EventArgs) Handles MyBase.Resize
        'włączony dopiero po przygotowaniu gry
        If poczatek = True Then
            ZmianaRozmiaru()
        End If
    End Sub

Podczas zmiany wielkości głównej formy, wielkość plansz zostanie zaktualizowana. Zajmijmy się teraz przygotowaniem wzoru i pól gry, aby gra miała sens, pola muszą być generowane losowo. Jeśli chodzi o wzór, jego pola nie będą ulegały zmianie lokalizacji, dlatego ten element będzie bardziej statyczny. Główna plansza gry będzie bardziej złożona, ta przestrzeń będzie składała się z kilku elementów:

PolaGlowneBool- określa lokalizacje pola pustego.

polaGlowne — ich wielkość jest generowana, gdy zmienia się wielkość planszy, w nie wpisujemy obrazki kolorów.

Metoda ta ma dwa stany. Pierwszy jest uruchamiany podczas uruchamiania gry, przygotowuje on obie plansze do gry. Drugi jest, wtedy gdy plansza jest załadowana, a my zmieniamy jej rozmiar.

    Private Sub GeneratorPol()
        'Przygotowuje czystą mapę pól, ich wielkość i położenie
        ' następnie klonuje tą magę plansz (wzoru i głównej planszy gry) i zapisuje w zmiennych:GlownaMapa 
        '                                                                                       i WzorMapa
        ' na których namalujemy odpowiednie kwadraciki

        Dim GlownaMapaPoczatek As New Bitmap(PlanszaGlowna.Width, PlanszaGlowna.Height)
        Dim WzorMapaPoczatek As New Bitmap(wzor.Width, wzor.Height)

        Dim g As Graphics = Graphics.FromImage(GlownaMapaPoczatek)
        Dim g2 As Graphics = Graphics.FromImage(WzorMapaPoczatek)

        WielkoscPolaWzor = (wzor.Width / 3) - 3
        WielkoscPola = (PlanszaGlowna.Width / 5) - 5

        If poczatek = False Then
            'generowana na początku, ustawia kwadraty, ich wielkość i polożenie
            Dim kolorPola As New SolidBrush(Color.FromArgb(60, 240, 240, 240))
            For i As Integer = 0 To 4
                For j As Integer = 0 To 4
                    polaGlowne(i, j) = New Rectangle(10 + (WielkoscPola * i), 10 + (WielkoscPola * j),
                                                     WielkoscPola - 1, WielkoscPola - 1)
                    g.FillRectangle(kolorPola, polaGlowne(i, j))

                    If Not j >= 3 Then
                        If Not i >= 3 Then
                            polaWzoru(i, j) = New Rectangle(5 + (WielkoscPolaWzor * i), 5 + (WielkoscPolaWzor * j),
                                                            WielkoscPolaWzor - 1, WielkoscPolaWzor - 1)
                            g2.FillRectangle(kolorPola, polaWzoru(i, j))
                        End If
                    End If
                    'tablica polaGlowneBool określa który kwadracik jest pusty, w tym wypadku, prawy dolny róg
                    If Not (i = 4 And j = 4) Then
                        polaGlowneBool(i, j) = False
                    Else
                        polaGlowneBool(i, j) = True
                    End If
                Next
            Next
        Else
            'jeśli to nie jest początek gry, zmieniamy tylko wielkość i położenie pól, zmieniamy wielkość pól na 
            '                                                                                             liście
            Dim kolorPola As New SolidBrush(Color.FromArgb(60, 240, 240, 240))
            For i As Integer = 0 To 4
                For j As Integer = 0 To 4
                    'zmieniamy wielkości w tablicy publicznej
                    polaGlowne(i, j) = New Rectangle(10 + (WielkoscPola * i), 10 + (WielkoscPola * j),
                                                     WielkoscPola - 1, WielkoscPola - 1)
                    g.FillRectangle(kolorPola, polaGlowne(i, j))
                    For a As Integer = 0 To kolekcjapol.Count - 1
                        If i = kolekcjapol(a).x And j = kolekcjapol(a).y Then
                            'przypisujemy zmienione pole do listy stuktury
                            Dim p As polaGryS = kolekcjapol(a)
                            p.rect = polaGlowne(i, j)
                            kolekcjapol(a) = p
                        End If
                    Next
                Next
            Next
        End If
        'ustawiamy tło paneli
        GlownaMapa = GlownaMapaPoczatek.Clone
        WzorMapa = WzorMapaPoczatek.Clone
        g.Dispose()
        g2.Dispose()
        'Jeśli jest to początek gry, należy przygotować wzór i losowo wyłożyć kolory
        If poczatek = False Then
            generujWzor()
        Else
            'jeśli nie jest to początek, należy wtedy poustawiać odpowiednio kolory
            UzupelnijWzor()
        End If

    End Sub

Chwilowo nic się jeszcze nie dzieje, ponieważ przygotowaliśmy tylko pola główne i określiliśmy ich wielkość. Jeśli dodamy sobie dwie linijki kodu, to zobaczymy, co zostało zrobione:

        wzor.BackgroundImage = WzorMapa
        PlanszaGlowna.BackgroundImage = GlownaMapa

Zajmiemy się teraz metodą „generujWzor()” która ładuje kafelki na plansze i przygotowuje puste pole:

    Private Sub generujWzor()
        ' Pierwszym etapem jest wygenerowanie wzoru do ułożenia i zapełnienie kolecji wzoru i listy pól
        'inicjujemy struktury
        Dim polaW As polaWzoruS
        Dim polaG As polaGryS
        'tworzymy chwilowe bitmapy, aby nie uszkodzić bitmapy oustej
        Dim chwilowaWzor As Bitmap = WzorMapa.Clone
        Dim chwilowaPola As Bitmap = GlownaMapa.Clone

        Dim gr_dest As Graphics = Graphics.FromImage(chwilowaWzor)
        Dim gr_dest2 As Graphics = Graphics.FromImage(chwilowaPola)

        Dim listaWylosowana(5) As Integer ' tablica będzie pilnowała aby kolorów nie było więcej niż 4
        'zaczynamy od stworzenia wzoru 3x3
        For i As Integer = 0 To 2
            For j As Integer = 0 To 2
                'losujemy kolor 
                Dim randomowy As Integer
                Do
                    randomowy = ran.Next(0, 6)
                    'jeśli już mamy trzy wylosowane takie kolory, wtedy losujemy ponownie
                    If listaWylosowana(randomowy) + 1 <= 4 Then
                        Exit Do
                    End If
                Loop
                'wypełniamy strukturę danymi
                polaW.x = i
                polaW.y = j
                polaW.kolor = randomowy
                polaW.img = ZmienRozmiar(listaKolorow(randomowy), New Size(WielkoscPolaWzor, WielkoscPolaWzor))

                listaKoncaGry.Add(randomowy)
                listaWylosowana(randomowy) += 1 'uzupelniamy listę pilnującą ilości kolorów
                'rysujemy element
                gr_dest.DrawImage(polaW.img, polaWzoru(i, j).Location.X, polaWzoru(i, j).Location.Y,
                                  polaW.img.Width - 1, polaW.img.Height - 1)
                kolekcjaWzor.Add(polaW) ' uzupełniamy kolekcję
            Next
        Next
        'tworzymy tablicę 5x5
        'czyścimy tablicę
        ReDim listaWylosowana(5) ' tablica będzie pilnowała aby kolorów nie było więcej niż 4
        For i As Integer = 0 To 4
            For j As Integer = 0 To 4
                'uzupełnia tylko pola false, zostawi jedno pole czyste
                If polaGlowneBool(i, j) = False Then
                    'losujemy kolor
                    Dim randomowy As Integer
                    Do
                        randomowy = ran.Next(0, 6)
                        If listaWylosowana(randomowy) + 1 <= 4 Then
                            Exit Do
                        End If
                    Loop
                    'wypełniamy strukturę danymi
                    polaG.rect = polaGlowne(i, j)
                    polaG.x = i
                    polaG.y = j
                    polaG.kolor = randomowy
                    polaG.img = ZmienRozmiar(listaKolorow(randomowy), New Size(WielkoscPola, WielkoscPola))

                    listaWylosowana(randomowy) += 1 'uzupelniamy listę pilnującą ilości kolorów
                    'rysujemy element
                    gr_dest2.DrawImage(polaG.img, polaGlowne(i, j).Location.X, polaGlowne(i, j).Location.Y,
                                       polaG.img.Width - 1, polaG.img.Height - 1)
                    kolekcjapol.Add(polaG) ' uzupełniamy listę
                End If
            Next
        Next
        'Ustawiamy nowe tła panaeli
        wzor.Image = chwilowaWzor
        PlanszaGlowna.Image = chwilowaPola
    End Sub

Zmienna randomowy odpowiedzialna jest za wylosowanie kafelka, listaWylosowana pilnuje za to, aby jednego rodzaju kafelka nie było więcej niż cztery. Jeśli chcesz zwiększyć lub zmniejszyć ilość kafelków, musisz zmienić te opcje odpowiednio do ilości kafelków, które posiadasz. W zależności od wielkości pól musimy przygotować tak obrazki naszych kafelków tak, aby był wielkości wcześniej określonego pola. Czyli należy zmniejszyć lub zwiększyć jego rozmiar. Odpowiedzialna jest za to funkcja ZmienRozmiar(), która wykorzystuje do zmiany rozmiaru bibliotekę System.Drawing.Drawing2D.

    Public Shared Function ZmienRozmiar(ByVal obrazek As Image, ByVal wielkosc As Size, Optional ByVal _
                                        ZachowajProporcjeObrazu As Boolean = True) As Image

        Dim NowaSzerokosc As Integer
        Dim NowaWysokosc As Integer
        'Określamy wielkość nowego obrazku, w zależności czy proporcje mają być zachowane
        If ZachowajProporcjeObrazu Then
            Dim OgyginalnaSzerokosc As Integer = obrazek.Width
            Dim OryginalnaWysokosc As Integer = obrazek.Height
            Dim SzerokoscProcent As Single = CSng(wielkosc.Width) / CSng(OgyginalnaSzerokosc)
            Dim WysokoscProcent As Single = CSng(wielkosc.Height) / CSng(OryginalnaWysokosc)
            Dim Procent As Single = If(WysokoscProcent < SzerokoscProcent,
                WysokoscProcent, SzerokoscProcent)
            NowaSzerokosc = CInt(OgyginalnaSzerokosc * Procent)
            NowaWysokosc = CInt(OryginalnaWysokosc * Procent)
        Else
            NowaSzerokosc = wielkosc.Width
            NowaWysokosc = wielkosc.Height
        End If

        Dim NowyObrazek As Image = New Bitmap(NowaSzerokosc, NowaWysokosc)
        'generujemy nowy obrazek
        Using graphicsHandle As Graphics = Graphics.FromImage(NowyObrazek)
            graphicsHandle.InterpolationMode = InterpolationMode.HighQualityBicubic
            graphicsHandle.DrawImage(obrazek, 0, 0, NowaSzerokosc, NowaWysokosc)
        End Using
        'funkcja zwraca NowyObrazek
        Return NowyObrazek
    End Function

Zobaczmy jak to wszystko działa:

Jak widzimy, pola są już dodane, lecz nasze pola nie dostosowują się do wielkości formy, ponieważ podczas wywołania metody Form1_Resize mamy już zmienną poczatek ustawioną na wartość true co oznacza, że jeśli zmieniamy rozmiar formy, wywoływana jest metoda UzupelnijWzor(), jeśli podczas zmiany rozmiaru wywoływana byłaby metoda generujWzor() wtedy podczas każdej zmiany, program generowałby nowy losowy wzór i nowe losowe pola. Dodajmy więc metodę UzupelnijWzor():

    Private Sub UzupelnijWzor()
        'metoda zmienia rozmiary gry wzór i plansza są już wygenerowane
        Dim chwilowaWzor As Bitmap = WzorMapa.Clone
        Dim chwilowaPola As Bitmap = GlownaMapa.Clone

        Dim gr_dest As Graphics = Graphics.FromImage(chwilowaWzor)
        Dim gr_dest2 As Graphics = Graphics.FromImage(chwilowaPola)

        'najpierw rysujemy kolory wzoru na podstawie zmienionych wielkości pól
        'mimo iż nic zabardzo się tu nie zmienia (przez budowe formy) dodaje go może komuś się przyda
        'nic nie psuje, można go usunąc
        For i As Integer = 0 To 2
            For j As Integer = 0 To 2
                For a As Integer = 1 To kolekcjaWzor.Count
                    If i = kolekcjaWzor(a).x And j = kolekcjaWzor(a).y Then
                        Dim myimage As Image = ZmienRozmiar(kolekcjaWzor(a).img,
                                                            New Size(WielkoscPolaWzor, WielkoscPolaWzor))
                        gr_dest.DrawImage(myimage, polaWzoru(i, j).Location.X, polaWzoru(i, j).Location.Y,
                                          myimage.Width - 1, myimage.Height - 1)
                    End If
                Next
            Next
        Next
        wzor.Image = chwilowaWzor 'ustawiamy tło panelu wzoru
        'następnie rysujemy kolory pól głównej planszy na podstawie zmienionych wielkości pól
        For i As Integer = 0 To 4
            For j As Integer = 0 To 4
                If polaGlowneBool(i, j) = False Then
                    For a As Integer = 0 To kolekcjapol.Count - 1
                        If i = kolekcjapol(a).x And j = kolekcjapol(a).y Then
                            Dim myimage As Image = ZmienRozmiar(kolekcjapol(a).img,
                                                                New Size(WielkoscPola, WielkoscPola))
                            gr_dest2.DrawImage(myimage, kolekcjapol(a).rect.Location.X,
                                               kolekcjapol(a).rect.Location.Y, myimage.Width - 1, myimage.Height - 1)
                        End If
                    Next
                End If
            Next
        Next
        PlanszaGlowna.Image = chwilowaPola
        KoniecGry()
    End Sub

Etap pierwszy metody, tak naprawdę nic nie robi, gdyż plansza wzoru jest nieruchoma, drugi etap ustawia kafelki na podstawie kolekcjipol w której zapisane są koordynaty pól, ich wielkość i obrazki. Nie działa już tylko ostatni element, który kontroluje czy gra już dobiegła końca:

    Private Sub KoniecGry()
        'Metoda sprawdza koniec gry
        Dim graWygrana As Boolean = True 'zakładamy, że to koniec
        Dim listaPorownawcza As New List(Of Integer) ' lista przechowująca 
                                    'numery kolorów środka głównej planszy
        'pobiera numey kolorów środka gry
        For i As Integer = 1 To 3
            For j As Integer = 1 To 3
                For k As Integer = 0 To kolekcjapol.Count - 1
                    If kolekcjapol(k).x = i And kolekcjapol(k).y = j Then
                        listaPorownawcza.Add(kolekcjapol(k).kolor)
                    End If
                Next
            Next
        Next
        ' jeśli na środku nie znajduje się puste pole, metoda przechodzi do 
                                                           'porównania list
        If listaPorownawcza.Count = listaKoncaGry.Count Then
            For i As Integer = 0 To listaKoncaGry.Count - 1
                If Not listaKoncaGry(i) = listaPorownawcza(i) Then
                    'listy nie są identyczne, koniec porównania
                    graWygrana = False
                    Exit For
                End If
            Next
            If graWygrana = True Then
                'listy są jednakowe
                PlanszaGlowna.Enabled = False
                MsgBox("Koniec gry, ułożyłeś układanke")
            End If
        End If
    End Sub

Tworzy ona listę porównawczą, w której przechowuje indeksy obrazków w liście listaKolorow„. Następnie porównuje je i jeśli są identyczne (wzór — środek planszy gry) kończy grę.

Oczywiście nie jest to koniec projektu, ponieważ jakoś musimy te kafelki przesuwać. Zaczniemy od klawiszy funkcyjnych. Ja użyje strzałek, lecz ty możesz wybrać inne klawisze. Musisz się zastanowić, jak będzie ci najwygodniej, jeśli przyciśniesz strzałkę w górę to który kafelek przesuniesz? Możesz przesunąć kafelek pod pustym polem w górę lub powyżej pustego pola w dół, moja konfiguracja:

    Private Sub Form1_KeyDown(sender As Object, e As KeyEventArgs) Handles MyBase.KeyDown
        'poruszanie za pomocą klawiszy, strzałek
        Dim lokalizacjaI As Integer
        Dim lokalizacjaJ As Integer
        'pobieramy lokalizację pustego pola
        For i As Integer = 0 To 4
            For j As Integer = 0 To 4
                If polaGlowneBool(i, j) = True Then
                    lokalizacjaI = i
                    lokalizacjaJ = j
                End If
            Next
        Next
        'jeśli to nie jst koniec gry, wtedy sprawdzamy który klawisz został użyty
        If PlanszaGlowna.Enabled = True Then
            If e.KeyCode = Keys.Down Then
                'klawisz w dół
                If Not lokalizacjaJ = 0 Then 'jeśli byłoby mniejsze od 0 wtedy wyszło by poza tablicę
                    For a As Integer = 0 To kolekcjapol.Count - 1
                        'pobiera lokalizację pola powyżej pola pustego
                        If lokalizacjaI = kolekcjapol(a).x And (lokalizacjaJ - 1) = kolekcjapol(a).y Then
                            ' i zmienia je na nowe
                            Dim p As polaGryS = kolekcjapol(a)
                            p.y = lokalizacjaJ
                            p.rect = polaGlowne(lokalizacjaI, lokalizacjaJ)
                            kolekcjapol(a) = p 'nadpisuje element na liście
                        End If
                    Next
                    'zmienia wartość logiczną pola
                    polaGlowneBool(lokalizacjaI, lokalizacjaJ) = False
                    polaGlowneBool(lokalizacjaI, lokalizacjaJ - 1) = True
                End If
            ElseIf e.KeyCode = Keys.Up Then
                If Not lokalizacjaJ = 4 Then 'jeśli byłoby większe od 4 wtedy wyszło by poza tablicę
                    For a As Integer = 0 To kolekcjapol.Count - 1
                        If lokalizacjaI = kolekcjapol(a).x And (lokalizacjaJ + 1) = kolekcjapol(a).y Then
                            Dim p As polaGryS = kolekcjapol(a)
                            p.y = lokalizacjaJ
                            p.rect = polaGlowne(lokalizacjaI, lokalizacjaJ)
                            kolekcjapol(a) = p
                        End If
                    Next
                    polaGlowneBool(lokalizacjaI, lokalizacjaJ) = False
                    polaGlowneBool(lokalizacjaI, lokalizacjaJ + 1) = True
                End If
            ElseIf e.KeyCode = Keys.Right Then
                If Not lokalizacjaI = 0 Then 'jeśli byłoby mniejsze od 0 wtedy wyszło by poza tablicę
                    For a As Integer = 0 To kolekcjapol.Count - 1
                        If (lokalizacjaI - 1) = kolekcjapol(a).x And lokalizacjaJ = kolekcjapol(a).y Then
                            Dim p As polaGryS = kolekcjapol(a)
                            p.x = lokalizacjaI
                            p.rect = polaGlowne(lokalizacjaI, lokalizacjaJ)
                            kolekcjapol(a) = p

                        End If
                    Next
                    polaGlowneBool(lokalizacjaI, lokalizacjaJ) = False
                    polaGlowneBool(lokalizacjaI - 1, lokalizacjaJ) = True
                End If
            ElseIf e.KeyCode = Keys.Left Then
                If Not lokalizacjaI = 4 Then 'jeśli byłoby większe od 4 wtedy wyszło by poza tablicę
                    For a As Integer = 0 To kolekcjapol.Count - 1
                        If (lokalizacjaI + 1) = kolekcjapol(a).x And lokalizacjaJ = kolekcjapol(a).y Then
                            Dim p As polaGryS = kolekcjapol(a)
                            p.x = lokalizacjaI
                            p.rect = polaGlowne(lokalizacjaI, lokalizacjaJ)
                            kolekcjapol(a) = p
                        End If
                    Next
                    polaGlowneBool(lokalizacjaI, lokalizacjaJ) = False
                    polaGlowneBool(lokalizacjaI + 1, lokalizacjaJ) = True
                End If
            End If
        End If
        'rysuje nowe kolory
        UzupelnijWzor()
    End Sub

Moje ustawienia w stosunku do pustego pola to:

Strzałka w dół

Strzałka w górę
Strzałka w lewo
Strzałka w prawo

Kod pobiera lokalizację pustego pola na podstawie tablicy „polaGlowneBool” i lokalizuje pole według wciśniętego klawisza. Następnie klonuje strukturę i zmienia jej pole x lub y i zajmowane pole (rect).

Trochę trudniej będzie z kursorem myszy. Dodajemy do panelu PlanszaGlowna zdarzenie MouseMove, kiedy kursor myszy znajdzie się nad panelem, pobrane zostaną koordynaty kursora e.x i e.y. Następnie poprzez określenie lokalizacji pustego pola przeszukujemy kolekcję kolekcjapol i sprawdzamy, czy kursor nie znajduje się nad polem (rect) po lewej, prawej, powyżej lub poniżej. Jeśli znajduje się, wtedy kursor zamieni wygląd na rączkę, jeśli nie na strzałkę.

    'kliknięcie myszą
    Private Sub PlanszaGlowna_MouseMove(sender As Object, e As MouseEventArgs) Handles PlanszaGlowna.MouseMove
        'Aby była możliwość wybrania pola za pomocą lewego przycisku myszy, należy najpierw
        'określić nad którym polem znajduje się kursor myszy
        Dim lokalizacjaI As Integer
        Dim lokalizacjaJ As Integer

        For i As Integer = 0 To 4
            For j As Integer = 0 To 4
                If polaGlowneBool(i, j) = True Then
                    lokalizacjaI = i
                    lokalizacjaJ = j
                End If
            Next
        Next

        For i As Integer = 0 To kolekcjapol.Count - 1
            If (kolekcjapol(i).rect.Location.X - 2 <= e.X And (kolekcjapol(i).rect.Location.X + kolekcjapol(i).rect.Width + 2 >= e.X)) _
                And (kolekcjapol(i).rect.Location.Y - 2 <= e.Y And
                (kolekcjapol(i).rect.Location.Y + kolekcjapol(i).rect.Height + 2 >= e.Y)) Then
                'jeśli kursor znajduje się po lewej lub po prawj stronie pustego pola, kursor zmieni rodzaj na rączkę
                If (kolekcjapol(i).x - 1 = lokalizacjaI Or kolekcjapol(i).x + 1 = lokalizacjaI) And kolekcjapol(i).y = lokalizacjaJ Then
                    Me.Cursor = Cursors.Hand
                    Exit For
                End If
                'jeśli kursor znajduje się powyżej lub poniżej stronie pustego pola, kursor zmieni rodzaj na rączkę
                If (kolekcjapol(i).y - 1 = lokalizacjaJ Or kolekcjapol(i).y + 1 = lokalizacjaJ) And kolekcjapol(i).x = lokalizacjaI Then
                    Me.Cursor = Cursors.Hand
                    Exit For
                End If
            Else
                'kursor pozostanie strzałką lub zmieni rodzaj na strzałkę jeśli nie spelnione są powyższe warunki
                Me.Cursor = Cursors.Arrow
            End If
        Next
    End Sub

Efekt taki jak na gifie poniżej:

Jak widzicie, kursor zmienia swój stan na rączkę. Jeśli chodzi o metodę kliknięcia, jest ona taka sama jak podczas użycia klawiszy. Metoda sprawdza, czy kursor jest nad polem sąsiadującym z pustym polem (kod skopiowany z MouseMove), jeśli tak to określa, który kwadrat i klonuje jego strukturę, podmienia dane z pustym polem i zapisuje w kolekcji.

    Private Sub PlanszaGlowna_MouseClick(sender As Object, e As MouseEventArgs) Handles PlanszaGlowna.MouseClick
        Dim lokalizacjaI As Integer
        Dim lokalizacjaJ As Integer
        'pobiera lokalizację pustego pola
        For i As Integer = 0 To 4
            For j As Integer = 0 To 4
                If polaGlowneBool(i, j) = True Then
                    lokalizacjaI = i
                    lokalizacjaJ = j
                End If
            Next
        Next
        'sprawdza nad którym polem jest kursor
        For i As Integer = 0 To kolekcjapol.Count - 1
            If (kolekcjapol(i).rect.Location.X - 2 <= e.X And (kolekcjapol(i).rect.Location.X +
                kolekcjapol(i).rect.Width + 2 >= e.X)) And (kolekcjapol(i).rect.Location.Y - 2 <= e.Y _
                And (kolekcjapol(i).rect.Location.Y + kolekcjapol(i).rect.Height + 2 >= e.Y)) Then
                'jeśli zostalo kliknięte pole po prawej stronie pustego pola 
                If kolekcjapol(i).x + 1 = lokalizacjaI And kolekcjapol(i).y = lokalizacjaJ Then
                    'pobierze strukture pola z listy
                    Dim p As polaGryS = kolekcjapol(i)
                    ' zmieni jego  lokalizację x
                    p.x = lokalizacjaI
                    'przypisze nowy kwadrat
                    p.rect = polaGlowne(lokalizacjaI, lokalizacjaJ)
                    'nadpisze nową strukture w liście
                    kolekcjapol(i) = p
                    'ustawi nowe miejsce true i false
                    polaGlowneBool(lokalizacjaI, lokalizacjaJ) = False
                    polaGlowneBool(lokalizacjaI - 1, lokalizacjaJ) = True
                End If
                'jeśli zostalo kliknięte pole po lewej stronie pustego pola 
                If kolekcjapol(i).x - 1 = lokalizacjaI And kolekcjapol(i).y = lokalizacjaJ Then
                    Dim p As polaGryS = kolekcjapol(i)
                    p.x = lokalizacjaI
                    p.rect = polaGlowne(lokalizacjaI, lokalizacjaJ)
                    kolekcjapol(i) = p
                    polaGlowneBool(lokalizacjaI, lokalizacjaJ) = False
                    polaGlowneBool(lokalizacjaI + 1, lokalizacjaJ) = True
                End If
                'jeśli zostalo kliknięte pole jest powyżej pustego pola 
                If kolekcjapol(i).y - 1 = lokalizacjaJ And kolekcjapol(i).x = lokalizacjaI Then
                    Dim p As polaGryS = kolekcjapol(i)
                    p.y = lokalizacjaJ
                    p.rect = polaGlowne(lokalizacjaI, lokalizacjaJ)
                    kolekcjapol(i) = p

                    polaGlowneBool(lokalizacjaI, lokalizacjaJ) = False
                    polaGlowneBool(lokalizacjaI, lokalizacjaJ + 1) = True
                End If
                'jeśli zostalo kliknięte pole jest poniżej pustego pola 
                If kolekcjapol(i).y + 1 = lokalizacjaJ And kolekcjapol(i).x = lokalizacjaI Then
                    Dim p As polaGryS = kolekcjapol(i)
                    p.y = lokalizacjaJ
                    p.rect = polaGlowne(lokalizacjaI, lokalizacjaJ)
                    kolekcjapol(i) = p
                    polaGlowneBool(lokalizacjaI, lokalizacjaJ) = False
                    polaGlowneBool(lokalizacjaI, lokalizacjaJ - 1) = True
                End If
            End If
        Next
        UzupelnijWzor()
    End Sub

Dobra gra gotowa:

Teraz wasza kolej, twórzcie dodawajcie i modyfikujcie ile chcecie. Stwórzcie grę taką jaką chcecie aby była.

Pełen projekt do pobrania tutaj: MagicznaUkladanka.zip

English: Magic_blocks.zip

 

Permalink do tego artykułu: https://visualmonsters.cba.pl/magiczna-ukladanka/

Dodaj komentarz

Twój adres email nie będzie publikowany.