Strona główna > Przykłady > [C#,WinForms] Gra Układanka

[C#,WinForms] Gra Układanka

Dzisiaj przedstawię proces tworzenia prostej gry, którą nazwałem „Układanką”. Jeżeli jesteś początkującym programistą i chcesz napisać pseudoużyteczny program z wykożystaniem Visual Studio i Windows Forms – ten wpis jest właśnie dla Ciebie. Jeżeli jesteś bardziej doświadczonym programistą, chcesz się trochę pośmiać / przeczytać dzienną normę kiepskiego kodu / powytykać kilka błędów i z wielką satysfakcją wyszydzić autora, bądź też pomóc zrozumieć autorowi jego błędy (niepotrzebne skreślić) – też możesz przeczytać 😉 Do dzieła!

Program będzie się składał z dziewięcu przycisków, z których jeden jest niewidoczny. Widoczne przyciski będą elementami układanki. Na przyciskach umieścimy losowo, bez powtórzeń cyfry od 1 do 8. Pozwolimy graczowi przesuwać elementy układanki w ten sposób, że jeśli zostanie naciśnięty element bezpośrednio sąsiadujący z wolnym polem, to przenosi się on na to pole. Zadaniem gracza jest uparządkowanie elementów według cyfr w kolejności rosnącej.

Układanka

Uruchamiamy Visual Studio i wybieramy nowy projekt Windows Forms. We właściwościach formy zmienimy FormBorderStyle na Fixed3D, aby użyszkodnik nie mógł zmienić rozmiaru okienka. MaximizeBox ustawiamy na false – przycisk maksymalizacji jest niedostępny. Właściwości Text zmieniamy na UKŁADANKA, co spowoduje wyświetlenie tego napisu na górnej belce okienka. ShowIcon ustawiamy na false, BackColor na PowderBlue (pozwalam wybrać inny :-D, kwestia gustu). Na formie umieszczmy 9 buttonów.
Od czasu do czasu warto rzucić okiem na wygenerowany kod w pliku Form1.Designer.cs, aby wiedzieć co w trawie piszczy 🙂 Dla przycisku w lewym dolnym rogu ustawiamy właściwość Visible na false.

Tworzymy pole będące dwuwymiarową tablicą typu Button, w której przechowywane będą elementy naszej układanki (przyciski)

private Button[,] buttonsTab;

Inicjalizujemy tablicę w konstruktorze Form1

buttonsTab=new Button[,]
{ {button1, button2, button3},
   {button4, button5, button6},
   {button7, button8, button9} };

Następnie zajmiemy się napisaniem metody, która sprawi, że na przyciskach będziemy widzieli losowe cyferki od 1 do 8 bez powtórzeń.

        //przypisujemy przyciskom losowe cyfry bez powtórzeń
        private void losujCyfryNaPrzyciskach()
        {
            int index;
            Random rand = new Random();

            //lista liczb, z której będziemy losować liczby i umieszczać je na przyciskach
            List<int> liczby = new List<int>() {1,2,3,4,5,6,7,8 };

            //tablica przycisków, kóre sa widoczne na początku gry (wszystkie bez 7)
            Button[] widocznePrzyciski = new Button[]{button1,button2,button3,button4,button5,button6,button8,button9 };

            foreach (Button b in widocznePrzyciski) //dla 8 widocznych przycisków
            {
                //losujemy wartość od 0 do liczności listy liczb
                index = rand.Next(0, liczby.Count);
                //liczbę o wylosowanym indeksie zamieniamy na string i wstawiamy do właściwości Text przycisku
                b.Text = liczby[index].ToString();
                //usuwamy z listy liczbę o wylosowanym indeksie, aby nie wylosować jej ponownie
                liczby.RemoveAt(index);
            }
        }

Metodę wywołujemy w konstruktorze Form1 po metodzie InitializeComponent().

W konstruktorze wywołamy jeszcze metodę, która doda obsługę zdarzenia Click do naszych buttonów.

//dodajemy obsługę zdarzenia Click dla każdego z przycisków
        private void dodajObslugeClickPrzyciskow()
        {
            foreach (Button b in buttonsTab)
            {
                //po kliknięcu któregokolwiek przycisku zostanie wywołana metoda o nazwie button_Click
                b.Click += new System.EventHandler(this.button_Click);
            }
        }

Kolejna metoda, którą wywołamy w konstruktorze Form1. Tym razem możemy zrealizować nasze estetyczne fantazje.

private void zmienFont_i_ColorPrzyciskow()
        {
            foreach (Button b in buttonsTab)
            {
                //tekst na przyciskach będzie miał nową czcionkę, rozmiar 25
                b.Font = new Font(FontFamily.GenericMonospace, 25);
                //zmieniamy kolory tekstu i tła przycisków
                b.BackColor = Color.Azure;
                b.ForeColor = Color.CornflowerBlue;
            }
        }

I dochodzimy do serca naszego programu – metoda która zostanie wykonana po kliknięciu przycisku tj. elementu naszej układanki. Używamy kilku mniejszych, czekających na napisanie metod. W metodzie dowiemy się, który przycisk został kliknięty oraz jaką pozycje w buttonsTab ma ten przycisk. Sprawdzimy, czy sąsiaduje on z przyciskiem niewidocznym i gdy zajdzie taka potrzeba przesówamy element.

private void button_Click(object sender, EventArgs e)
{
     //zmienne, w których zapiszemy pozycję klikniętego przycisku w buttonsTab
     int index1, index2;
     //tworzymy zmienną b i przypisujemy do niej referencję do przycisku, który wywołał zdarzenie Click
     Button b = sender as Button;
     Button niewidoczny; //będzie przechowywał referencję do niewidocznego przycisku

     ustawIndeksyPrzycisku(out index1, out index2, b); //ustawia index1 i index2

     //jeśli kliknięty przycisk nie sąsiaduje z niewidocznym, to metoda kończy swoje działanie
     if (!czyJestPrzyNiewidocznym(index1, index2, out niewidoczny))
          return;
     else
     {
         //zamienieamy miejscami kliknięty przycisk i niewidoczny przycisk (przysunięcie elementu układanki)
         zamianaNiewidocznego(b, niewidoczny);

         if (sprCzyKoniec()) //sprawdzamy czy układanka została już ułożona
         {
             MessageBox.Show("WYGRAŁEŚ!!!"); //nagroda dla zwycięzcy

             //przywracamy układanke do stanu początkowego
             foreach(Button but in buttonsTab) { but.Visible = true; }
             button7.Visible = false;
             losujCyfryNaPrzyciskach();
         }
     }
}

Teraz zabieramy się za pisanie brakujących funkcji.
Poniższa metoda rozpozna po nazwie, który przycisk został wciśnięty i przypisze wartości zmiennym index1 oraz index2 (zadeklarowanym wewnątrz metody button_Click), które informują o indeksach przycisku w tablicy przycisków buttonsTab. Targa mną silne przeczucie, że ta metoda nie wygląda najlepiej. Może nie powinno jej być wogóle, a problem powinien zostać rozwiązany z użyciem bardziej wyrafinowanego sposobu… Cóż, w każdym bądź razie działa.

private void ustawIndeksyPrzycisku(out int index1,out int index2,Button b)
        {
            index1 = 0; index2 = 0;
            if (b.Name == "button1")
            {
                index1 = 0; index2 = 0;
            }
            else if (b.Name == "button2")
            {
                index1 = 0; index2 = 1;
            }
            else if (b.Name == "button3")
            {
                index1 = 0; index2 = 2;
            }
            else if (b.Name == "button4")
            {
                index1 = 1; index2 = 0;
            }
            else if (b.Name == "button5")
            {
                index1 = 1; index2 = 1;
            }
            else if (b.Name == "button6")
            {
                index1 = 1; index2 = 2;
            }
            else if (b.Name == "button7")
            {
                index1 = 2; index2 = 0;
            }
            else if (b.Name == "button8")
            {
                index1 = 2; index2 = 1;
            }
            else if (b.Name == "button9")
            {
                index1 = 2; index2 = 2;
            }
        }

Następna metoda sprawdza, czy wciśnięty przycisk sąsiaduje z przyciskiem niewidocznym (wolnym polem, na które można przesunąć element układanki). Sprawdzamy, czy przycisk po lewej stronie (buttonsTab[index1, index2 - 1] w naszej tablicy) jest niewidoczny. Jednak wcześniej dowiadujemy się, czy tablica posiada element o indeksach [index1, index2 - 1], wykorzystując metodę czyIndeksySaPoprawne(), którą napiszemy później.
Analogicznie postępujemy z przyciskiem po prawej stronie, na dole i na górze.

private bool czyJestPrzyNiewidocznym(int index1, int index2, out Button niewidoczny)
        {
            if (czyIndeksySaPoprawne(index1, index2 - 1)) //po lewej
            {
                if (buttonsTab[index1, index2 - 1].Visible == false)
                {
                    niewidoczny = buttonsTab[index1, index2 - 1];
                    return true;
                }
            }
            if (czyIndeksySaPoprawne(index1, index2 + 1))//po prawej
            {
                if (buttonsTab[index1, index2 + 1].Visible == false)
                {
                    niewidoczny = buttonsTab[index1, index2 + 1];
                    return true;
                }
            }
            if (czyIndeksySaPoprawne(index1 - 1, index2))//na górze
            {
                if (buttonsTab[index1 - 1, index2].Visible == false)
                {
                    niewidoczny = buttonsTab[index1 - 1, index2];
                    return true;
                }
            }
            if (czyIndeksySaPoprawne(index1 + 1, index2))//na dole
            {
                if (buttonsTab[index1 + 1, index2].Visible == false)
                {
                    niewidoczny = buttonsTab[index1 + 1, index2];
                    return true;
                }
            }
            niewidoczny = null;
            return false;
        }

Metoda, która sprawdzi, czy indeksy są poprawne dla naszej tablicy 3×3 (buttonsTab)

private bool czyIndeksySaPoprawne(int index1, int index2)
        {
            if (index1 < 0 || index1 > 2 || index2 < 0 || index2 > 2)
            {
                return false;
            }
            return true;
        }

Metoda realizująca „przesunięcie elementu układanki”. Kliknięty przycisk zamienia się tekstem z niewidocznym i sam się ukrywa.

private void zamianaNiewidocznego(Button b,Button niewidoczny)
        {
            b.Visible = false;
            niewidoczny.Text = b.Text.ToString();
            b.Text = "x";
            niewidoczny.Visible = true;
        }

Sprawdzamy, czy wysiłki gracza coś dały, tzn. czy elementy są już ułożone w odpowiedniej kolejności.

private bool sprCzyKoniec()
        {
            int i=0;
            foreach (Button b in buttonsTab)
            {
                i++;
                if ( b.Text == "x" || b.Text==i.ToString()) continue;
                else return false;
            }
            return true; //jeśli elementy są ułożone w odpowiedniej kolejności zwracamy true
        }

I tak kończy się nasza przygoda z dzielną układanką, podstępnymi elementami, okrótną tablicą, piękną formą i biednym programistą.

Gotowy projekt można ściągnąć TUTAJ.

Kategorie:Przykłady Tagi: , ,
  1. 4 marca 2011 o 11:22 am

    Metoda ustawIndeksyPrzycisku mogłaby wyglądać tak:

    private void ustawIndeksyPrzycisku(out int index1, out int index2, Button b)
    {
    int x = b.Name[b.Name.Length – 1] – ‚0’ – 1;
    index1 = x / 3;
    index2 = x % 3;
    }

    Może nie najładniejszy sposób, ale lepszy od ręcznego sprawdzania.

    • 4 marca 2011 o 12:18 pm

      Rzeczywiście, teraz jest krótsza i lepiej wygląda. Przy okazji dowiedziałem się jak szybko zamienić char na int. Dzięki! 😉

  1. 1 marca 2011 o 5:54 pm

Dodaj komentarz