shape & shape : tangram puzzle game

Aplikacja do przetestowania: Tangram

Czym jest tangram? Ja pierwszy raz jak zobaczyłem tą prostą grę stwierdziłem, że to gra dla dzieci i nic w niej skomplikowanego nie ma. Trochę się pomyliłem w tych ocenach i podczas tworzenia tego małego projektu, rozrósł się on do dużego projektu. Przeszkody na które natrafiłem musiałem jakoś przeskoczyć a gdzieniegdzie wymagało to ode mnie dużego kombinowania i pomysłowości. Gra będzie wyglądała tak jak na gifie:

Prawym przyciskiem myszy, będziemy obracać nasze obiekty. Zaczynamy więc od stworzenia formy:

Rodzaj elementu Nazwa elementu Ustawienia
Form Form1 Name: Form1
Text: shape & shape : tangrampuzzle gameCSV
Size: 1091; 808
DoubleBuffered:True
Icon: Sha&sha.ico
Panel Panel3 Name: Panel3
Size: 100;100
Location: 973; 560
Anchor: Bottom, Right
Panel Panel4 Name: Panel3
Size: 100;100
Location: 973; 666
Anchor: Bottom, Right
Timer Timer1 Name: Timer1
Panel Panel2 Name: Panel2
Size: 450; 241
Location: 623; 7
Anchor: top, Right

Poniżej opis ułożenia elementów w panelu 2, który będzie zawierał przyciski i wzór do wykonania.

 
Rodzaj elementu Nazwa elementu Ustawienia
Panel Panel1 Name: Panel1
Size: 240; 240
Location: 0;0
BackColor: Turquoise
Button Button1 Name: Panel3
Size: 201; 23
Location: 246; 168
Text: Skopiuj wzór
Button Button2 Name: Button2
Size: 1201; 46
Location: 246; 5
Font:Microsoft Sans Serif;
15,75pt
Text: Start / Losuj
Button Button3 Name: Button3
Size: 201; 41
Location: 246; 197
Font: Microsoft Sans Serif; 12pt 15,75pt
Text: Poddaje się
Button Button4 Name: Button4
Size: 201; 23
Location: 246; 57
Text: Resetuj
Label Label1 Name: Label1
Size: 149; 39
Location: 278; 100
BackColor: White
BorderStyle: FixedSingle
Font: Microsoft Sans Serif; 14,25pt
TextAlign: MiddleCenter

Reszta elementów, będzie tworzona w sposób dynamiczny. Nasza gra będzie się składać z Formy, modułu i jednej klasy:

  • Form1 – forma
  • GenerujKsztalty – klasa
  • PubliczneElementy – moduł

Czy taka hierarchia jest potrzebna? Oczywiście nie, wszystko można wrzucić do jednej formy i też będzie działać ale tak jest bardziej schludnie i przejrzyście.

Klasa GenerujKsztalty będzie tworzyć nam nasze elementy (głównie bitmapy) i strukturę a moduł PubliczneElementy będzie przechowywał listę wzorów i naszą strukturę. Zaczniemy od przygotowania planszy, to na niej odbywać będzie się gra i nie jest powiązana z innymi elementami, po prostu generuje obrazek tła formy dlatego ją dodamy sobie na początku. Kiedy forma jest już przygotowana, przechodzimy do jej kodu (F7).

Co tu się dzieje i co jest co? Po pierwsze, trzeba tutaj napomknąć, że jest to zwykłe malowanie/dodawanie grafiki do/na bitmapę. Najpierw inicjujemy bitmapę, w tym wypadku jest to zmienna „tapeta” na którą nanosimy elementy graficzne przy użyciu zmiennej „g”, na samym końcu ustawiamy bitmapę „tapeta” jako tło formy. Tutaj na szczególną uwagę zasługuje utworzona tablica obiektów „Rectangle”:

Posłuży nam ona do równania położonych obiektów. Aby to zobaczyć, odblokujcie elementy w 57 linijce:

teraz na naszą formę nałożone zostaną przezroczyste panele:

Pod tymi panelami, są obiekty „Rectangle” które będziemy wykorzystywać do centrowania położonych elementów. Spójżcie na obrazek poniżej, przedstawia on sytuacje w której trzymany jest obiekt 1 (trzymane obiekty podświetlają się na czarno). Ciężko jest umieścić go tak aby był na odpowiednim miejscu, co do pixela.

