openGL을 이용한 벽돌깨기 게임
- 벽돌깨기의 스틱은 방향키로 조절할 수 있으며 이는 상하좌우로 움직일 수 있다.
- 벽돌깨기의 회색 벽돌은 공이 닿아도 깨지지 않고 공을 튕겨낸다
#include <Windows.h>
#include <gl/glut.h>
#include <gl/glu.h>
#include <math.h>
#define PI 3.141592
float move = 13; //스틱의 속도
float width = 780.0; //창의 넓이
float height = 600.0; //창의 높이
int num = 45; //공 그리기
float radius = 10.0; //공의 반지름
float cx = 300.0; //공의 x좌표
float cy = 500.0; //공의 y좌표
float dx = 8.2; //공의 속도
float dy = 2.2; //공의 속도
float xx = 0.0; //스틱의 x좌표
float yy = 25.0; //스틱의 y좌표
float delta;
float xp = 0.0; //벽돌의 x좌표 위치
float yp = 580.0; //벽돌의 y좌표 위치
struct Color {
float r;
float g;
float b;
};
struct Block {
float x, y;
int collision;
Color color;
};
Block *block;
int total = 78;
void init_Block(void) {
block = new Block[total];
int num = 0;
for (int i = 0; i < total; i++) {
if (i == 1 || i == 3 || i == 6 || i == 10 || i == 15 || i == 21 || i == 28 || i == 36 || i == 45 || i == 55 || i == 66) { //벽돌의 행을 바꾸는 기준
num = 0; //새로운 행을 의미, 초기화
yp -= 30; //30만큼 밑으로 출력
}
//벽돌의 크기 x=45, y=25 (각각의 크기마다 +5픽셀 = 여백)
block[i].x = xp + 50 * num; //벽돌의 x좌표 위치
block[i].y = yp; //벽돌의 y좌표 위치
block[i].collision = 0; //벽돌 충돌검사 초기화
num++; //벽돌의 개수가 늘 때마다 늘어남
}
}
void init() {
glClearColor(0.0, 0.0, 0.0, 0.0);
gluOrtho2D(0, width, 0, height);
init_Block();
}
void MyReshape(int w, int h) {
glViewport(0, 0, w, h);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluOrtho2D(-20, width, height, -20);
}
void Draw_Ball(float x, float y, float r) { //공 그리기
delta = 2.0*PI / num;
glBegin(GL_POLYGON);
glColor3f(1, 0.8, 0);
for (int i = 0; i < num; i++) {
glVertex2f(x + r*cos(delta*i), y + r*sin(delta*i));
}
glEnd();
}
void Draw_Stick(void) { //스틱 그리기 (시계방향)
glColor3f(0.6, 0.6, 0.6);
glBegin(GL_POLYGON);
glVertex2f(xx, yy);
glVertex2f(xx + 95.0, yy);
glVertex2f(xx + 95.0, yy + 25.0);
glVertex2f(xx, yy + 25.0);
glEnd();
}
void Draw_Brick(void) { //벽돌 그리기
for (int i = 0; i < total; i++) {
if (block[i].collision == 0) { //벽돌이 충돌하지 않은 상태에서 벽돌을 그림
if (i == 0 || i == 1 || i == 3 || i == 6 || i == 10 || i == 15 || i == 21 || i == 28 || i == 36 || i == 45 || i == 55) {
glColor3f(0.0, 0.6, 0.6);
}
else if (i == 2 || i == 4 || i == 7 || i == 11 || i == 16 || i == 22 || i == 29 || i == 37 || i == 46 || i == 56) {
glColor3f(0.4, 0.6, 0.8);
}
else if (i == 5 || i == 8 || i == 12 || i == 17 || i == 23 || i == 30 || i == 38 || i == 47 || i == 57) {
glColor3f(0.8, 0.4, 0.4);
}
else if (i == 9 || i == 13 || i == 18 || i == 24 || i == 31 || i == 39 || i == 48 || i == 58) {
glColor3f(0.8, 0.8, 0.4);
}
else if (i == 14 || i == 19 || i == 25 || i == 32 || i == 40 || i == 49 || i == 59) {
glColor3f(0.8, 0.6, 0.8);
}
else if (i == 20 || i == 26 || i == 33 || i == 41 || i == 50 || i == 60) {
glColor3f(0.8, 0.6, 0.6);
}
else if (i == 27 || i == 34 || i == 42 || i == 51 || i == 61) {
glColor3f(0.6, 0.8, 0.4);
}
else if (i == 35 || i == 43 || i == 52 || i == 62) {
glColor3f(0.8, 0.6, 0.4);
}
else if (i == 44 || i == 53 || i == 63) {
glColor3f(0.6, 0.6, 0.8);
}
else if (i == 54 || i == 64) {
glColor3f(0.6, 0.8, 1);
}
else if (i == 65) {
glColor3f(0.6, 0.8, 0.6);
}
else if (i == 77) {
glColor3f(0.6, 0.2, 0.2);
}
else {
glColor3f(0.6, 0.6, 0.6);
}
//벽돌좌표 (시계방향)
glBegin(GL_POLYGON);
glVertex2f(block[i].x, block[i].y); //a
glVertex2f(block[i].x + 45, block[i].y); //b
glVertex2f(block[i].x + 45, block[i].y - 25); //c
glVertex2f(block[i].x, block[i].y - 25);//d
glEnd();
}
}
}
void Collision_Wall(void) { //벽과의 충돌
if (cx + radius >= width)
dx *= (-1.0); //공과 오른쪽 벽의 충돌
if (cx - radius <= 0)
dx *= (-1.0); //공과 왼쪽 벽의 충돌
if (cy + radius >= height)
dy *= (-1.0); //공과 천장의 충돌
if (cy - radius <= 0) {
dx = 0;
dy = 0; //공과 바닥의 충돌, 충돌시 공의 움직임 없음
}
if (xx <= 0) { //스틱과 왼쪽 벽의 충돌
xx = 0;
}
if (xx + 95 >= width) { //스틱과 오른쪽 벽의 충돌
xx = width - 95;
}
cx += dx; //공의 좌표 += 공의속도
cy += dy; //공의 좌표 += 공의속도
}
void Collision_Stick(void) { //공과 스틱의 충돌
if ((xx <= cx - radius) && (cx + radius <= xx + 95.0) && (yy + 25.0 >= cy - radius)) {
dx *= (-1.0);
dy *= (-1.0);
}
}
void Collision_Brick(void) { //공과 벽돌의 충돌
for (int i = 0; i < total; i++) {
//점과 점 사이의 거리
float a = sqrt(pow(cx - block[i].x, 2) + pow(cy - block[i].y, 2));
float b = sqrt(pow(cx - block[i].x + 45, 2) + pow(cy - block[i].y, 2));
float c = sqrt(pow(cx - block[i].x + 45, 2) + pow(cy - block[i].y - 25, 2));
float d = sqrt(pow(cx - block[i].x, 2) + pow(cy - block[i].y - 25, 2));
if (block[i].x - radius <= cx && cx <= block[i].x + 45 + radius && cy <= block[i].y + radius && cy >= block[i].y - 25 - radius) { //공과 벽돌의 모서리 충돌
if (i <= 65 || i == 77) {
block[i].collision += 1;
block[i].x = -100;
block[i].y = -100;
}
else {
block[i].collision = 0;
}
dx *= (-1.0);
dy *= (-1.0);
}
}
}
void Move_Stick(int key, int x, int y) { //스틱의 움직임
switch (key) {
case GLUT_KEY_LEFT:
xx -= move;
break;
case GLUT_KEY_RIGHT:
xx += move;
break;
case GLUT_KEY_UP:
yy += move;
break;
case GLUT_KEY_DOWN:
yy -= move;
break;
}
glutPostRedisplay();
}
void RenderScene(void) {
glClear(GL_COLOR_BUFFER_BIT);
Draw_Ball(cx, cy, radius);
Draw_Stick();
Draw_Brick();
Collision_Wall();
Collision_Stick();
Collision_Brick();
glFlush();
glutSwapBuffers();
}
void main(int argc, char** argv) {
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB);
glutInitWindowSize(780, 600);
glutInitWindowPosition(0, 0);
glutCreateWindow("벽돌깨기");
init();
glutDisplayFunc(RenderScene);
//glutReshapeFunc(MyReshape); 필요시 수정
glutIdleFunc(RenderScene);
glutSpecialFunc(Move_Stick);
glutMainLoop();
}
벽돌의 배치는 이중 for문의 별 찍기를 응용하였다.
구조는 사진 좌측 하단 맵 참고 (알카노이드)
공의 충돌 검사 하는데 애를 좀 먹은 프로젝트로
공이 충돌해도 벽이 깨지지 않거나 공과 충돌한 벽돌이 아닌 공이 아예 닿지 않은 벽돌이 깨지는 등
충돌검사의 범위 오차로 인한 변수들이 많았다.
'학교' 카테고리의 다른 글
[2019-1] 임베디드시스템 프로젝트 (0) | 2021.08.26 |
---|---|
[2018-1] 창의공학설계입문 프로젝트 (0) | 2021.08.26 |
[2019-1] 자바프로그래밍 과제 (0) | 2021.08.12 |
[2019-1] 디지털영상처리1 8장 과제 (0) | 2021.08.12 |
[2019-1] 디지털영상처리1 7장 과제 (0) | 2021.08.12 |