본문 바로가기

C# WPF

[C# WPF]Matching Game in WPF, WPF 매칭게임

같은 그림 두장을 찾으면 카드를 오픈하는 매칭 게임을 WPF로 만들어 보겠습니다.

Form으로 만든 같은 프로그램은 http://drbeeeye.tistory.com/49 을 참조하십시오.

Form 프로그램과 WPF 프로그램은 비슷하면서도 다른 점이 많아 헷갈립니다. 하나의 프로젝트를 Form과 WPF로 각각 만들어보면 그 차이를 알게 됩니다. 가장 큰 차이는 그래픽 처리 부분이 아닌가 싶습니다. Form에서는 GDI+를 사용하고 WPF에서는 그래픽 객체를 Add 하는 방법을 사용합니다.

1. Xaml 파일부터 만들겠습니다. 같은 크기의 여러 콘트롤을 사용하는 경우에는 UniformGrid를 사용하는 것이 좋습니다. 윈도우 Title, Height, Width를 바꾸어주고, board라는 이름을 갖는 UniformGrid를 선언했습니다.

<Window x:Class="MatchingWPF.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="Ball Matching in WPF(v1.0) by Beeeye Dmu" Height="550" Width="550" ResizeMode="NoResize">
    <UniformGrid Name="board">       
    </UniformGrid>
</Window>

2.  이 프로그램에서 사용하는 아이콘 이미지들은 아래 그림과 같습니다. image 폴더 밑에 따로 저장해 두었습니다. 

 

 

3. 이제 board에 16개의 Button을 넣겠습니다. 그리고 모든 버튼에는 뒤집힌 카드 즉 ckeck1.ico 이미지를 표시하겠습니다. ckeck1.ico 는 체크 표시 이미지 입니다.


        private void setBoard()
        {
            for (int i = 0; i < 16; i++)
            {
                Button c = new Button();
                c.Background = Brushes.White;
                c.Margin = new Thickness(10);
                board.Children.Add(c);

                BitmapImage bi = new BitmapImage();
                bi.BeginInit();
                bi.UriSource = new Uri("../../image/check1.ico", UriKind.Relative);
                bi.EndInit();

                Image myImage = new Image();
                myImage.Margin = new Thickness(10);
                myImage.Stretch = Stretch.Fill;
                myImage.Source = bi;
                c.Content = myImage;   
                c.Tag = tagSet();    
                c.Click += btn_Click;
            }
       }

4.  프로그램을 실행하면 다음과 같이 16개의 버튼이 board 에 자리잡고 있습니다. 버튼의 Background를 흰색으로 설정했지만 마우스가 올라가면 바탕색이 변하여 표시가 납니다.

 

5. 이제 각 버튼에 해당하는 숫자를 랜덤하게 0~15까지 부여하겠습니다.부여된 숫자를 각 Button의 Tag 가 갖게 합니다.

        // 중복되지 않는 0~15까지의 숫자를 tag에 셋팅
        private int tagSet()
        {
            int i;
            while (true)
            {
                i = r.Next(16);
                if (rnd[i] == 0)
                {
                    rnd[i] = 1;
                    break;
                }
            }
            return i % 8;
        }

6. 버튼이 클릭될 때 실행되는 btn_Click 이벤트 함수는 다음과 같습니다.


        void btn_Click(object sender, RoutedEventArgs e)
        {
            if (ready == false)
                return;

            Button btn = sender as Button;
            string sports = "";

            if ((int)btn.Tag == 0)
                sports = "골프";
            else if ((int)btn.Tag == 1)
                sports = "농구";
            else if ((int)btn.Tag == 2)
                sports = "럭비";
            else if ((int)btn.Tag == 3)
                sports = "배구";
            else if ((int)btn.Tag == 4)
                sports = "야구";
            else if ((int)btn.Tag == 5)
                sports = "축구";
            else if ((int)btn.Tag == 6)
                sports = "탁구";
            else if ((int)btn.Tag == 7)
                sports = "테니스";
           
            BitmapImage bi = new BitmapImage();
            bi.BeginInit();
            bi.UriSource = new Uri("../../image/"+sports+".ico", UriKind.Relative);
            bi.EndInit();

            Image myImage = new Image();
            myImage.Margin = new Thickness(10);
            myImage.Stretch = Stretch.Fill;
            myImage.Source = bi;

            btn.Content = myImage;

            if (first == null)   // 첫번째 Click 된 경우
            {
                first = btn;
                first.Tag = (int)first.Tag + 80;
                return;
            }
           
            second = btn;

            if ((int)first.Tag % 8 == (int)second.Tag % 8) // 매칭됨
            {
                first.Tag = (int)first.Tag + 80;    // 매칭된 btn의 Tag는 160+
                second.Tag = (int)second.Tag + 160;
                first = null;
                second = null;

                matched += 2;
                if (matched >= 16)  // 모두 다 매칭되었다면, 끝내거나 다시 시작하거나
                {
                    MessageBoxResult res = MessageBox.Show("성공했습니다. 다시 시작할까요?",
                                                    "Success", MessageBoxButton.YesNo);
                    if (res == MessageBoxResult.Yes)
                    {
                        resetRnd();
                        resetBoard();
                        matched = 0;
                    }
                    else
                        Close();
                }
                return;
            }

            first.Tag = (int)first.Tag - 80;
            ready = false;
            myTimer.Start();
        }

7. MainWindow() 생성자 함수는 다음과 같이 setBoard() 함수와 함께 myTimer와 myTimer_Tick 함수를 설정합니다.


        public MainWindow()
        {
            InitializeComponent();

            setBoard();
            myTimer.Interval = new TimeSpan(0, 0, 0, 0, 750);    // 0.75초
            myTimer.Tick+=myTimer_Tick;
        }

8. 2장의 버튼이 다른 공을 갖는 경우 0.75초 후에 원래 뒤집힌 카드 모양으로 돌아오게 하려고 myTimer_Tick 함수에서는 0.75초 마다 불려지고 바로 myTimer를 Stop시키고 뒤집힌 모습으로 버튼을 바꿉니다.


        void myTimer_Tick(object sender, EventArgs e)
        {
            myTimer.Stop();

            Image myImage = setCheckImage();

            first.Content = setCheckImage();
            second.Content = setCheckImage();

            first = null;
            second = null;
            ready = true;
        }

9. 여러번 반복되는 Image 처리를 위해 setCheckImage 함수를 사용합니다. 이 함수는 버튼의 콘텐츠를 뒤집힌 상태의 check 이미지로 표시하기 위해 Image를 만들어 return 합니다.

        // check image(뒤집힌 이미지)를 세팅하여 return
        private static Image setCheckImage()
        {
            BitmapImage bi = new BitmapImage();
            bi.BeginInit();
            bi.UriSource = new Uri("../../image/check1.ico", UriKind.Relative);
            bi.EndInit();
            Image myImage = new Image();
            myImage.Margin = new Thickness(10);
            myImage.Stretch = Stretch.Fill;
            myImage.Source = bi;

            return myImage;
        }

10. 모든 카드가 매칭되면 메시지박스가 나타나서 다시 게임을 할 지, 끝낼 지를 묻습니다. 다시 시작하게 되면 다음의 과정을 수행하여 초기상태와 같게 만듭니다.

                    if (res == MessageBoxResult.Yes)
                    {
                        resetRnd();
                        resetBoard();
                        matched = 0;
                    }

11. resetRnd() 함수는 랜덤하게 버튼의 Tag 값을 0~15로 세팅하기 위해 rnd[]을 초기화합니다.


        private void resetRnd()
        {
            for (int i = 0; i < 16; i++)
                rnd[i] = 0;
        }

12. resetBoard() 함수는 카드를 모두 뒤집고, 버튼의 Tag 값을 설정합니다.


        private void resetBoard()
        {
            foreach(Button btn in board.Children)
            {
                btn.Content = setCheckImage();
                btn.Tag = tagSet();
            }
        }

13. 플레이 중의 모습은 다음 그림과 같습니다.

 

 

Beeeys Dmu