2018-02-06 15:25:25

C++ 및 opencv 공부겸 저격 게임을 만들어봤습니다.ㅋㅋ 


게임 방법은 아주 간단합니다. 마우스 좌클릭만 할 줄 알면 됩니다. 원이 일정 시간 간격으로 화면에 나타나는데 원의 중심을 클릭하시면 됩니다. 원의 중심에 가까울수록 좋은 점수를 받을 수 있고, 원의 중심을 정확하게 클릭한 경우에는 보너스 점수를 받게 됩니다. 일정 점수를 넘게 되면 다음 라운드로 진출하게 되고 점점 원의 크기도 커지고, 원이 나타나는 시간 간격도 짧아집니다. 많은 원을 없애지 못한 경우에 다음 라운드로 넘어가지 못하고 게임이 종료되니 빠르고 정확히 원들을 없애는 것에 목표를 두시면 됩니다. 


게임 파일은 아래 링크에 있으니 다운받으신 후에 압축푸시고, ImSniper.exe 실행하시면 됩니다. 티스토리는 파일 업로드가 10메가바이트 밖에 안되서 ..ㅠ 네이버 클라우드를 사용했습니다. 


ImSniper 다운로: http://naver.me/FbGBbYqi

아임스나이퍼 게임을 해보신 분들은 자신의 기록을 댓글로 남겨주시면 감사하겠습니다! ^^ 참고로 제 기록은 23865입니다. 재미있게 즐기셨다면 주변 친구들에게도 소개해주세요. ㅎㅎ  


아래 게임 실행 영상을 보시죠. 




원의 중심을 정확히 맞출 때의 그 쾌감! 


한번 느껴보시길. ㅎㅎ 



위 게임을 만들기 위해 사용한 C++ 소스코드는 다음과 같습니다. 


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
// MyGame_20180131.cpp: 콘솔 응용 프로그램의 진입점을 정의합니다.
//
 
#include "stdafx.h"
#include <iostream>
#include <opencv2/core.hpp>
#include <opencv2/highgui.hpp>
 
#include <opencv2/imgproc.hpp> //rectangle() 사용을 위해
 
#include <stdlib.h> //srand() 사용을 위해
#include <time.h> //time() 사용을 위해
 
#include <Windows.h> // Sleep() 사용을 위해
#include <process.h> // 쓰레드 사용을 위해
 
#include <mmsystem.h> // PlaySound() 사용을 위해
 
#pragma comment(lib, "winmm.lib")
#define SOUND_NAME1 "water001.wav" // 사각형 출현시 사운드
#define SOUND_NAME2 "scifi054.wav" // 클릭시 사운드
#define SOUND_NAME3 "musical097.wav" // 라운드 통과시 사운드
#define SOUND_NAME4 "battle003.wav" // 원 완전 제거시 사운드 => 바꾸기
 
// 전역변수들
int ing = 100;
int circle_size = 30// 1단계 원 반지름 
int circle_num = 0// 원 갯수
int click_num = 0// 좌클릭 수
int circles[100][2];
int bonus = 10// 보너스 점수 
int total_bonus = 0
 
unsigned _stdcall TIMER(void *arg)
{
    srand((int)time(NULL));
    
    int sec = 20;
    std::cout << "남은 시간 " <<std::endl;
 
    for (int i = 1; i <= sec; i++)
    {
        ing = 1;
 
        if ((sec - i) < 10)
            std::cout << 0 << sec - i;
        else
            std::cout << sec - i;
 
        Sleep(1000);
 
        if ((sec-i) != 0)
            std::cout << "\r";
 
    }
    ing = 0;
 
    return 0;
}
 
void onMouse(int event, int x, int y, int flags, void* param);
 
