Devlog
article thumbnail

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문의 별 찍기를 응용하였다.

구조는 사진 좌측 하단 맵 참고 (알카노이드)

 

 

 

 

 

 

 

공의 충돌 검사 하는데  애를 좀 먹은 프로젝트로

공이 충돌해도 벽이 깨지지 않거나 공과 충돌한 벽돌이 아닌 공이 아예 닿지 않은 벽돌이 깨지는 등

충돌검사의 범위 오차로 인한 변수들이 많았다. 

profile

Devlog

@덩이

포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!

검색 태그