raylib:pong게임
pong게임
차이
문서의 선택한 두 판 사이의 차이를 보여줍니다.
양쪽 이전 판이전 판다음 판 | 이전 판 | ||
raylib:pong게임 [2023/09/22 00:49] – 이거니맨 | raylib:pong게임 [2023/11/20 00:29] (현재) – 이거니맨 | ||
---|---|---|---|
줄 1: | 줄 1: | ||
+ | ~~stoggle_buttons~~ | ||
+ | |||
+ | |||
+ | {{: | ||
+ | |||
+ | |||
pong 게임을 만들어보자 | pong 게임을 만들어보자 | ||
줄 138: | 줄 144: | ||
int rWidth = 20; | int rWidth = 20; | ||
int rHeight = 120; | int rHeight = 120; | ||
- | </ | + | </ |
=== 나. 페달 그리기 === | === 나. 페달 그리기 === | ||
줄 150: | 줄 156: | ||
// Draw Right Rectangle | // Draw Right Rectangle | ||
DrawRectangle(screenWidth - rPadding - rWidth, (screenHeight - rHeight) / 2, rWidth, rHeight, BLUE); | DrawRectangle(screenWidth - rPadding - rWidth, (screenHeight - rHeight) / 2, rWidth, rHeight, BLUE); | ||
- | </ | + | </ |
==== 4. 결론 ==== | ==== 4. 결론 ==== | ||
줄 162: | 줄 168: | ||
===== 클래스화 하기 ===== | ===== 클래스화 하기 ===== | ||
+ | ==== 1. ball.h ==== | ||
+ | |||
+ | 움직이는 공을 클래스화 하자. | ||
+ | |||
+ | ball.h에 다음과 같이 공의 속성을 정의하자. | ||
+ | 공에는 좌표로 x와 y가 있을 것이고, 반지름 값이 | ||
+ | |||
+ | 생성자와 소멸자 그리고 | ||
+ | |||
+ | <file raylib game.h> | ||
+ | class Ball | ||
+ | { | ||
+ | public: | ||
+ | int x = 400; | ||
+ | int y = 225; | ||
+ | int radius = 40; | ||
+ | int speed = 5; | ||
+ | |||
+ | Ball(); | ||
+ | |||
+ | Ball(int x, int y, int radius, int speed); | ||
+ | |||
+ | ~Ball(); | ||
+ | |||
+ | void Update(); | ||
+ | |||
+ | | ||
+ | }; | ||
+ | </ | ||
+ | |||
+ | ==== 2. ball.cpp ==== | ||
+ | |||
+ | cpp실행파일엔느 생성자와 소멸자를 통하여 볼의 초기값을 생성하였다. | ||
+ | |||
+ | 그리고 update 메서드에서 볼의 x 위치와 | ||
+ | |||
+ | <file raylib ball.cpp> | ||
+ | #include " | ||
+ | |||
+ | Ball:: | ||
+ | { | ||
+ | x = 400; | ||
+ | y = 225; | ||
+ | radius = 40; | ||
+ | speed = 5; | ||
+ | } | ||
+ | |||
+ | Ball:: | ||
+ | { | ||
+ | x = x; | ||
+ | y = y; | ||
+ | radius = radius; | ||
+ | speed = speed; | ||
+ | } | ||
+ | |||
+ | Ball:: | ||
+ | { | ||
+ | |||
+ | } | ||
+ | |||
+ | void Ball:: | ||
+ | { | ||
+ | x += speed; | ||
+ | y += speed; | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | |||
+ | ==== 3. game 로직에서 구현 ==== | ||
+ | |||
+ | === 가. Ball 인스턴스 생성하기 === | ||
+ | |||
+ | game.cpp 파일을 수정하자. | ||
+ | |||
+ | 다음과 같이 ball.h를 인클루드 한 후에, 파일의 최상단에 ball 인스턴스를 만들자. | ||
+ | |||
+ | <WRAP center round important 70%> | ||
+ | game클라스의 어느 메서드에서든 돌아가야 하므로 | ||
+ | </ | ||
+ | |||
+ | |||
+ | <code raylib> | ||
+ | #include " | ||
+ | |||
+ | Ball ball = Ball(); | ||
+ | </ | ||
+ | |||
+ | 이렇게 인스턴스를 만들면 ball초기화 되었으므로 이제 game 클라스 내에서 ball을 사용할 수 있따. | ||
+ | |||
+ | === 나. ball 그리기 === | ||
+ | |||
+ | void Game:: | ||
+ | 기존의 draw circle을 다음과 같이 ball의 값으로 바꾸면 기존과 동일하게 | ||
+ | |||
+ | <code raylib> | ||
+ | // Draw Ball | ||
+ | DrawCircle(ball.x, | ||
+ | </ | ||
+ | |||
+ | 이렇게 기존 x, y등의 변수를 ball로 객체화 하였으므로, | ||
+ | |||
+ | |||
+ | === 다. ball 움직이기 === | ||
+ | |||
+ | void Game:: | ||
+ | |||
+ | <code raylib> | ||
+ | void Game:: | ||
+ | { | ||
+ | // Update | ||
+ | // | ||
+ | // TODO: Update your variables here | ||
+ | // | ||
+ | ball.Update(); | ||
+ | </ | ||
+ | |||
+ | <WRAP center round tip 70%> | ||
+ | 참고로, | ||
+ | </ | ||
+ | |||
+ | |||
+ | === 참고 : 게임 화면 리로딩 ==== | ||
+ | |||
+ | 만약 Game:: | ||
+ | |||
+ | ClearBackground(BLACK)이 없다면 기존에 그려진 배경 위에 계속 업데이트된 공이 그려지므로 | ||
+ | |||
+ | 공의 중첩적으로 그려질 것이다. | ||
+ | |||
+ | |||
+ | |||
+ | ===== 충돌(컬리젼) 판단 하기 ===== | ||
+ | |||
+ | |||
+ | ==== 1. 개념 ==== | ||
+ | |||
+ | 볼이 각 벽을 부딪히면 반대로 튕기게 한다고 하자. | ||
+ | |||
+ | 그렇다면 왼쪽과 위쪽은 | ||
+ | |||
+ | ball.x - radius가 x = 0보다 작으면 반대로 튕겨질 것이고, ball.x + radius가 | ||
+ | |||
+ | 또한 이렇게 x와 y는 각 각 따로 판단되므로 | ||
+ | |||
+ | |||
+ | ==== 2. 변수 정의하기 ==== | ||
+ | |||
+ | 기존에 하나로 나눴던 speed를 x와 y로 나누자. | ||
+ | 그리고 ball클래스는 screen의 사이즈를 할 방버이 없다. 따라서 game.cpp에서 ball에게 screen size를 넘겨줄 함수를 작성하자. | ||
+ | 마지막으로 컬리젼 체크하는 함수를 선언하자. | ||
+ | |||
+ | === 가. ball.h === | ||
+ | |||
+ | <file raylib ball.h> | ||
+ | class Ball | ||
+ | { | ||
+ | public: | ||
+ | int x = 400; | ||
+ | int y = 225; | ||
+ | int radius = 25; | ||
+ | int speed_x = 3; | ||
+ | int speed_y = 3; | ||
+ | |||
+ | Ball(); | ||
+ | ~Ball(); | ||
+ | |||
+ | void Update(); | ||
+ | void SetScreenSize(int width, int height); | ||
+ | |||
+ | private: | ||
+ | int screenWidth = 0, screenHeight = 0; | ||
+ | void CheckCollision(); | ||
+ | }; | ||
+ | </ | ||
+ | |||
+ | |||
+ | === 나. ball.cpp === | ||
+ | |||
+ | ball의 x값과 y값이 각각의 벽에 닿으면 방향을 바꾸도록 다음과 같이 코드를 짜자. | ||
+ | |||
+ | checkcollision 함수는 매 업데이트시마다 확인하도록 불러주자. | ||
+ | |||
+ | <file raylib ball.cpp> | ||
+ | #include " | ||
+ | |||
+ | Ball:: | ||
+ | { | ||
+ | x = 400; | ||
+ | y = 225; | ||
+ | radius = 25; | ||
+ | speed_x = 3; | ||
+ | speed_y = 3; | ||
+ | } | ||
+ | |||
+ | Ball:: | ||
+ | { } | ||
+ | |||
+ | void Ball:: | ||
+ | { | ||
+ | x += speed_x; | ||
+ | y += speed_y; | ||
+ | |||
+ | CheckCollision(); | ||
+ | } | ||
+ | |||
+ | void Ball:: | ||
+ | { | ||
+ | screenWidth = width; | ||
+ | screenHeight = height; | ||
+ | } | ||
+ | |||
+ | void Ball:: | ||
+ | { | ||
+ | if ((x - radius <= 0) or (x + radius >= screenWidth)) { speed_x *= -1;} | ||
+ | |||
+ | if ((y - radius <= 0) or (y + radius >= screenHeight)) {speed_y *= -1;} | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | === 다. game.cpp에 추가 === | ||
+ | |||
+ | 다음과 같이 생성자에서 ball 인스턴스에 현재의 스크린 크기를 알려주는 작업이 필요하다. | ||
+ | |||
+ | <code raylib> | ||
+ | Game:: | ||
+ | { | ||
+ | SetTargetFPS(60); | ||
+ | InitWindow(width, | ||
+ | | ||
+ | screenWidth = width; | ||
+ | screenHeight = height; | ||
+ | |||
+ | ball.SetScreenSize(screenWidth, | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | |||
+ | ===== 페달 클래스 만들기 ===== | ||
+ | |||
+ | ==== 1. 목표 ==== | ||
+ | |||
+ | 볼을 객체화 하였으니 이번에는 양 페달을 객체화 해보자. | ||
+ | |||
+ | 첫째, 왼쪽 페달은 플레이어 페달로서 키보드로 움직이게 할 것이다. | ||
+ | |||
+ | 둘째, 오른쪽 페달은 AI페달로서 공의 위치에 따라 자동으로 움직이게 할 것이다. | ||
+ | |||
+ | 셋째, 기존에 볼 클래스는 데이타만 저장하고 그리기는 game 클래스에서 구현했다. 이번에는 그리기도 각 페달 클래스 내에서 구현하도록 하겠다. | ||
+ | |||
+ | |||
+ | ==== 2. paddle.h 만들기 ==== | ||
+ | |||
+ | === 가. 인클루드 하기 === | ||
+ | |||
+ | 객체 내에서 그리기 함수를 구현할 것이므로 raylib.h 를 인클루드 할 것이다. | ||
+ | |||
+ | 그런데 game.h에서도 이미 한번 raylib를 인클루드 하므로, 다음과 같이 game.h와 paddle.h에 인쿨루드를 한번만 하게 만들어 주자. | ||
+ | |||
+ | <code raylib> | ||
+ | #ifndef __RAYLIB__ | ||
+ | #define __RAYLIB__ | ||
+ | #include " | ||
+ | #endif | ||
+ | </ | ||
+ | |||
+ | === 나. 구조체 만들기 === | ||
+ | |||
+ | 페달은 4개의 점이 있으므로 페달을 구성하는 4개의 점을 구조체로 만들 것이다. | ||
+ | |||
+ | <code raylib> | ||
+ | typedef struct Rect { | ||
+ | float startX; | ||
+ | float startY; | ||
+ | float width; | ||
+ | float height; | ||
+ | } Rect; | ||
+ | </ | ||
+ | |||
+ | === 다. 변수와 함수 선언하기 === | ||
+ | |||
+ | 그 외에 페달에는 스피드가 필요할 것이고, 페달과 벽과의 거리(padding)가 필요할 것이다. | ||
+ | |||
+ | 그리고 꼭 필요한건 아닌데 인간이 생각하기에는 페달의 가장자리부터 생각하는 것보다는 페달의 중심부부터 생각하는게 직관적이므로 페달의 중심부를 x,y라고 하자. | ||
+ | |||
+ | 이런식으로 하여 일단 생성자와 소멸자, 그리고 draw와 update를 선언하기로 하자. 그러면 paddle.h는 다음과 같이 될 것이다. | ||
+ | |||
+ | <file paddle.h> | ||
+ | #ifndef __RAYLIB__ | ||
+ | #define __RAYLIB__ | ||
+ | #include " | ||
+ | #endif | ||
+ | |||
+ | typedef struct Rect { | ||
+ | float startX; | ||
+ | float startY; | ||
+ | float width; | ||
+ | float height; | ||
+ | } Rect; | ||
+ | |||
+ | class Paddle | ||
+ | { | ||
+ | |||
+ | protected: | ||
+ | Rect rect; | ||
+ | int pPadding = 10; | ||
+ | float pSpeed = 2; | ||
+ | float x, y; | ||
+ | |||
+ | |||
+ | public: | ||
+ | Paddle(); | ||
+ | ~Paddle(); | ||
+ | void Update(); | ||
+ | void Draw(); | ||
+ | |||
+ | }; | ||
+ | </ | ||
+ | |||
+ | ==== 3. paddle.cpp 만들기 ==== | ||
+ | === 가. 생성자 만들기 === | ||
+ | |||
+ | 생성자에는 네모의 각 좌표 값을 생성할 것이다. 정확한 좌표 값을 대입할 필요는 없고, 너비와 높이만 여기에서 정확하게 생성하면 된다. | ||
+ | |||
+ | <code raylib> | ||
+ | Paddle:: | ||
+ | { | ||
+ | pPadding = 10; | ||
+ | pSpeed = 2; | ||
+ | rect = {(float)pPadding, | ||
+ | }; | ||
+ | </ | ||
+ | |||
+ | 소멸자에는 아주 것도 안 할 것이므로 여기에서는 생략한다. | ||
+ | |||
+ | === 나. 좌표값 초기화 하기 === | ||
+ | |||
+ | paddle의 정확한 좌표를 알려면 전체 스크린의 사이즈를 알아야 한다. | ||
+ | |||
+ | 그런데 전체 스크린은 game 클래스가 생성되고 나서야 생성되므로 | ||
+ | |||
+ | game에서 전체 스크린을 만든 후 paddle의 좌표를 지정해주는 함수가 필요하다. | ||
+ | |||
+ | 우리는 이를 Paddle:: | ||
+ | |||
+ | 우리가 페들을 움직이게 할 때 y값만 움직이게 할 것이므로, | ||
+ | |||
+ | 그러면 다음과 같이 만들 수 있다. | ||
+ | |||
+ | <code raylib> | ||
+ | void Paddle:: | ||
+ | { | ||
+ | x = (float)pPadding; | ||
+ | y = GetScreenHeight() / 2; | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | |||
+ | === 다. Draw 만들기 === | ||
+ | |||
+ | ball 클래스와 달리 이번에는 paddle 클래스 내에서 raylib를 사용할 것이다. 그러면 다음과 같이 구현할 수 있을 것이다. | ||
+ | |||
+ | <code raylib> | ||
+ | void Paddle:: | ||
+ | { | ||
+ | // Draw Player Rectangle | ||
+ | DrawRectangle(rect.startX, | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | === 라. Update 만들기 === | ||
+ | |||
+ | raylib에서 키입력은 IsKeyDown()함수를 이용하면 된다. 위와 아래 키에 따라 y값을 바뀌게 해보자. | ||
+ | |||
+ | 참고로, 직관적인 이해를 위하여 y값을 페들의 중심으로 설정했다. 따라서 y값이 변하면 rect의 startY도 같이 변하게 만들어줘야 하는 것을 잊지 말자. | ||
+ | |||
+ | <code raylib> | ||
+ | void Paddle:: | ||
+ | { | ||
+ | if (IsKeyDown(KEY_UP)) y -= pSpeed; | ||
+ | if (IsKeyDown(KEY_DOWN)) y += pSpeed; | ||
+ | |||
+ | rect.startY = y - rect.height; | ||
+ | }; | ||
+ | </ | ||
+ | |||
+ | === 마. paddle의 이동한계 만들어 보기 === | ||
+ | |||
+ | 다음과 같이 paddle의 이동 한계선을 설정하자. | ||
+ | |||
+ | <code raylib> | ||
+ | void Paddle:: | ||
+ | { | ||
+ | if (y <= rect.height / 2) {y = rect.height / 2;} | ||
+ | |||
+ | if (y >= (GetScreenHeight() - rect.height / 2)) {y = GetScreenHeight() - rect.height / 2;} | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | |||
+ | 이 함수는 paddle의 update함수에서 불러줘야 할 것이다. | ||
+ | |||
+ | === 비. game에서 구현해 보기 === | ||
+ | |||
+ | 이제 game.cpp에서 플레이어 페달을 구현하자. 전체 소스는 다음과 같다. | ||
+ | |||
+ | <file raylib game.cpp> | ||
+ | #include " | ||
+ | |||
+ | #ifndef __RAYLIB__ | ||
+ | #define __RAYLIB__ | ||
+ | #include " | ||
+ | #endif | ||
+ | #include " | ||
+ | #include " | ||
+ | |||
+ | Ball ball = Ball(); | ||
+ | Paddle player = Paddle(); | ||
+ | |||
+ | Game:: | ||
+ | { | ||
+ | SetTargetFPS(60); | ||
+ | InitWindow(width, | ||
+ | | ||
+ | screenWidth = width; | ||
+ | screenHeight = height; | ||
+ | | ||
+ | ball.SetScreenSize(screenWidth, | ||
+ | player.Init(); | ||
+ | } | ||
+ | |||
+ | Game:: | ||
+ | { | ||
+ | CloseWindow(); | ||
+ | } | ||
+ | |||
+ | bool Game:: | ||
+ | { | ||
+ | return WindowShouldClose(); | ||
+ | } | ||
+ | |||
+ | void Game:: | ||
+ | { | ||
+ | BeginDrawing(); | ||
+ | Update(); | ||
+ | Draw(); | ||
+ | EndDrawing(); | ||
+ | |||
+ | } | ||
+ | |||
+ | void Game:: | ||
+ | { | ||
+ | ClearBackground(BLACK); | ||
+ | |||
+ | // Center Line | ||
+ | DrawLine(screenWidth | ||
+ | |||
+ | // Draw Ball | ||
+ | DrawCircle(ball.x, | ||
+ | |||
+ | // Draw Player Rectangle | ||
+ | player.Draw(); | ||
+ | } | ||
+ | |||
+ | void Game:: | ||
+ | { | ||
+ | ball.Update(); | ||
+ | player.Update(); | ||
+ | } | ||
+ | |||
+ | |||
+ | </ | ||
+ | |||
+ | === 사. 결과 === | ||
+ | |||
+ | 다음 그림과 같이 나올 것이다. | ||
+ | |||
+ | {{: | ||
+ | |||
+ | |||
+ | ==== 4. 정리하기 ==== | ||
+ | |||
+ | 일단 여기까지 공과 플레이어를 그려봤다. | ||
+ | |||
+ | 지금까지의 소스파일은 다음과 같다. | ||
+ | |||
+ | {{ : | ||
+ | |||
+ | |||
+ | ===== 에네미 페달 그리기 ===== | ||
+ | |||
+ | ==== 1. paddle.h 선언 ===== | ||
+ | |||
+ | 에네미 페달은 기존 paddle을 상속할 것이다. 그리고 업데이트 부분만 따로 구현하면 된다. | ||
+ | |||
+ | 다음과 같다. | ||
+ | |||
+ | |||
+ | <code raylib> | ||
+ | class CPUPaddle : public Paddle | ||
+ | { | ||
+ | public: | ||
+ | |||
+ | void Init(); | ||
+ | void Update(float ball_x, float ball_y, float ball_radius); | ||
+ | }; | ||
+ | </ | ||
+ | |||
+ | 페달은 공의 y좌표에 따라 움직이게 할 것이므로 ball_y변수가 필요하다. | ||
+ | |||
+ | 나머지 입력 변수들은 후에 충돌을 감지하기 위해 미리 선언하였다. | ||
+ | |||
+ | ==== 2. paddle.cpp ==== | ||
+ | |||
+ | === 가. init() 함수 === | ||
+ | |||
+ | 에메니 페달의 초기화는 다음과 같다. 즉, x좌표만 화면의 오른쪽으로 해주면 된다. | ||
+ | |||
+ | <code raylib> | ||
+ | void CPUPaddle:: | ||
+ | { | ||
+ | x = GetScreenWidth() - (float)pPadding - rect.width; | ||
+ | y = GetScreenHeight() | ||
+ | |||
+ | rect.startX = x; | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | === 나. update 함수 === | ||
+ | |||
+ | 공의 y좌표에 따라 페달의 y 값이 변하게 하면된다. 그러면 다음과 같이 될 것이다. | ||
+ | |||
+ | 페달은 y값이 페달의 정중앙을 의미하므로 rect.startY는 정중앙에서 페달의 높이의 2분의1을 빼야한다. | ||
+ | |||
+ | <code raylib> | ||
+ | void CPUPaddle:: | ||
+ | { | ||
+ | CheckPosition(); | ||
+ | |||
+ | if (y > ball_y) {y -= pSpeed;} | ||
+ | if (y < ball_y) {y += pSpeed;} | ||
+ | |||
+ | rect.startY = y - rect.height / 2; | ||
+ | |||
+ | } | ||
+ | </ | ||
+ | |||
+ | === 다. draw함수 === | ||
+ | |||
+ | 기존 페달 클래스의 draw를 상속받는 것으로 충분하므로 굳이 따로 정의해줄 필요가 없다. | ||
+ | |||
+ | |||
+ | ==== 3. game.cpp에서 구현 ==== | ||
+ | |||
+ | === 가. 생성 === | ||
+ | |||
+ | 최상단에 다음과 같이 생성하자. | ||
+ | |||
+ | <code raylib> | ||
+ | CPUPaddle AI = CPUPaddle(); | ||
+ | </ | ||
+ | |||
+ | === 나. 초기값 설정 === | ||
+ | |||
+ | Game:: | ||
+ | |||
+ | <code raylib> | ||
+ | AI.Init() | ||
+ | </ | ||
+ | |||
+ | === 다. 기타 === | ||
+ | |||
+ | 나머지 update()와 draw()에도 각각 | ||
+ | |||
+ | AI.update()와 AI.draw()를 넣어주면 된다. | ||
+ | |||
+ | 그러면 다음 그림과 같이 된다. | ||
+ | |||
+ | 공의 y위치에 따라 에메니 페달이 따라 움직이는 것을 알 수 있다. | ||
+ | |||
+ | {{: | ||
+ | |||
+ | |||
+ | ==== 4. 정리 ==== | ||
+ | |||
+ | 지금까지 작성한 소스는 다음과 같다. | ||
+ | |||
+ | {{ : | ||
+ | |||
+ | |||
+ | ===== 페달과 공의 컬리젼 로직 구현하기 ===== | ||
+ | |||
+ | ==== 1. 기본 개념 ==== | ||
+ | |||
+ | 페달과 공이 부딪히면 공이 반대편으로 튕겨가게 할 것이다. 공과 벽의 컬리젼과 마찬가지로 공의 방향 스피드만 바꿔주면 된다. | ||
+ | |||
+ | 그런데 공과 페달의 부딪힘을 어느 단계에서 할 것인가가 고민된디. | ||
+ | |||
+ | game.cpp에서는 공과 페달 양자를 모두 불러올 수 있으므로 game.cpp에서 공의 충돌을 판단하는 것이 가장 편할 것이다. | ||
+ | |||
+ | 그래서 다음과 같이 game:: | ||
+ | |||
+ | <code raylib> | ||
+ | void Game:: | ||
+ | { | ||
+ | ball.Update(); | ||
+ | Vector2 ballPos = {ball.x, ball.y}; | ||
+ | |||
+ | player.Update(); | ||
+ | Rectangle playerRect = player.getRect(); | ||
+ | | ||
+ | if (CheckCollisionCircleRec(ballPos, | ||
+ | | ||
+ | AI.Update(ball.x, | ||
+ | Rectangle AIRect = AI.getRect(); | ||
+ | if (CheckCollisionCircleRec(ballPos, | ||
+ | |||
+ | } | ||
+ | |||
+ | </ | ||
+ | |||
+ | 여기서 Vector2와 Rectangle은 모두 [[https:// | ||
+ | Vector2는 float 두개의 구조체이고, | ||
+ | |||
+ | ==== 2. paddle클래스에서 getRect() 함수 ==== | ||
+ | |||
+ | raylib에서 rectangle구조체를 만들어두었으므로 사실 우리는 rect구조체를 만들 필요는 없었다. | ||
+ | |||
+ | 그러나 기존에 이미 rect구조체를 만들어서 사용하였으므로 이를 rectangle로 변환하여 리턴하는 함수가 필요하다. | ||
+ | |||
+ | paddle.h와 paddle.cpp에서 다음과 같이 구현했다. | ||
+ | |||
+ | <code raylib> | ||
+ | Rectangle Paddle:: | ||
+ | { | ||
+ | return {rect.startX, | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | |||
+ | ==== 3. CheckCollisionCircleRec() 함수 ===== | ||
+ | |||
+ | 구와 네모상자간의 컬리젼은 raylib의 내장함수인 CheckCollisionCircleRect(Vector2 center, float radius, Rectangle rect)함수를 이용하였다. | ||
+ | |||
+ | 첫 2변수가 구의 값이고, 3번째가 박스의 값이다. | ||
+ | |||
+ | 직접 함수를 만들 수도 있겟지만, | ||
+ | |||
+ | 구와 네모상자가 컬리젼을 하면 ball에서 공을 튕기게 하였다. | ||
+ | |||
+ | ==== 4. ball.bounce(int i) 함수 ==== | ||
+ | |||
+ | 다음과 같이 함수를 만들었다. | ||
+ | |||
+ | 아주 간단하지만, | ||
+ | |||
+ | ball의 x스피드는 3씩 움직이게 하였으므로, | ||
+ | |||
+ | 그런데 충돌과 동시에 볼의 위치를 1씩 옮기면 볼이 다음 플레임에서는 이미 충돌 판정의 밖에 위치하게 되므로 재차 충돌 판정을 하는 불상사를 막을 수 있다. | ||
+ | |||
+ | <code raylib> | ||
+ | void Ball:: | ||
+ | { | ||
+ | speed_x *= -1; | ||
+ | |||
+ | if (dic == 1) | ||
+ | { | ||
+ | x += 2; | ||
+ | }else | ||
+ | { | ||
+ | x -= 2; | ||
+ | } | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | |||
+ | ==== 4. 정리 ==== | ||
+ | |||
+ | 일단 이렇게 하면 간단하게나마 게임의 모양은 갖추게 된 것을 알 수 있다. | ||
+ | |||
+ | {{ : | ||
+ | |||
+ | |||
+ | ===== 게임스코어와 게임 리셋 ===== | ||
+ | |||
+ | ==== 1. 게임 스코어 그리기 ==== | ||
+ | |||
+ | === 가. 변수 설정하기 === | ||
+ | |||
+ | 플레이어 점수와 에네미 점수를 저장하기로 한다. | ||
+ | |||
+ | 따라서 game.h에 각각 변수를 선언하기로 한다. | ||
+ | |||
+ | <code raylib> | ||
+ | int PlayerScore = 0; | ||
+ | int EnemyScore =0; | ||
+ | </ | ||
+ | |||
+ | === 나. 스코어 텍스트 그리기 === | ||
+ | |||
+ | 레이라이브에서 텍스트를 그릴때에는 DrawText 함수를 사용한다. | ||
+ | |||
+ | // Draw text (using default font) | ||
+ | void DrawText(const char *text, int posX, int posY, int fontSize, Color color); | ||
+ | | ||
+ | 또한 raylib에서는 TextFormat 함수를 지원하는데, | ||
+ | |||
+ | 따라서 위 2개를 조합해서 스코어 점수를 그리면 다음 코드와 같다. | ||
+ | | ||
+ | |||
+ | <code raylib> | ||
+ | // Draw Score | ||
+ | DrawText(TextFormat(" | ||
+ | |||
+ | DrawText(TextFormat(" | ||
+ | </ | ||
+ | |||
+ | ==== 2. 스코어 점수 올리기 ==== | ||
+ | === 가. 개념 === | ||
+ | |||
+ | 이제 스코어를 표현하는 것은 했으니 스코어 점수를 올려야 할 것이다. | ||
+ | |||
+ | 플레이어가 왼쪽에 있으므로 오른쪽에 위치한 적이 공을 걷어내지 못하여 오른쪽 벽에 부딪히면 플레이어 점수가 오를 것이고, | ||
+ | |||
+ | 그 반대면 적의 점수가 오를 것이다. | ||
+ | |||
+ | |||
+ | 이러한 왼쪽과 오른쪽 벽에 대한 충돌 판정은 이전에 ball클래스에서 했었다. 따라서 ball클래스에서 왼쪽과 오른쪽 벽의 충돌 판정값을 불러와야 할 것이다. | ||
+ | |||
+ | === 나. enum class 작성하기 === | ||
+ | |||
+ | 공이 어디에 부딪혔는지를 판단해야 하니까, 열거형을 클래스로 만들어서 리턴해줄 값을 구성하자. | ||
+ | |||
+ | ball.h에 다음과 같이 enum class를 만들자 | ||
+ | |||
+ | <code raylib> | ||
+ | enum class ColResult | ||
+ | { | ||
+ | None, Player, Enemy | ||
+ | }; | ||
+ | </ | ||
+ | |||
+ | === 다. 점수 획득 충돌 판정하여 리턴하기 === | ||
+ | |||
+ | 기존에는 ball이 양옆에 닿으면 x_speed값을 반전시키게 하였지만, | ||
+ | |||
+ | 따라서 다음과 같이 CheckCollision()함수를 변경하면 될 것이다. | ||
+ | |||
+ | <code raylib> | ||
+ | ColResult Ball:: | ||
+ | { | ||
+ | if (x - radius <= 0) | ||
+ | return ColResult:: | ||
+ | | ||
+ | if (x + radius >= screenWidth) | ||
+ | return ColResult:: | ||
+ | |||
+ | |||
+ | if ((y - radius <= 0) or (y + radius >= screenHeight)) {speed_y *= -1;} | ||
+ | |||
+ | return ColResult:: | ||
+ | }; | ||
+ | </ | ||
+ | |||
+ | 이 값은 Update()에서 불러오는 것이므로 Ball:: | ||
+ | |||
+ | === 라. 판정값 가져와서 점수 올리기 === | ||
+ | |||
+ | 이제 판정 값을 가져왔으므로 game.cpp에서 다음과 같이 각각의 점수를 올리면 된다. | ||
+ | |||
+ | <code raylib> | ||
+ | ColResult ballResult = ball.Update(); | ||
+ | |||
+ | if (ballResult == ColResult:: | ||
+ | { | ||
+ | PlayerScore++; | ||
+ | }else if(ballResult == ColResult:: | ||
+ | { | ||
+ | EnemyScore++; | ||
+ | }; | ||
+ | </ | ||
+ | |||
+ | |||
+ | ==== 3. 게임 리셋하기 ==== | ||
+ | |||
+ | === 가. 개념 === | ||
+ | |||
+ | 공이 양 옆으로 빠지면 공과 플레이어 위치를 각각 원위치로 다시 세팅해주면 된다. 플레이어와 에네미는 이미 초기화하는 함수를 만들어 두었으므로 어렵지 않다 따라서 공의 Reset 함수를 만들면 된다. | ||
+ | |||
+ | 공은 리셋될때마다 다른 방향으로 튀게 할 것이므로, | ||
+ | |||
+ | === 나. 공의 리셋함수 === | ||
+ | |||
+ | ball.h에 reset()함수를 void로 선언하자. | ||
+ | |||
+ | 이후에 ball.cpp에 다음과 같이 함수를 만든다. | ||
+ | |||
+ | <code raylib> | ||
+ | void Ball:: | ||
+ | { | ||
+ | x = screenWidth / 2; | ||
+ | y = screenHeight / 2; | ||
+ | radius = 20; | ||
+ | |||
+ | int speed_choice[2] = {-1, 1}; | ||
+ | speed_x *= speed_choice[GetRandomValue(0, | ||
+ | speed_y *= speed_choice[GetRandomValue(0, | ||
+ | |||
+ | } | ||
+ | </ | ||
+ | |||
+ | GetRandomValue는 레이라이브에서 제공하는 랜더 값 리턴 함수이다. 0부터 1까지 중에서 하나를 고르게 하는 것이다. | ||
+ | 우리는 speed_choice라는 int 배열 중에서 0과 1번째 값을 랜덤하게 고르게 된다. | ||
+ | 따라서 기존의 공의 스피드를 랜덤하게 반전시킬 수 있다. | ||
+ | |||
+ | === 다. game.cpp === | ||
+ | |||
+ | Update()함수 안에 다음과 같이 수정하면 된다. | ||
+ | |||
+ | <code raylib> | ||
+ | if (ballResult == ColResult:: | ||
+ | { | ||
+ | PlayerScore++; | ||
+ | ball.Reset(); | ||
+ | player.Init(); | ||
+ | AI.Init(); | ||
+ | }else if(ballResult == ColResult:: | ||
+ | { | ||
+ | EnemyScore++; | ||
+ | ball.Reset(); | ||
+ | player.Init(); | ||
+ | AI.Init(); | ||
+ | }; | ||
+ | </ | ||
+ | |||
+ | |||
+ | ==== 4. 결론 ==== | ||
+ | |||
+ | {{: | ||
+ | |||
+ | 이제 게임을 완성하였다. | ||
+ | |||
+ | 완성된 소스파일은 다음과 같다. | ||
+ | |||
+ | {{ : | ||
+ | 여기까지 만들어봤으면 기본적인 객체지향에 대하여 이해가 되었을 것이다. | ||
+ | 이후에는 [[raylib: |
raylib/pong게임.1695311367.txt.gz · 마지막으로 수정됨: 2023/09/22 00:49 저자 이거니맨