본문 바로가기

C# Form

Matching Game, 매칭게임

  두 장의 카드를 선택해서 같은 그림이면 Open하고 아니면 다시 덮어줍니다. 모든 쌍의 그림을 맞추면 게임이 끝나는 Matching Game을 만들어보겠습니다.

  우선, 프로그램이 시행되는 모습을 보면 다음과 같습니다. 먼저 16개의 자리가 모두 가려져 있는 그림이 표시됩니다. 마우스를 클릭하면 그림이 보이는데, 두 장의 그림이 맞으면 그림이 보이는 상태로 유지되고, 다르면 다시 그림이 안보이게 됩니다.

  처음엔 아래 그림처럼 시작이 되겠죠?

 

  아래 그림은 총 8쌍 중에서 7쌍을 맞춘 상태의 그림입니다. 8쌍이 다 맞으면 게임이 끝나게 됩니다. 공을 주제로 그림을 사용했습니다. 이름도 Ball Matching으로 하겠습니다.

 

이제 순서대로 프로그램을 설명하도록 하겠습니다.

 1. Form 에 다음 그림과 같이 TableLayoutPanel을 넣습니다.

 2. TableLayoutPanel의 오른쪽 위 삼각형을 누르고 "열 추가", "행 추가"를 하여 열과 행을 4개씩으로 만듭니다. 그리고 "행 및 열 편집"을 선택해서 각 행과 열을 25%의 크기를 갖도록 수정합니다. 확인을 누릅니다.

 

 

3. TableLayoutPanel의 속성을 다음과 같이 변경합니다. Dock - Fill, Margin - 10,10,10,10, CellBorderStyle - Single. 이제 아래 그림의 오른쪽 처럼 16개의 같은 크기로 나뉘게 됩니다.

 

 

4. 나뉘어진 각 칸에 도구상자에서 PictureBox를 가져다 놓습니다. PictureBox의 속성에서 SizeMode - StretchImage, Dock - Fill, Margin - 20,20,20,20 으로 변경합니다. 이제 디자인은 그림과 같이 됩니다.

 

5. 이제 디자인은 끝났습니다. 이제 부터는 코딩에 들어갑니다. F7 키를 눌러서 코드 창을 띄웁니다. 참, 이미지를 넣어야 하겠군요. 솔루션 탐색기에서 image 폴더를 만들고 여기에 8개의 공(골프.ico, ... 등)과 뒷면 이미지(ckeck1.ico)를 드래그하여 넣습니다. ico 확장자는 icon 파일을 나타냅니다. iconfinder.com에서 필요한 무료 아이콘 이미지를 검색하여 사용하면 됩니다.

 

 

6. Form1 생성자 함수에 다음과 같이 코딩을 추가합니다. ClientSize를 550x550으로 셋팅하고 initTable() 한 후 타이머를 만듭니다. 타이머는 그림 두 장을 본 후 일치하지 않으면 0.75초 후에 다시 덮을 때 쓰입니다.

  Timer myTimer = new Timer();
  int [] rnd = new int[16];        // 겹치지 않는 16개의 random 값을 만들 때 사용함 
  Random r = new Random();
  int matched = 0;            // 매칭된 그림의 수, 16개가 되면 게임이 끝남

        public Form1()
        {
            InitializeComponent();
            ClientSize = new Size(550, 550);
           
            initTable();    // Tag를 셋팅한다

            myTimer.Interval = 750;
            myTimer.Tick += myTimer_Tick;
        }


        private void initTable()
        {
            foreach (Control c in tableLayoutPanel1.Controls)
            {
                Bitmap myImage;
                myImage = new Bitmap("../../image/check1.ico");
                PictureBox pb = c as PictureBox;
                pb.Image = myImage;
                pb.Tag = tagSet();
            }
        }

7. 위에서 initTable() 함수는 16개의 PictureBox 각각의 그림을 뒤집힌 그림(check1.ico)으로 세팅하고, Tag에 그림을 표시하는 숫자를 넣어줍니다. Tag에 들어가는 숫자는 0~7까지로 각각이 공의 종류를 나타냅니다.  이를 위해 tagSet()이란 함수를 썼습니다.

        // 중복되지 않는 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;
        }

