사용자 도구

사이트 도구


raylib:pong게임
pong게임

차이

문서의 선택한 두 판 사이의 차이를 보여줍니다.

차이 보기로 링크

양쪽 이전 판이전 판
다음 판
이전 판
raylib:pong게임 [2023/09/28 13:13] 이거니맨raylib:pong게임 [2023/11/20 00:29] (현재) 이거니맨
줄 1: 줄 1:
 ~~stoggle_buttons~~ ~~stoggle_buttons~~
 +
 +
 +{{:raylib:raylib_chapter4.png?600|Raylib Pong Chapter4}}
 +
  
 pong 게임을 만들어보자   pong 게임을 만들어보자  
줄 643: 줄 647:
  
  
-====정리하기 ===== +==== 4. 정리하기 ==== 
  
 일단 여기까지 공과 플레이어를 그려봤다. 일단 여기까지 공과 플레이어를 그려봤다.
  
-다음 시간는 상속을 해보고, 공과 페달과의 컬리젼에 대하여 구현해 보자 +지금까지의 소스파일은 다음과 같다. 
 + 
 +{{ :raylib:raylibpong_chap1.zip |raylib pong 제1강 소스파일}} 
 + 
 + 
 +===== 네미 페달 그리기 =====  
 + 
 +==== 1. paddle.h 선언 ===== 
 + 
 +에네미 페달은 기존 paddle을 상속할 것이다. 그리고 업데이트 부분만 따로 구현하면 된다.  
 + 
 +다음과 같다. 
 + 
 + 
 +<code raylib> 
 +class CPUPaddle : public Paddle 
 +
 +    public:  
 + 
 +    void Init(); 
 +    void Update(float ball_x, float ball_y, float ball_radius); 
 +}; 
 +</code> 
 + 
 +페달은 공의 y좌표에 따라 움직이게 할 것이므로 ball_y변수가 필요하다. 
 + 
 +나머지 입력 변수들은 후에 충돌을 감지하기 위해 미리 선언하였다. 
 + 
 +==== 2. paddle.cpp ==== 
 + 
 +=== 가. init() 함수 === 
 + 
 +에메니 페달의 초기화는 다음과 같다. 즉x좌표만 화면의 오른쪽으로 해주면 된다. 
 + 
 +<code raylib> 
 +void CPUPaddle::Init() 
 +
 +    x = GetScreenWidth() - (float)pPadding - rect.width; 
 +    y = GetScreenHeight()  / 2;  
 + 
 +    rect.startX = x; 
 +
 +</code> 
 + 
 +=== 나. update 함수 === 
 + 
 +의 y좌표에 따라 페달의 y 값이 변하게 하면된다. 그러면 다음과 같이 될 것이다. 
 + 
 +페달은 y값이 페달의 정중앙을 의미하므로 rect.startY는 정중앙에서 페달의 높이의 2분의1을 빼야한다. 
 + 
 +<code raylib> 
 +void CPUPaddle::Update(float ball_x, float ball_y, float ball_radius) 
 +
 +    CheckPosition(); 
 + 
 +    if (y > ball_y) {y -= pSpeed;} 
 +    if (y < ball_y) {y += pSpeed;} 
 + 
 +    rect.startY = y - rect.height / 2; 
 + 
 +
 +</code>  
 + 
 +=== 다. draw함수 ===  
 + 
 +기존 페달 클래스의 draw를 상속받는 것으로 충분하므로 굳이 따로 정의해줄 필요가 없다. 
 + 
 + 
 +==== 3. game.cpp에서 구현 ==== 
 + 
 +=== 가. 생성 ===  
 + 
 +최상단에 다음과 같이 생성하자. 
 + 
 +<code raylib> 
 +CPUPaddle AI = CPUPaddle(); 
 +</code> 
 + 
 +=== 나. 초기값 설정 === 
 + 
 +Game::Game() 함수에 다음과 같이 초기값을 설정하도록 하자. 
 + 
 +<code raylib> 
 +AI.Init() 
 +</code>  
 + 
 +=== 다. 기타 ===  
 + 
 +나머지 update()와 draw()에도 각각 
 + 
 +AI.update()와 AI.draw()를 넣어주면 된다. 
 + 
 +그러면 다음 그림과 같이 된다.  
 + 
 +공의 y위치에 따라 에메니 페달이 따라 움직이는 것을 알 수 있다. 
 + 
 +{{:raylib:raylibponenemypaddle.png?600|Raylib Pong Enemy Paddle}} 
 + 
 + 
 +==== 4. 정리 ====  
 + 
 +지금까지 작성한 소스는 다음과 같다. 
 + 
 +{{ :raylib:raylibpong_chap2.zip |Raylib Pong Chaper2}} 
 + 
 + 
 +===== 페달과 공의 컬리젼 로직 구현하기 =====  
 + 
 +==== 1. 기본 개념 ====  
 + 
 +페달과 공이 부딪히면 공이 반편으로 튕겨가게 할 것이다. 공과 벽의 컬리젼과 마찬가지로 공의 방향 스피드만 바꿔주면 된다.  
 + 
 +그런데 공과 페달의 부딪힘을 어느 단계에서 할 것인가가 고민된디.  
 + 
 +game.cpp에서는 공과 페달 양자를 모두 불러올 수 있으므로 game.cpp에서 공의 충돌을 판단는 것이 가장 편할 것이다. 
 + 
 +그래서 다음과 같이 game::update() 함수를 작성했다. 
 + 
 +<code raylib> 
 +void Game::Update() 
 +
 +    ball.Update(); 
 +    Vector2 ballPos = {ball.x, ball.y};  // 공의 위치  
 + 
 +    player.Update(); 
 +    Rectangle playerRect = player.getRect(); // 플레이어 위치 가져오기  
 +     
 +    if (CheckCollisionCircleRec(ballPos, ball.radius, playerRect)) {ball.Bounce(1);};  // 플레이어와 공과의 충돌 확인 
 +     
 +    AI.Update(ball.x, ball.y, ball.radius); 
 +    Rectangle AIRect = AI.getRect();   // AI 이치 가져오기  
 +    if (CheckCollisionCircleRec(ballPos, ball.radius, AIRect)) {ball.Bounce(0);};  // AI와 공과의 충돌 확인  
 + 
 +
 + 
 +</code>  
 + 
 +기서 Vector2와 Rectangle은 모두 [[https://www.raylib.com/cheatsheet/cheatsheet.html|raylib에서 마련해준 구조체]]이다.  
 +Vector2는 float 두개의 구조체이고, rectnagle은 float 4개로 구성되어 있다. 
 + 
 +==== 2. paddle클래스에서 getRect() 함수 ====  
 + 
 +raylib에서 rectangle구조체를 만들어두었으므로 사실 우리는 rect구조체를 만들 필요는 없었다.  
 + 
 +그러나 기존에 이미 rect구조체를 만들어서 사용하였으므로 이를 rectangle로 변환하여 리턴하는 함수가 필요하다. 
 + 
 +paddle.h와 paddle.cpp에서 다음과 같이 구현했다.  
 + 
 +<code raylib> 
 +Rectangle Paddle::getRect() 
 +
 +    return {rect.startX, rect.startY, rect.width, rect.height}; 
 +
 +</code>  
 + 
 + 
 +==== 3. CheckCollisionCircleRec() 함수 =====  
 + 
 +구와 네모상자간의 컬리젼은 raylib의 내장함수인 CheckCollisionCircleRect(Vector2 center, float radius, Rectangle rect)함수를 이용하였다.  
 + 
 +첫 2변수가 구의 값이고, 3번째가 박스의 값이다.  
 + 
 +직접 함수를 만들 수도 있겟지만, 굳이 우리가 컬리젼 알고리즘에 관심 있는 것은 아니므로 여기에서는 패스하도록 하자.  
 + 
 +구와 네모상자가 컬리젼을 하면 ball에서 공을 튕기게 하였다.  
 + 
 +==== 4. ball.bounce(int i) 함수 ====  
 + 
 +다음과 같이 함수를 만들었다.  
 + 
 +아주 간단하지만, 플레이어를 부딪히면 볼의 위치를 +1로 하고, 에네미를 부딪히면 볼의 x위치를 -1로 하였다.  
 + 
 +ball의 x스피드는 3씩 움직이게 하였으므로, ball이 x위치를 바꿔서 업데이트 루프를 돌아서 한번 더 움직인후 충돌 판정을 하여도 또 충돌판정에 걸릴 가능성이 있다. 
 + 
 +그런데 충돌과 동시에 볼의 위치를 1씩 옮기면 볼이 다음 플레임에서는 이미 충돌 판정의 밖에 위치하게 되므로 재차 충돌 판정을 하는 불상사를 막을 수 있다.  
 + 
 +<code raylib> 
 +void Ball::Bounce(bool dic) 
 +
 +    speed_x *= -1; 
 + 
 +    if (dic == 1)  
 +    { 
 +        x += 2; 
 +    }else 
 +    { 
 +        x -= 2;  
 +    } 
 +
 +</code>  
 + 
 + 
 +==== 4. 정리 ==== 
 + 
 +일단 이렇게 하면 간단하게나마 게임의 모양은 갖추게 된 것을 알 수 있다.  
 + 
 +{{ :raylib:raylibpong_chap3.zip |Raylib Pong Chapter3}}  
 + 
 + 
 +===== 게임스코어와 게임 리셋 ===== 
 + 
 +==== 1. 게임 스코어 그리기 ====  
 + 
 +=== 가. 변수 설정하기 ===  
 + 
 +플레이어 점수와 에네미 점수를 저장하기로 한다. 
 + 
 +따라서 game.h에 각각 변수를 선언하기로 한다. 
 + 
 +<code raylib>  
 +        int PlayerScore = 0; 
 +        int EnemyScore =0; 
 +</code> 
 + 
 +=== 나. 스코어 텍스트 그리기 === 
 + 
 +레이라이브에서 텍스트를 그릴때에는 DrawText 함수를 사용한다.  
 + 
 +  // Draw text (using default font) 
 +  void DrawText(const char *text, int posX, int posY, int fontSize, Color color);        
 +   
 +또한 raylib에서는 TextFormat 함수를 지원하는데, 이는 sprintf와 비슷하다고 보면 된다.  
 + 
 +따라서 위 2개를 조합서 스코어 점수를 그리면 다음 코드와 같다.  
 +   
 + 
 +<code raylib> 
 +        // Draw Score  
 +        DrawText(TextFormat("Player : %i", PlayerScore), screenWidth / 8 -  10, 10, 40, DARKGREEN); 
 + 
 +        DrawText(TextFormat("Enemy : %i", EnemyScore), screenWidth / 8 * 5  - 10, 10, 40, DARKPURPLE); 
 +</code>  
 + 
 +==== 2. 스코어 점수 올리기 ==== 
 +=== 가. 개념 ===  
 + 
 +이제 스코어를 표현하는 것은 했으니 스코어 점수를 올려야 할 것이다.  
 + 
 +플레이어가 왼쪽에 있으므로 오른쪽에 위치한 적이 공을 걷어내지 못하여 오른쪽 벽에 부딪히면 플레이어 점수가 오를 것이고, 
 + 
 +그 반대면 적의 점수가 오를 것이다.  
 + 
 + 
 +이러한 왼쪽과 오른쪽 벽에 대한 충돌 판정은 이전에 ball클래스에서 했었다. 따라서 ball클래스에서 왼쪽과 오른쪽 벽의 충돌 판정값을 불러와야 할 것이다.  
 + 
 +=== 나. enum class 작성하기 === 
 + 
 +공이 어디에 부딪혔는지를 판단해야 하니까, 열거형을 클래스로 만들어서 리턴해줄 값을 구성하자.  
 + 
 +ball.h에 다음과 같이 enum class를 만들자  
 + 
 +<code raylib> 
 +enum class ColResult  
 +
 +    None, Player, Enemy 
 +};  
 +</code>  
 + 
 +=== 다. 점수 획득 충돌 판정하여 리턴하기 ===  
 + 
 +기존에는 ball이 양옆에 닿으면 x_speed값을 반전시키게 하였지만, 이제는 점수를 획득하는 것으로 바꾸어야 한다. 
 + 
 +따라서 다음과 같이 CheckCollision()함수를 변경하면 될 것이다.  
 + 
 +<code raylib> 
 +ColResult Ball::CheckCollision() 
 +
 +    if (x - radius <= 0)  
 +        return ColResult::Enemy; 
 +     
 +    if (x + radius >= screenWidth)  
 +        return ColResult::Player; 
 + 
 + 
 +    if ((y - radius <= 0) or (y + radius >= screenHeight)) {speed_y *= -1;} 
 + 
 +    return ColResult::None; 
 +}; 
 +</code>  
 + 
 +이 값은 Update()에서 불러오는 것이므로 Ball::Update()함수도 ColResult를 리턴하게 바꾸는 것을 잊지 말자.  
 + 
 +=== 라. 판정값 가져와서 점수 올리기 ===  
 + 
 +이제 판정 값을 가져왔으므로 game.cpp에서 다음과 같이 각각의 점수를 올리면 된다.  
 + 
 +<code raylib> 
 +    ColResult ballResult = ball.Update();  
 + 
 +    if (ballResult == ColResult::Player)  
 +    { 
 +        PlayerScore++; 
 +    }else if(ballResult == ColResult::Enemy) 
 +    { 
 +        EnemyScore++; 
 +    }; 
 +</code> 
 + 
 + 
 +==== 3. 게임 리셋하기 ====  
 + 
 +=== 가. 개념 ===  
 + 
 +공이 양 옆으로 빠지면 공과 플레이어 위치를 각각 원위치로 다시 세팅해주면 된다. 플레이어와 에네미는 이미 초기화하는 함수를 만들어 두었으므로 어렵지 않다 따라서 공의 Reset 함수를 만들면 된다. 
 + 
 +공은 리셋될때마다 다른 방향으로 튀게 할 것이므로, 랜덤하게 속도를 바꿔줘야 한다.  
 + 
 +=== 나. 공의 리셋함수 === 
 + 
 +ball.h에 reset()함수를 void로 선언하자.  
 + 
 +이후에 ball.cpp에 다음과 같이 함수를 만든다.  
 + 
 +<code raylib> 
 +void Ball::Reset() 
 +
 +    x = screenWidth / 2; 
 +    y = screenHeight / 2; 
 +    radius = 20;  
 + 
 +    int speed_choice[2] = {-1, 1}; 
 +    speed_x *= speed_choice[GetRandomValue(0, 1)];  
 +    speed_y *= speed_choice[GetRandomValue(0, 1)]; 
 + 
 +
 +</code> 
 + 
 +GetRandomValue는 레이라이브에서 제공하는 랜더 값 리턴 함수이다. 0부터 1까지 중에서 하나를 고르게 하는 것이다.  
 +우리는 speed_choice라는 int 배열 중에서 0과 1번째 값을 랜덤하게 고르게 된다.  
 +따라서 기존의 공의 스피드를 랜덤하게 반전시킬 수 있다.  
 + 
 +=== 다. game.cpp ===  
 + 
 +Update()함수 안에 다음과 같이 수정하면 된다. 
 + 
 +<code raylib> 
 +    if (ballResult == ColResult::Player)  
 +    { 
 +        PlayerScore++; 
 +        ball.Reset(); 
 +        player.Init(); 
 +        AI.Init(); 
 +    }else if(ballResult == ColResult::Enemy) 
 +    { 
 +        EnemyScore++; 
 +        ball.Reset(); 
 +        player.Init(); 
 +        AI.Init(); 
 +    }; 
 +</code> 
 + 
 + 
 +==== 4. 결론 ====  
 + 
 +{{:raylib:raylib_chapter4.png?600|Raylib Pong Chapter4}}  
 + 
 +이제 게임을 완성하였다. 
 + 
 +완성된 소스파일은 다음과 같다. 
 + 
 +{{ :raylib:raylibpong_chap4.zip |Raylib Pong Chapter4}} 
 + 
 +여기까지 만들어봤으면 기본적인 객체지향에 대하여 이해가 되었을 것이다.  
 +이후에는 [[raylib:flappybird:flappy_bird_만들기|Flappy Bird만들기]]를 만들어 보자
raylib/pong게임.1695874392.txt.gz · 마지막으로 수정됨: 2023/09/28 13:13 저자 이거니맨