Dzisiaj zrobimy sobie program do układania puzzli, ale aby to zrobić musimy mieć puzzle. Nie będziemy robić programu do dzielenia obrazka na puzzle, gdyż byłoby to bardzo skomplikowane. Dużo łatwiej i estetyczniej jest wyciąć sobie je samemu przy użyciu Inkscape i Gimpa. Aby to zrobić, należy oczywiście zainstalować oba programy i dodać do nich rozszerzenia:
Inkscape: https://github.com/Neon22/inkscape-jigsaw
Gimp: https://github.com/devolonter/gimp-cut-to-pieces
Nie będę się tutaj rozwijał w temacie, wszystko co i jak prezentuje na filmie poniżej:
Działanie programu który będziemy robić prezentuje na tym filmie:
Program który ja przygotowałem prezentuje poniżej:

Puzzelek 1.0
Kiedy potniemy już sobie puzzle, przyszedł czas na utworzenie Formy do naszego projektu:

| Rodzaj elementu | Nazwa elementu | Ustawienia |
|---|---|---|
| Form | Form1 | Name: Form1 Text: Puzzle VisualMonsters.cba.pl Size: 642; 475 WindowState: Maximized BackColor: White BackgroungImageLayout: CenterDubleBuffer: True |
| Panel | Panel1 | Name: Panel1 Anchor: Bottom, Right Size: 187; 52 BorderStyle: FixedSingle |
| TrackBar | TrackBar1 | Name: TrackBar1 Dock: Fill Maximum: 100 Value: 10 |
Należy teraz umieścić nasze puzzle w programie (jeśli nie chciało wam się robić puzzli, możecie pobrać je tutaj: iron-man, statek, South Park). Robimy to tak jak na gifie poniżej:

Program, nie będzie miał bardzo skomplikowanych ustawień, tak więc obracanie puzzli czy zmiana ich rozmiaru pozostawiam wam. Nie będziemy się też za bardzo skupiać nad komplikowaniem tego projektu. Tutorial ma za zadanie nauczyć użytkownika manipulować elementami graficznymi. Zaczynamy od dodania elementów puzzli z zasobów projektu i od deklaracji zmiennych:
Public Class Form1
Public OffsetX As New List(Of Integer)
Public OffsetY As New List(Of Integer)
Public WszystkiePuzzle As New List(Of Bitmap)
Public LokalizacjaPuzzla As New List(Of Rectangle)
Private Trzymany As Integer = 0
Public TrzymanyIndex As New List(Of Boolean)
Public TrzymanyIndexPrecyzyjny As New List(Of Integer) 'wykorzystamy go do precyzyjnego dopasowania elementów
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
'Me.BackgroundImage = ZmienKrycie(My.Resources.sp, 0.1)
Dim ran As New Random
'przeszukujemy specjalną lokalizację naszego projektu, w poszukiwaniu obrazków
For Each ResourceFile As DictionaryEntry In My.Resources.ResourceManager.GetResourceSet(Globalization.CultureInfo.CurrentCulture, True, True).OfType(Of Object)()
'jeśli natrafimy na obrazek
If TypeOf (ResourceFile.Value) Is Image Then
'nie jest on obrazkiem złożonym sp.bmp (tym podglądowym)
If Not ResourceFile.Key.ToString.Contains("sp") Then
Dim imaeege As Image = ResourceFile.Value
Dim bm_source As New Bitmap(imaeege)
'uzupełniamy listę o tą bitmapę
WszystkiePuzzle.Add(bm_source)
'ustawiamy położenie startowe puzzli
'każdy puzel zostanie rozsypany losowo
OffsetX.Add(ran.Next(0, Me.Width - 2 * bm_source.Width))
OffsetY.Add(ran.Next(0, Me.Height - 2 * bm_source.Height))
'nie trzymamy jeszcze żadnych puzli, więc listę wypełniamy 'False'
TrzymanyIndex.Add(False)
End If
End If
Next
'Nie chcemy aby wielkość formy była mniejsza niż nasz obrazek, dlatego dodamy sobie takie ograniczenie
Me.MinimumSize = New Size(My.Resources.sp.Width + 50, My.Resources.sp.Height + 50)
'Tworzymy figury o określonej lokalizacji (X,Y) i wielkości obrazka
For i As Integer = 0 To WszystkiePuzzle.Count - 1
LokalizacjaPuzzla.Add(New Rectangle(OffsetX(i), OffsetY(i), WszystkiePuzzle(i).Width, WszystkiePuzzle(i).Height))
Next
End Sub
Private Sub Form1_Paint(ByVal sender As Object, ByVal e As System.Windows.Forms.PaintEventArgs) Handles Me.Paint
'Tworzy grafikę na formie głównej, wywoływany jest gdy zmianie ulega jakiś element
For i As Integer = 0 To WszystkiePuzzle.Count - 1
If TrzymanyIndexPrecyzyjny.Contains(i) Then
e.Graphics.FillRectangle(New SolidBrush(Color.FromArgb(50, 255, 0, 0)), LokalizacjaPuzzla(i))
End If
e.Graphics.DrawImage(WszystkiePuzzle(i), LokalizacjaPuzzla(i))
Next
End Sub
End Class
Oczywiście efektem będzie wyświetlenie puzzli na naszej formie, zajmiemy się teraz etapem ogólnego przesuwania elementów. Będzie się ona składał z trzech zdarzeń:
-
MouseMove
– to zdarzenie monitoruje nam na bieżąco położenie kursora nad formą, jeśli natrafi na nasz obiekt (co ważne funkcja KursorJestNadObiektem zwróci true, a zwróci tylko wtedy gry pixel, nad którym znajduje się kursor, nie jest przezroczysty) wtedy pobiera jego index i zmienia kursor na łapkę:
Oczywiście w chwili złapania obiektu, zdarzenie MouseMove odpowiada również za zmianę lokalizacji obiektu, ale metoda ta nie jest skomplikowana i polega na zmianie parametrów w liście LokalizacjaPuzzla. -
MouseDown
– Ustawia Offset, który jest wykorzystywany w przesuwaniu elementu/ów. Dział ten podzieliłem na trzy części.
- Pierwszy to przesuwanie tylko jednego elementu odbywa się to za pomocą lewego przycisku myszy:

- Drugi to przesuwanie wskazanego elementu i elementów z nim graniczących:

- Trzeci etap to przesuwanie wszystkich stykających się ze sobą elementów:

- Pierwszy to przesuwanie tylko jednego elementu odbywa się to za pomocą lewego przycisku myszy:
-
MouseUp
– etap końcowy zmienia opcje TrzymanyIndex na false, dla trzymanych obiektów sprawdza, czy elementy nie zostały przesunięte poza układany obszar (dzięki niemu nie zgubimy naszych puzzli :P):

Wiemy jak to będzie działać, pełen kod wygląda tak:
'Pubiera index elementu nad którym znajduje się kursor i zmienia położenie elementu jeśli jest trzymany
Private Sub Form1_MouseMove(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles Me.MouseMove
For i As Integer = 0 To WszystkiePuzzle.Count - 1
If TrzymanyIndex(i) = True Then
LokalizacjaPuzzla(i) = New Rectangle(e.X + OffsetX(i), e.Y + OffsetY(i), WszystkiePuzzle(i).Width, WszystkiePuzzle(i).Height)
Me.Invalidate() 'powoduje ponowne malowanie obiektów
Else
Dim new_cursor As Cursor = Cursors.Default
For d As Integer = 0 To WszystkiePuzzle.Count - 1
If (KursorJestNadObiektem(e.X, e.Y, d)) Then
new_cursor = Cursors.Hand
'ustawia index obiektu nad którym jest kursor
Trzymany = d
End If
Next
If (Me.Cursor <> new_cursor) Then
Me.Cursor = new_cursor
End If
End If
Next
End Sub
Private Function KursorJestNadObiektem(ByVal x As Integer, ByVal y As Integer, ByVal k As Integer) As Boolean
If ((x < LokalizacjaPuzzla(k).Left) OrElse
(y < LokalizacjaPuzzla(k).Top) OrElse
(x >= LokalizacjaPuzzla(k).Right) _
OrElse (y >= LokalizacjaPuzzla(k).Bottom)) _
Then Return False
Dim i As Integer = x - LokalizacjaPuzzla(k).X
Dim j As Integer = y - LokalizacjaPuzzla(k).Y
Return (WszystkiePuzzle(k).GetPixel(i, j).A > 0)
End Function
Private Sub Form1_MouseDown(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles Me.MouseDown
TrzymanyIndexPrecyzyjny.Clear()
Panel1.Enabled = False
'Warunek sprawdza który klawisz myszy jest trzymany
If e.Button = MouseButtons.Left Then
'Trzymanie jednego elementu
'Funkcja sprawdza czy kursor myszy nadal jest nad obiektem
If (KursorJestNadObiektem(e.X, e.Y, Trzymany)) Then
TrzymanyIndex(Trzymany) = True
'elementy wykorzystywane do zmiany położenia puzzla
OffsetX(Trzymany) = LokalizacjaPuzzla(Trzymany).X - e.X
OffsetY(Trzymany) = LokalizacjaPuzzla(Trzymany).Y - e.Y
'lista wykorzystywana na późniejszym etapie do precyzyjnego ustawienia położenia
TrzymanyIndexPrecyzyjny.Add(Trzymany)
End If
Else
'trzymanie wielu elementów
Dim listaPolaczonych As New List(Of Integer) 'przechowuje elementy wielu połączonych puzzli
'Funkcja sprawdza czy kursor myszy nadal jest nad obiektem
If (KursorJestNadObiektem(e.X, e.Y, Trzymany)) Then
'dodaje pierwszy index trzymanego obiektu
TrzymanyIndex(Trzymany) = True
'elementy wykorzystywane do zmiany położenia puzzla
OffsetX(Trzymany) = LokalizacjaPuzzla(Trzymany).X - e.X
OffsetY(Trzymany) = LokalizacjaPuzzla(Trzymany).Y - e.Y
' dwie listy indexów, jedna wykorzystywana do przenoszenia bardzo wielu połączonych puzzli
listaPolaczonych.Add(Trzymany)
TrzymanyIndexPrecyzyjny.Add(Trzymany)
For Each el As Rectangle In LokalizacjaPuzzla
'Warunek sprawdza czy w obrębie puzla znajdują się inne puzle które można by przesunąć (z dokładnością 5%)
If (el.Location.X >= (LokalizacjaPuzzla(Trzymany).Location.X - 0.05 * LokalizacjaPuzzla(Trzymany).Width) And el.Location.X <= (LokalizacjaPuzzla(Trzymany).Location.X + LokalizacjaPuzzla(Trzymany).Width + 0.05 * LokalizacjaPuzzla(Trzymany).Width) Or
el.Location.X + el.Width >= (LokalizacjaPuzzla(Trzymany).Location.X - 0.05 * LokalizacjaPuzzla(Trzymany).Width) And el.Location.X <= (LokalizacjaPuzzla(Trzymany).Location.X + LokalizacjaPuzzla(Trzymany).Width + 0.05 * LokalizacjaPuzzla(Trzymany).Width)) And
(el.Location.Y >= (LokalizacjaPuzzla(Trzymany).Location.Y - 0.05 * LokalizacjaPuzzla(Trzymany).Height) And el.Location.Y <= (LokalizacjaPuzzla(Trzymany).Location.Y + LokalizacjaPuzzla(Trzymany).Height + 0.05 * LokalizacjaPuzzla(Trzymany).Height) Or
el.Location.Y + el.Height >= (LokalizacjaPuzzla(Trzymany).Location.Y - 0.05 * LokalizacjaPuzzla(Trzymany).Height) And el.Location.Y <= (LokalizacjaPuzzla(Trzymany).Location.Y + LokalizacjaPuzzla(Trzymany).Height + 0.05 * LokalizacjaPuzzla(Trzymany).Height)) Then
'Jeśli znajdują się tam jakieś puzzle wtedy dodajemy ich indexy do list aby była możliwość
'wyróżnienia ich w następnych krokach
If Not TrzymanyIndexPrecyzyjny.Contains(LokalizacjaPuzzla.IndexOf(el)) Then
TrzymanyIndexPrecyzyjny.Add(LokalizacjaPuzzla.IndexOf(el))
TrzymanyIndex(LokalizacjaPuzzla.IndexOf(el)) = True
'ustawiamy offset
OffsetX(LokalizacjaPuzzla.IndexOf(el)) = LokalizacjaPuzzla(LokalizacjaPuzzla.IndexOf(el)).X - e.X
OffsetY(LokalizacjaPuzzla.IndexOf(el)) = LokalizacjaPuzzla(LokalizacjaPuzzla.IndexOf(el)).Y - e.Y
End If
End If
Next
'Jeśli przytrzymamy Ctrl na klawiaturze, podczas trzymania lewego przycisku myszy
'będziemy mogli przesunąć nietylko elementy zawierające się w obrębie jednego puzzla,
'będziemy mogli przesunąć wszystkie puzzle do siebie przylegające
If My.Computer.Keyboard.CtrlKeyDown Then
'pętla strawdza warunek dla wszystkich elementów z listy dodatkowej, jeśli do elementów na liście należą inne elementy na liście się nie znajdującej, wtedy dodawane są do listy
Do
Dim przerwijPetle As Boolean = True 'warunek kończący pętle
For i As Integer = 0 To TrzymanyIndex.Count - 1
If TrzymanyIndex(i) = True Then
If Not listaPolaczonych.Contains(i) Then
listaPolaczonych.Add(i)
przerwijPetle = False
For Each el As Rectangle In LokalizacjaPuzzla
If (el.Location.X >= (LokalizacjaPuzzla(i).Location.X - 0.05 * LokalizacjaPuzzla(i).Width) And el.Location.X <= (LokalizacjaPuzzla(i).Location.X + LokalizacjaPuzzla(i).Width + 0.05 * LokalizacjaPuzzla(i).Width) Or
el.Location.X + el.Width >= (LokalizacjaPuzzla(i).Location.X - 0.05 * LokalizacjaPuzzla(i).Width) And el.Location.X <= (LokalizacjaPuzzla(i).Location.X + LokalizacjaPuzzla(i).Width + 0.05 * LokalizacjaPuzzla(i).Width)) And
(el.Location.Y >= (LokalizacjaPuzzla(i).Location.Y - 0.05 * LokalizacjaPuzzla(i).Height) And el.Location.Y <= (LokalizacjaPuzzla(i).Location.Y + LokalizacjaPuzzla(i).Height + 0.05 * LokalizacjaPuzzla(i).Height) Or
el.Location.Y + el.Height >= (LokalizacjaPuzzla(i).Location.Y - 0.05 * LokalizacjaPuzzla(i).Height) And el.Location.Y <= (LokalizacjaPuzzla(i).Location.Y + LokalizacjaPuzzla(i).Height + 0.05 * LokalizacjaPuzzla(i).Height)) Then
If Not TrzymanyIndexPrecyzyjny.Contains(LokalizacjaPuzzla.IndexOf(el)) Then
TrzymanyIndexPrecyzyjny.Add(LokalizacjaPuzzla.IndexOf(el))
TrzymanyIndex(LokalizacjaPuzzla.IndexOf(el)) = True
OffsetX(LokalizacjaPuzzla.IndexOf(el)) = LokalizacjaPuzzla(LokalizacjaPuzzla.IndexOf(el)).X - e.X
OffsetY(LokalizacjaPuzzla.IndexOf(el)) = LokalizacjaPuzzla(LokalizacjaPuzzla.IndexOf(el)).Y - e.Y
End If
End If
Next
End If
End If
Next
'pętla kończy się gdy nie ma już co dodać
If przerwijPetle = True Then
Exit Do
End If
Loop
End If
End If
End If
End Sub
'Upuszczenie puzla
Private Sub Form1_MouseUp(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles Me.MouseUp
Panel1.Enabled = True
For i As Integer = 0 To WszystkiePuzzle.Count - 1
'Nie chcemy aby nasze puzzle wylądowały poza obszarem gry, dlatego wprowadzimy sobie dodatkowe ustawienia
If LokalizacjaPuzzla(i).Location.X + WszystkiePuzzle(i).Width > Me.Width Then
LokalizacjaPuzzla(i) = New Rectangle(Me.Width - WszystkiePuzzle(i).Width, LokalizacjaPuzzla(i).Location.Y, WszystkiePuzzle(i).Width, WszystkiePuzzle(i).Height)
End If
If LokalizacjaPuzzla(i).Location.Y + WszystkiePuzzle(i).Height > Me.Height Then
LokalizacjaPuzzla(i) = New Rectangle(LokalizacjaPuzzla(i).Location.X, Me.Height - WszystkiePuzzle(i).Height, WszystkiePuzzle(i).Width, WszystkiePuzzle(i).Height)
End If
If LokalizacjaPuzzla(i).Location.X < 0 Then
LokalizacjaPuzzla(i) = New Rectangle(0, LokalizacjaPuzzla(i).Location.Y, WszystkiePuzzle(i).Width, WszystkiePuzzle(i).Height)
End If
If LokalizacjaPuzzla(i).Location.Y < 0 Then
LokalizacjaPuzzla(i) = New Rectangle(LokalizacjaPuzzla(i).Location.X, 0, WszystkiePuzzle(i).Width, WszystkiePuzzle(i).Height)
End If
TrzymanyIndex(i) = False
Next
TrzymanyIndexPrecyzyjny.Clear()
Me.Invalidate()
End Sub
Ostatnim etapem jest doprecyzowanie elementów. Często jest tak, że dopasowanie elementów do siebie za pomocą myszy, może nam sprawić dość duży kłopot. Spróbujcie ruszyć tak myszą, aby przesunąć ją o 1px w lewo bądź w prawo. Ciężkie zadanie, więc wprowadziłem taką oto metodę. Gdy element lub elementy są trzymane, ich index dodawany jest do listy „TrzymanyIndexPrecyzyjny”, który pozwoli lokalizacji tych elementów o 1px zgodnie z przyciśniętym klawiszem strzałki:

Kod do tego elementu jest bardzo prosty:
'Aby ułatwić użytkownikowi kontrolę położenia puzla dodamy sobie możliwość jego dokładnej kontroli
'za pośrednictwem klawiszsy strzałek na klawiaturze
Private Sub main_KeyDown(ByVal sender As System.Object, ByVal e As System.Windows.Forms.KeyEventArgs) Handles MyBase.KeyDown
Select Case e.KeyCode
Case Keys.Up
For i As Integer = 0 To TrzymanyIndexPrecyzyjny.Count - 1
Dim moveX As Integer = LokalizacjaPuzzla(TrzymanyIndexPrecyzyjny(i)).Location.X
Dim movey As Integer = LokalizacjaPuzzla(TrzymanyIndexPrecyzyjny(i)).Location.Y - 1
LokalizacjaPuzzla(TrzymanyIndexPrecyzyjny(i)) = New Rectangle(moveX, movey, WszystkiePuzzle(TrzymanyIndexPrecyzyjny(i)).Width, WszystkiePuzzle(TrzymanyIndexPrecyzyjny(i)).Height)
Next
Case Keys.Left
For i As Integer = 0 To TrzymanyIndexPrecyzyjny.Count - 1
Dim moveX As Integer = LokalizacjaPuzzla(TrzymanyIndexPrecyzyjny(i)).Location.X - 1
Dim movey As Integer = LokalizacjaPuzzla(TrzymanyIndexPrecyzyjny(i)).Location.Y
LokalizacjaPuzzla(TrzymanyIndexPrecyzyjny(i)) = New Rectangle(moveX, movey, WszystkiePuzzle(TrzymanyIndexPrecyzyjny(i)).Width, WszystkiePuzzle(TrzymanyIndexPrecyzyjny(i)).Height)
Next
Case Keys.Right
For i As Integer = 0 To TrzymanyIndexPrecyzyjny.Count - 1
Dim moveX As Integer = LokalizacjaPuzzla(TrzymanyIndexPrecyzyjny(i)).Location.X + 1
Dim movey As Integer = LokalizacjaPuzzla(TrzymanyIndexPrecyzyjny(i)).Location.Y
LokalizacjaPuzzla(TrzymanyIndexPrecyzyjny(i)) = New Rectangle(moveX, movey, WszystkiePuzzle(TrzymanyIndexPrecyzyjny(i)).Width, WszystkiePuzzle(TrzymanyIndexPrecyzyjny(i)).Height)
Next
Case Keys.Down
For i As Integer = 0 To TrzymanyIndexPrecyzyjny.Count - 1
Dim moveX As Integer = LokalizacjaPuzzla(TrzymanyIndexPrecyzyjny(i)).Location.X
Dim movey As Integer = LokalizacjaPuzzla(TrzymanyIndexPrecyzyjny(i)).Location.Y + 1
LokalizacjaPuzzla(TrzymanyIndexPrecyzyjny(i)) = New Rectangle(moveX, movey, WszystkiePuzzle(TrzymanyIndexPrecyzyjny(i)).Width, WszystkiePuzzle(TrzymanyIndexPrecyzyjny(i)).Height)
Next
End Select
Me.Invalidate()
End Sub
Pozostał ostatni element, kiedy układamy puzzle mamy na pudełku docelowy składany obrazek. Musimy wiedzieć, co układamy, tak samo będzie i w naszym przypadku. Opcja zablokowana w Form_Load i TrackBar będą nam kontrolować przezroczystość obrazka docelowego tak, aby podpowiedz, nie była oczywista i nie zasłaniała nam naszych puzzli:
Odblokuj w Form_Load:
Me.BackgroundImage = ZmienKrycie(My.Resources.sp, 0.1)
Kod kontrolujący krycie tła:
Private Sub TrackBar1_Scroll(sender As Object, e As EventArgs) Handles TrackBar1.Scroll
Me.BackgroundImage = ZmienKrycie(My.Resources.sp, TrackBar1.Value / 100)
End Sub
Public Shared Function ZmienKrycie(ByVal img As Image, ByVal Krycie As Single) As Bitmap
Dim bmp As New Bitmap(img.Width, img.Height)
Dim graphics__1 As Graphics = Graphics.FromImage(bmp)
Dim colormatrix As New ColorMatrix
colormatrix.Matrix33 = Krycie
Dim imgAttribute As New ImageAttributes
imgAttribute.SetColorMatrix(colormatrix, ColorMatrixFlag.[Default], ColorAdjustType.Bitmap)
If Krycie = 1 Then
graphics__1.FillRectangle(New SolidBrush(Color.Red), 0, 0, img.Width, img.Height)
Else
graphics__1.DrawImage(img, New Rectangle(0, 0, bmp.Width, bmp.Height), 0, 0, img.Width, img.Height, GraphicsUnit.Pixel, imgAttribute)
End If
graphics__1.Dispose()
Return bmp
End Function
Efekt prezentuje poniżej:

Zauważyć można, że gdy krycie jest równe 1, wtedy tło robi się całe czerwone, jest to zabieg celowy pomagający stwierdzić czy elementy puzzli są dobrze dopasowane (można go usunąć, jeśli uznacie, że jest niepotrzebny lub zmienić mu kolor z czerwonego na czarny, lub biały).

Jeśli chodzi o aplikacje, to jest gotowa do działania. Do pobrania:
Pełen projekt: Puzzle_VisualMonsters.cba.pl.zip
Sam kod źródłowy: Puzzle VisualMonsters.cba.pl.txt
Podpowiedzi:
Obracanie puzzli o określony kąt
Jeśli jesteście zainteresowani rozwojem tej aplikacji, na pewno wpadniecie na pomysł, aby obrócić puzzle o jakiś losowy kąt. Nie jest to takie proste, ponieważ będziemy musieli przerzucić się z „Rectangli” na listę punktów dokładniej czterech, które będą określały położenie obróconego elementu i dodać nową listę integer (Private KatNachylenia As New List(Of Integer)) którą, wypełnimy na początku albo wartością 0 lub losową z przedziału od 0 do 360. Jeśli chcecie sprawdzić, w jaki to działa sposób, zmieńcie metodę Form1_Paint:
Private Sub Form1_Paint(ByVal sender As Object, ByVal e As System.Windows.Forms.PaintEventArgs) Handles Me.Paint
'Tworzy grafikę na formie głównej, wywoływany jest gdy zmianie ulega jakiś element
For i As Integer = 0 To WszystkiePuzzle.Count - 1
Dim ang As Integer = KatNachylenia(i)
Dim W As Integer = WszystkiePuzzle(i).Width
Dim H As Integer = WszystkiePuzzle(i).Height
Dim m As Matrix = New Matrix
m.RotateAt(ang, New Point(LokalizacjaPuzzla(i).Location.X, LokalizacjaPuzzla(i).Location.Y))
e.Graphics.Transform = m
e.Graphics.DrawImage(WszystkiePuzzle(i), LokalizacjaPuzzla(i))
If TrzymanyIndexPrecyzyjny.Contains(i) Then
e.Graphics.FillRectangle(New SolidBrush(Color.FromArgb(50, 255, 0, 0)), LokalizacjaPuzzla(i))
End If
Next
End Sub
Należy też dodać jakiś bodziec, który będzie zmieniał kąt nachylenia naszego obrazka, w moim wypadku jest to scroll na myszce:
Private Sub Form1_MouseWheel(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles Me.MouseWheel
If e.Delta > 0 Then
For i As Integer = 0 To TrzymanyIndexPrecyzyjny.Count - 1
KatNachylenia(TrzymanyIndexPrecyzyjny(i)) = (KatNachylenia(TrzymanyIndexPrecyzyjny(i)) + 1) Mod 360
Next
Else
For i As Integer = 0 To TrzymanyIndexPrecyzyjny.Count - 1
KatNachylenia(TrzymanyIndexPrecyzyjny(i)) = (KatNachylenia(TrzymanyIndexPrecyzyjny(i)) - 1)
Next
End If
Me.Invalidate()
End Sub
Wygląda to bardzo dobrze:

Niestety trzeba bardzo się namęczyć, aby dopracować ten element, ponieważ należy zastąpić „Rectangle” tablicą punktów. Łatwiejszą metodą jest obrót tylko o 90 stopni, taką metodę możemy w całości zamontować w zdarzeniu MouseWheel:

Ja w swoim programie wykorzystałem właśnie tę metodę, ponieważ jest łatwa do zaimplementowania i nie wymaga dużo klikania.
Dim obrot As Boolean = False
Private Sub Form1_MouseWheel(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles Me.MouseWheel
If e.Delta > 0 Then
For i As Integer = 0 To TrzymanyIndexPrecyzyjny.Count - 1
If obrot = False Then
Dim Obraz As New Bitmap(WszystkiePuzzle(TrzymanyIndexPrecyzyjny(i)))
Dim p As Point = New Point(LokalizacjaPuzzla(TrzymanyIndexPrecyzyjny(i)).Location.X, LokalizacjaPuzzla(TrzymanyIndexPrecyzyjny(i)).Location.Y)
LokalizacjaPuzzla(TrzymanyIndexPrecyzyjny(i)) = New Rectangle(p.X, p.Y, Obraz.Height, Obraz.Width)
Obraz.RotateFlip(RotateFlipType.Rotate90FlipY)
WszystkiePuzzle(TrzymanyIndexPrecyzyjny(i)) = Obraz
obrot = True
Else
Dim Obraz As New Bitmap(WszystkiePuzzle(TrzymanyIndexPrecyzyjny(i)))
Dim p As Point = New Point(LokalizacjaPuzzla(TrzymanyIndexPrecyzyjny(i)).Location.X, LokalizacjaPuzzla(TrzymanyIndexPrecyzyjny(i)).Location.Y)
LokalizacjaPuzzla(TrzymanyIndexPrecyzyjny(i)) = New Rectangle(p.X, p.Y, Obraz.Height, Obraz.Width)
Obraz.RotateFlip(RotateFlipType.Rotate90FlipX)
WszystkiePuzzle(TrzymanyIndexPrecyzyjny(i)) = Obraz
obrot = False
End If
Next
Else
For i As Integer = 0 To TrzymanyIndexPrecyzyjny.Count - 1
If obrot = False Then
Dim Obraz As New Bitmap(WszystkiePuzzle(TrzymanyIndexPrecyzyjny(i)))
Dim p As Point = New Point(LokalizacjaPuzzla(TrzymanyIndexPrecyzyjny(i)).Location.X, LokalizacjaPuzzla(TrzymanyIndexPrecyzyjny(i)).Location.Y)
LokalizacjaPuzzla(TrzymanyIndexPrecyzyjny(i)) = New Rectangle(p.X, p.Y, Obraz.Height, Obraz.Width)
Obraz.RotateFlip(RotateFlipType.Rotate90FlipX)
WszystkiePuzzle(TrzymanyIndexPrecyzyjny(i)) = Obraz
obrot = True
Else
Dim Obraz As New Bitmap(WszystkiePuzzle(TrzymanyIndexPrecyzyjny(i)))
Dim p As Point = New Point(LokalizacjaPuzzla(TrzymanyIndexPrecyzyjny(i)).Location.X, LokalizacjaPuzzla(TrzymanyIndexPrecyzyjny(i)).Location.Y)
LokalizacjaPuzzla(TrzymanyIndexPrecyzyjny(i)) = New Rectangle(p.X, p.Y, Obraz.Height, Obraz.Width)
Obraz.RotateFlip(RotateFlipType.Rotate90FlipY)
WszystkiePuzzle(TrzymanyIndexPrecyzyjny(i)) = Obraz
obrot = False
End If
Next
End If
Me.Invalidate()
End Sub
Jeśli macie jakieś pomysły lub pytania, pytajcie a może coś dodamy do tutoriala