int main()
{
    int round = 1// 몇 라운드인지. 
    int round_score = 0// 라운드 점수 초기 설정. 
    int pixel_score = 0
    int score = 0// 종합 점수. 
    int goal = 80// 라운드 목표 점수. 
    int speed = 3000
 
    int height = 700// 게임창 세로 길이
    int width = 1200// 게임창 가로 길이
    
    srand((int)time(NULL)); //현재 시간을 이용해서 씨드 설정
    
    std::cout << "<<ImSniper>>\n"<< std::endl;
    std::cout << "게임시작 3초전..." << std::endl;
    Sleep(3000);
 
    while (1)
    {    
        cv::Mat bg_img(height, width, CV_8UC3, cv::Scalar(0.00.00.0)); // 검정 배경 화면 만들기 600 x 1000
        cv::imshow("Game", bg_img);
        
        cv::setMouseCallback("Game", onMouse, reinterpret_cast<void*>(&bg_img)); // 게임 창에 마우스 핸들러 설정
        
        std::cout << "\n\n<Round " << round << ">" << std::endl;
        _beginthreadex(NULL0, TIMER, 00NULL); // 쓰레드 
        
        while (1//라운드 종료될 때까지 계속
        {    
            // 너무 가장자리에는 원이 생겨나면 안됨.     
            int P_x = circle_size + rand() % (width - 2*circle_size);
            int P_y = circle_size + rand() % (height - 2*circle_size);
 
            cv::circle(bg_img, cv::Point(P_x, P_y), circle_size, cv::Scalar(rand() % 255, rand() % 255, rand() % 255), -110);
            cv::circle(bg_img, cv::Point(P_x, P_y), 5, cv::Scalar(00255), -110);
            circles[circle_num][0= P_x;
            circles[circle_num][1= P_y;
 
            circle_num++;
 
            PlaySound(TEXT(SOUND_NAME1), NULL, SND_FILENAME | SND_ASYNC);
            cv::imshow("Game", bg_img);
            cv::waitKey(speed);
 
            if (ing == 0)
            {
                break;
            }
    
        }
 
        double color_pixel_num = 0
 
        for (int x = 0; x < width; x++)
        {
            for (int y = 0; y < height; y++)
            {
                if (bg_img.at<cv::Vec3b>(y, x) != cv::Vec3b(000))
                {
                    color_pixel_num++// 없애지 못한 픽셀들
                }
                    
            }
        }
 
        pixel_score = (int)((height*width - color_pixel_num) / (height*width) * 100);
        round_score = pixel_score + total_bonus;
        score = score + round_score; 
        
        std::cout << "\n\nRound "<< round << " 점수>> " << round_score <<std::endl;
 
        std::cout << "종합 점수>> " << score << std::endl
 
        if (pixel_score >= goal)
        {
            PlaySound(TEXT(SOUND_NAME3), NULL, SND_FILENAME | SND_ASYNC);
            round++;
            std::cout << "Go to Round " << round << "!\n\n" << std::endl;
            
            circle_size = circle_size * 1.1;
            speed = speed * 0.9;
            Sleep(2000); // 다음 라운드로 넘어가기 전에 2초간 쉬자. 
            click_num = 0;
            circle_num = 0;
            total_bonus = 0;
            bonus = bonus * 1.5;
            ing = 100;
        }
        else
        {
            std::cout << "다음 라운드 진출 실패! 게임 끝!\n\n" << std::endl;
            std::cout << "최종 점수: " << score << std::endl << std::endl;
            Sleep(5000);
            break;
        }
        
    }
 
    return 0;
}
 
void onMouse(int event, int x, int y, int flags, void* param)
{
    cv::Mat *im = reinterpret_cast<cv::Mat*>(param);
 
    switch (event) // 이벤트 전달
    {
        case CV_EVENT_LBUTTONDOWN: // 마우스 좌클릭시 이벤트
            if (click_num <= circle_num && ing == 1// 클릭 수가 사각형 수보다 적고, 라운드 진행 중일때만 리무버 작동.
            {
                //cv::rectangle(*im, cv::Point(x - (int)circle_size / 2, y - (int)circle_size / 2), cv::Point(x + (int)circle_size / 2, y + (int)circle_size / 2), cv::Scalar(0.0, 0.0, 0.0), -1, 1, 0); // 검정색 정사각형을 덧그리자.
                cv::circle(*im, cv::Point(x, y), circle_size, cv::Scalar(0.00.00.0), -110);
                PlaySound(TEXT(SOUND_NAME2), NULL, SND_FILENAME | SND_ASYNC);
                // 만약에 클릭한 위치가 원의 중심이라면 보너스 점수 + 10 주기!!! 이거 중요. 좀 더 재밌게 즐기려면. 랜덤으로 생겨난 좌표값들을 전역변수로 저장해놓을 필요가 있을듯. 그래서 지금 클릭한 것과 맞는지 확인.
                for (int k = 0; k < 100; k++)
                {
                    if ((x == circles[k][0&& y == circles[k][1]) || ((x -1== circles[k][0&& (y - 1== circles[k][1]) || ((x + 1== circles[k][0&& (y + 1== circles[k][1]) || (x == circles[k][0&& (y - 1== circles[k][1]) || ((x - 1== circles[k][0&& y == circles[k][1]) || (x == circles[k][0&& (y + 1== circles[k][1]) || ((x + 1== circles[k][0&& y == circles[k][1]) || ((x + 1== circles[k][0&& (y - 1== circles[k][1]) || ((x - 1== circles[k][0&& (y + 1== circles[k][1]))
                    {
                        total_bonus = total_bonus + bonus;
                        std::cout << "sec: bonus point " << bonus <<"!" << std::endl;
                        PlaySound(TEXT(SOUND_NAME4), NULL, SND_FILENAME | SND_ASYNC);
                    }
                }
                click_num++;
            }
            break;
    
    }
    // 클릭할 때마다 효과음 주기. 
    // 클릭수에 한계를 줘서 무한 클릭하지 않도록 방지. 생성된 정사각형 수만큼만 클릭 가능. 
}
cs