Punkt (0,0) naszego trójkąta jest na obszarze żółtego kwadratu, i to do tego kwadratu nastąpi wyrównanie. Po położeniu obiektu, zostanie on wyrównany do środka tego kwadratu.

To zapewni nam, że ostateczna forma będzie zwarta i będzie tworzyć całość. Potrzebne będzie to do końcowego porównania. Kiedy mamy już gotowe te elementy, odblokujcie element „PrzygotujWzory()”:

Przechodzimy sobie teraz do naszego Modułu „PubliczneElement”

Struktura to po prostu nasz nowy obiekt składający się ze zmiennych. Tutaj na uwagę zasługuje element tworzący listę wzorów. Każdy wzór, który musimy ułożyć posiada listę elementów, ich położenie (współrzędne) i wartość obrotu w prawo.

Potrzeba aż tylu elementów, ponieważ nasz zbiór będzie również przechowywał potencjalny sposób ułożenia elementów:

Teraz zajmiemy się początkowym ułożeniem elementów, czyli generowaniem kształtów do układania. Odblokujcie:

Obiekty będziemy dzielić na dwie grupy, czerwone i turkusowe. Czerwonych jest 6 a turkusowych 8. Turkusowe będą miały wartość od 1-4 a czerwone od 11-13. Aby móc namalować obiekt należy stworzyć dwa elementy, kształt (Rectangle) i obrazek (Bitmap). Wszystko to przygotujemy ręcznie. Każdego elementu jest po dwie sztuki. Gotowe elementy będziemy przechowywać na liście „kolekcjaElementow”.

Teraz możemy wrócić do kodu Formy1. Mimo tego, że utworzyliśmy kształty, one nie pojawią się na naszej formie, jeśli nie zostaną do niej dodane. Nasza forma, musi mieć aktywne zdarzenie Paint:

Kod tego zdarzenia:

Nasza forma ma teraz dodane kształty którymi będziemy poruszać:

Teraz zajmiemy się przesuwaniem obiektów. Interesują nas tutaj trzy zdarzenia:

  1. MouseMove – śledzi położenie kursora
  2. MouseDown – moment przyciśnięcia lewego przycisku myszy
  3. Mouseup – moment puszczenia tego przycisku

Zdarzenie MouseMove będzie działało dwojako, ponieważ gdy lewy przycisk myszy będzie wciśnięty, poruszanie myszą spowoduje przesunięcie obiektu, jeśli nie będziemy trzymać lewego przycisku myszy a będziemy znajdować się nad kształtem, jego stan kursora, ze strzałki zostanie zmieniony na rączkę.

Zdarzenie MaouseMove śledzi położenie kursora, jego koordynaty pobieramy za pomocą punktu wykorzystując „ByVal e As System.Windows.Forms.MouseEventArgs”, położenie określają zmienne (e.X, e.Y). Jeśli nie trzymamy obiektu, wtedy koordynaty są na bieżąco sprawdzane, jeśli element jest trzymany, wtedy tworzony jest nowy kształt (Rectangle) w którym zmieniamy położenie (rectnew.Location = New Point(e.X + OffsetXX, e.Y + OffsetYY)) i nadpisujemy go na liście:

Wygląda to pięknie, i będzie jeszcze piękniej. Zajmiemy się teraz zdarzeniem MouseDown, które jest wywoływane w momencie przyciśnięcia lewego przycisku myszy. Zdarzenie to, określi nam który element jest wybrany, zmieni kolor elementu, podświetli go, tak abyśmy się nie pogubili, i przesunie na koniec kolekcji, przesuwając jednocześnie nasz obiekt do przodu.

Jeśli nie zastosujemy offsetu, po wybraniu lewym przyciskiem myszy nasz obiekt zostanie zrównany z punktem kursora:

Musimy więc dopasować tak koordynaty aby kursor myszy znajdował się w miejscu jego ułożenia. teraz po wybraniu lewego przycisku myszy możemy podnosić nasze kształty. Ułożenie kształtów zapewnia zdarzenie MouseUp. Podzielone jest ono na dwie opcje, pierwsza to kliknięcie prawym przyciskiem myszy, spowoduje to obrócenie obiektu, lewy przycisk myszy ułoży obiekt.

W zależności od tego jaki kształt wybierzemy, można go obracać dwa lub cztery razy o 90 stopni. Jeśli obrócimy określony kształt dwa razy, wtedy następuje zmiana na jego lustrzane odbicie, ma on wtedy 4 stany:

Ten kształt ma cztery stany, dwa zwykłe i dwa lustrzane

Ten kształt ma cztery stany, bez lustrzanego odbicia

Ten kształt jest symetryczny, więc nie ma żadnego stanu.

Najwięcej stanów mają dwa elementy turkusowe, bo aż 8, cztery zwykłe i cztery lustrzane:

Wartość obrotu jest szalenie istotna, ponieważ wykorzystywana jest ostatecznie do porównania obrazków i określeniu, czy gra się skończyła. Kiedy już określiliśmy nowy obiekt Rectangle i wygenerowaliśmy nową bitmapę, nadpisujemy ją na liście. Przyszedł czas na omówienie lewego klawisza myszy, na początku sprawdzamy czy nie na relacji „nasz obiekt <=> listaKwadratow(i)” jeśli tak jest nasz element będzie odpowiednio dopasowany. Gdyby tego nie było, nasz element nie byłby równo ułożony. Można odblokować element:

Efektem będzie wyświetlenie kwadratu:

To zapewnia nam spójną całość formy. Dodatkowo do struktury dodajemy element .polozenie który będzie potrzebny do sprawdzenia czy gra jest zakończona. Jeśli kwadrat zostanie znaleziony, tak jak na gifie powyżej, wtedy następuje zmiana koloru i nadpisanie obiektu. Jeśli jednak kwadrat nie zostanie znaleziony a element jest w ruchu, oznacza to, że został ułożony poza planszą. Wtedy należy nadać mu koordynaty (e.X, eY). Jeśli element znajduje się  poza planszą należy go przesunąć.

Mamy już potrzebne elementy. Możemy przesuwać kształty i układać je na planszy. Zaczynamy grę! Tworzymy uchwyt dla przycisku „Start / Losuj” (button2):

Wracamy do klasy GenerujKsztalty, musimy stworzyć funkcję rysującą wybrany wzór, aby było trudniej, będziemy generować tylko czerwone kształty. Oczywiście bitmapa wygenerowana tą metodą będzie dużo mniejsza. Musimy określić punkty na tej bitmapie na których zaczepimy kształty:

Teraz wzór będzie generowany:

Najtrudniejszy element, czyli „KoniecGry()” zostawimy sobie na koniec. Dodajmy teraz funkcjonalność reszty klawiszy:

Wracamy do klasy „GenerujKsztalty”, w momencie poddania gry, gracz zostanie poinstruowany o sposobie ułożenia elementów:

 

Jest to bardzo pomocny element, gdyż użytkownik może zarzucić nam, że układanki nie da się ułożyć.

Kod jest trochę długi, ponieważ przy użyciu tablicy wzoru, musimy namalować i ułożyć elementy. Przyszedł czas na ostatni, najtrudniejszy element naszej układanki. Sprawdzenie czy wzór jest taki sam jak to co ułożyliśmy. Na pewno przyszło wam do głowy aby po prostu porównać koordynaty wszystkich elementów i wzoru. Niestety nie będzie tak łatwo. Zobaczcie obrazek poniżej:

Aż trzy elementy na dole mają różne współrzędne. Podczas sprawdzania współrzędnych tak duża różnica elementów jest niedopuszczalna. Lecz użytkownik ułożył obrazek zgodnie ze wzorem. Miałem dużo pomysłów na to jak to zrobić ale jeden okazał się najlepszy. Stworzymy mała bitmapę z czerwonymi elementami. Wracamy do GenerujKsztalty i dodajemy ostatnią funkcję zwracającą bitmapę:

Nie różni się ona od innych zamieszczonych w tej klasie elementów, teraz w form1 odblokujmy w zdarzeniu MouseUp:

I dodajmy tą metodę do kodu:

Kiedy ułożymy wzór, w prawym dolnym rogu w panelach 3 i 4, pojawi się bitmapa do porównań. Panele te są niepotrzebne więc można je usunąć, ale dodałem je aby pokazać wam jak to działa:

Dobra, gra gotowa, teraz należy powymyślać więcej wzorów. Tutaj zdam się na was. Jeśli macie jakieś ciekawe wzory, napiszcie, przydadzą się. Pozdrawiam

Projekt do pobrania: Tangram visualMonsters.cba.pl

 

 

 

Permalink do tego artykułu: https://visualmonsters.cba.pl/shape-tangram-puzzle/

Dodaj komentarz

Twój adres email nie będzie publikowany.