8. 첫번째 오픈한 그림(PictureBox)을 first, 두번째 오픈한 그림(PictureBox)을 second 라고 하겠습니다.  이를 Form1 클래스에 선언합니다.

        private PictureBox first=null, second=null;

9. 이제 마우스 클릭될 때, first와 second를 지정하고 Tag를 사용하여 조작하도록 하겠습니다.

디자인 창에서 16개의 PictureBox Ctrl 키를 눌러 모두 선택하고, 이벤트에서 Click을 선택하고 Enter를 치면 pictureBox_Click 이름으로 이벤트 함수가 만들어집니다.


        private void pictureBox_Click(object sender, EventArgs e)
        {
            PictureBox pb = sender as PictureBox;

            if ((int)pb.Tag >= 80) // 이미 오픈 되었거나, 매칭되어 열려있는 PictureBox 라면...
                return;

            string sports = "";
            if ((int)pb.Tag == 0)
                sports = "골프";
            else if ((int)pb.Tag == 1)
                sports = "농구";
            else if ((int)pb.Tag == 2)
                sports = "럭비";
            else if ((int)pb.Tag == 3)
                sports = "배구";
            else if ((int)pb.Tag == 4)
                sports = "야구";
            else if ((int)pb.Tag == 5)
                sports = "축구";
            else if ((int)pb.Tag == 6)
                sports = "탁구";
            else if ((int)pb.Tag == 7)
                sports = "테니스";

            string iFile = "../../image/" + sports + ".ico";
            Bitmap myImage;
            myImage = new Bitmap(iFile);

            if (first == null)   // 첫장이라면
            {
                first = pb;
                first.Image = myImage;
                first.Tag = (int)first.Tag + 80;
                return;
            }

            second = pb;
            second.Image = myImage;

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

                matched += 2;
                if (matched >= 16)  // 모두다 맞춤
                {
                    DialogResult res;
                    res = MessageBox.Show("모두 맞추었습니다. 게임을 다시 하시겠습니까?", "Finish!!", MessageBoxButtons.YesNo);
                    if (res == DialogResult.Yes)
                    {
                        initTable();    // Tag를 셋팅한다
                        matched = 0;
                    }
                    else
                        this.Close();
                }
                return;
            }

            // 매칭되지 않았다면, first.Tag 를 원래대로 돌려두고, 타이머를 시작합니다.
            first.Tag = (int)first.Tag - 80;
            myTimer.Start();    // 타이머 시작
        }

10. 타이머 함수는 다음과 같이 만듭니다. 시작된 타이머를 Stop 시키고, 열려있던 그림을 닫아주고 first와 second를 null 로 만들어 줌으로써 초기상태로 되돌립니다.


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

            Bitmap myImage = new Bitmap("../../image/check1.ico");
            first.Image = myImage;
            second.Image = myImage;

            first = null;
            second = null;
        }

11. 추가적으로 해결할 부분은, 타이머가 동작하고 있을 때 마우스가 클릭되면 에러가 생길 수 있습니다. 이를 방지하기 위해서 bool ready; 를 선언해주고, 타이머 시작 전에 false로 바꾸어 주었다가 Timer_Tick() 함수에서 타이머를 Stop한 후에 다시 true로 만들어줍니다. 그리고 private void pictureBox_Click(object sender, EventArgs e) 함수에서 다음의 if 문을 넣어주면 클릭해도 아무런 동작을 안하게 되어 문제가 생기지 않습니다.

if( ready == false )
    return;


Beeeye Dmu

'C# Form' 카테고리의 다른 글

C# Form에서 delay 주기  (0) 2016.06.10
ListView 사용법  (0) 2015.10.01
커서의 모습을 바꾸는 방법  (0) 2014.10.22
Form의 크기를 조정하는 방법  (0) 2014.10.22
[C# Form] 오목 프로그램  (0) 2014.10.13