목차

  

Raylib Pong Chapter4

pong 게임을 만들어보자

템플릿 이용하기

전에 설명한 템플릿을 복사한 후 복사한 폴더를 “RaylibPong”으로 이름짓자.

이제 여기에서 부터 코딩을 시작할 것이다.

기초 그리기

일단 다음과 같이 화면에 그림을 그릴 것이다. pong 게임은 2개의 페달과 1개의 공이 있으면 된다. 부가적으로, 가운데를 알릴 수 있는 가운데 선을 하나 그리면 좋을 것이다.

Pong 기초 그리기

1. 가운데 선 그리기

가. 화면 크기 변수 선언하기

우리는 screenWidth와 screenHeight를 main.cpp에 선언하였다. 그런데 우리는 game.cpp 에서 게임 로직을 돌릴 것이다.

따라서 game.h에 screenWidth와 screenHeight를 다시 선언하고 이를 받아줘야 한다.

game.h 의 Game클래스의 private에 다음을 선언하자.

        int screenWidth = 800;
        int screenHeight = 450; 

이제 이를 game.cpp에서 저장하자.

Game::Game(int width, int height, std::string title) 
{
    SetTargetFPS(60);               // Set our game to run at 60 frames-per-second
    InitWindow(width, height, title.c_str()); 
 
    screenWidth = width; 
    screenHeight = height; 
}

나. 가운데 선 그리기

raylib에서 DrawLine 함수는 (X시작점, Y시작점, X끝점, Y끝점, 선색깔)의 순이다.

자세한 것은 항상 raylib의 cheatsheet를 참조하자.

따라서 화면의 X가운데에서 수직으로 선을 그리려면 다음과 같이 함수를 선언해 주면 된다.

        ClearBackground(BLACK);
 
        DrawLine(screenWidth  / 2, 0, screenWidth / 2, screenHeight, RAYWHITE);

2. 움직이는 공 그리기

가. 환경 변수 설정하기

공은 위치 변수로서 x, y가 있을 것이고 반지름으로 r이 있을 것이다.

game.h에 다음과 같이 공의 변수를 설정한다.

    private: 
 
        int x = 400;
        int y = 225; 
        int r = 50;  

나. 공 그리기

raylib에서 원을 그리는 함수는 DrawCircle 함수이다. game.cpp에 공을 그려보자

void Game::Draw()
{
 
    // Draw
    //----------------------------------------------------------------------------------
 
        ClearBackground(BLACK);
 
        // Center Line
        DrawLine(screenWidth  / 2, 0, screenWidth / 2, screenHeight, RAYWHITE);
 
        // Draw Ball
        DrawCircle(x, y, r, RAYWHITE);
 
    //----------------------------------------------------------------------------------
}

다. 참고 : 키보드 키로 공을 움직이게 해보기

raylib에서 키보드 키 입력은 IsKeyDown()을 이용한다. 다음과 같이 Game::Update()문에 키보드 입력을 코딩해보자.

game.cpp 파일에 추가하는 것이다.

void Game::Update()
{
    // Update
    //----------------------------------------------------------------------------------
    // TODO: Update your variables here
    //----------------------------------------------------------------------------------
 
 
    if (IsKeyDown(KEY_RIGHT)) x += 2; 
    if (IsKeyDown(KEY_LEFT)) x -= 2; 
    if (IsKeyDown(KEY_UP)) y -= 2; 
    if (IsKeyDown(KEY_DOWN)) y += 2; 
}

여기 까지 따라 왔다면 키보드 방향키에 따라 공이 움직인다는 것을 알 수 있다.

3. 페달 그리기

가. 페달 변수 설정하기

페달은 벽과의 간격을 의미하는 padding과 페달자체의 너비와 높이를 의미하는 width와 height가 있을 것이다.

이를 game.h에 추가하자.

        // Rectangle 
        int rPadding = 10; 
        int rWidth = 20; 
        int rHeight = 120;

나. 페달 그리기

왼쪽과 오른쪽의 페달은 아래와 같이 변수를 입력하면 그려질 것이다. game.cpp의 일부분은 다음과 같다.

        // Draw Left Rectangle
        DrawRectangle(rPadding, (screenHeight - rHeight) / 2, rWidth, rHeight, BLUE);
 
        // Draw Right Rectangle 
        DrawRectangle(screenWidth - rPadding - rWidth, (screenHeight - rHeight) / 2, rWidth, rHeight, BLUE);

4. 결론

지금까지 잘 따라왔다면 다음과 같이 그려질 것이다.

Pong 그림 그리기 완료

클래스화 하기

1. ball.h

움직이는 공을 클래스화 하자.

ball.h에 다음과 같이 공의 속성을 정의하자. 공에는 좌표로 x와 y가 있을 것이고, 반지름 값이 radius, 속도 값인 speed가 있을 것이다.

생성자와 소멸자 그리고 Update 함수도 같이 정의하였다.

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 위치와 y위치를 3씩 더하게 만들었다.

ball.cpp
#include "ball.h"
 
Ball::Ball()
{
    x = 400;
    y = 225;
    radius = 40;
    speed = 5; 
}
 
Ball::Ball(int x, int y, int radius, int speed)
{
    x = x;
    y = y;
    radius = radius;
    speed = speed;
}
 
Ball::~Ball()
{
 
}
 
void Ball::Update()
{
    x += speed;
    y += speed;
}

3. game 로직에서 구현

가. Ball 인스턴스 생성하기

game.cpp 파일을 수정하자.

다음과 같이 ball.h를 인클루드 한 후에, 파일의 최상단에 ball 인스턴스를 만들자.

game클라스의 어느 메서드에서든 돌아가야 하므로 game생성자 안에서 ball 인스턴스를 만들면 안된다.

#include "ball.h"
 
Ball ball = Ball();

이렇게 인스턴스를 만들면 ball초기화 되었으므로 이제 game 클라스 내에서 ball을 사용할 수 있따.

나. ball 그리기

void Game::Draw()메소드 내에서 기존의 draw circle을 다음과 같이 ball의 값으로 바꾸면 기존과 동일하게 ball 이 그려진다.

        // Draw Ball
        DrawCircle(ball.x, ball.y, ball.radius, RAYWHITE);

이렇게 기존 x, y등의 변수를 ball로 객체화 하였으므로, game.h에 정의한 x, y, radius 등은 삭제해도 된다.

다. ball 움직이기

void Game::Update() 에 다음과 같이 ball객체 내의 함수를 실행시킨다.

void Game::Update()
{
    // Update
    //----------------------------------------------------------------------------------
    // TODO: Update your variables here
    //----------------------------------------------------------------------------------
    ball.Update();

참고로, Game::Tick()에서 update 메서드가 draw보다 먼저 실행되는 것을 알 수 있다.

참고 : 게임 화면 리로딩

만약 Game::Draw() 메소드 내에서

ClearBackground(BLACK)이 없다면 기존에 그려진 배경 위에 계속 업데이트된 공이 그려지므로

공의 중첩적으로 그려질 것이다.

충돌(컬리젼) 판단 하기

1. 개념

볼이 각 벽을 부딪히면 반대로 튕기게 한다고 하자.

그렇다면 왼쪽과 위쪽은 x = 0일테고, y = 0일 것이다. 오른쪽은 x = screenWidth일테고, y = screenHeight일 것이다.

ball.x - radius가 x = 0보다 작으면 반대로 튕겨질 것이고, ball.x + radius가 x = screenWidth 보다 크다면 다시 튕겨질 것이다.

또한 이렇게 x와 y는 각 각 따로 판단되므로 speed도 x와 y로 각각 나눠 줘야 할 것이다.

2. 변수 정의하기

기존에 하나로 나눴던 speed를 x와 y로 나누자. 그리고 ball클래스는 screen의 사이즈를 할 방버이 없다. 따라서 game.cpp에서 ball에게 screen size를 넘겨줄 함수를 작성하자. 마지막으로 컬리젼 체크하는 함수를 선언하자.

가. ball.h

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 함수는 매 업데이트시마다 확인하도록 불러주자.

ball.cpp
#include "ball.h"
 
Ball::Ball()
{
    x = 400;
    y = 225;
    radius = 25;
    speed_x = 3; 
    speed_y = 3; 
}
 
Ball::~Ball()
{ }
 
void Ball::Update()
{
    x += speed_x;
    y += speed_y; 
 
    CheckCollision();
}
 
void Ball::SetScreenSize(int width, int height)
{
    screenWidth = width;
    screenHeight = height;
}
 
void Ball::CheckCollision()
{
    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 인스턴스에 현재의 스크린 크기를 알려주는 작업이 필요하다.

Game::Game(int width, int height, std::string title) 
{
    SetTargetFPS(60);               // Set our game to run at 60 frames-per-second
    InitWindow(width, height, title.c_str()); 
 
    screenWidth = width; 
    screenHeight = height;  
 
    ball.SetScreenSize(screenWidth, screenHeight);
}

페달 클래스 만들기

1. 목표

볼을 객체화 하였으니 이번에는 양 페달을 객체화 해보자.

첫째, 왼쪽 페달은 플레이어 페달로서 키보드로 움직이게 할 것이다.

둘째, 오른쪽 페달은 AI페달로서 공의 위치에 따라 자동으로 움직이게 할 것이다.

셋째, 기존에 볼 클래스는 데이타만 저장하고 그리기는 game 클래스에서 구현했다. 이번에는 그리기도 각 페달 클래스 내에서 구현하도록 하겠다.

2. paddle.h 만들기

가. 인클루드 하기

객체 내에서 그리기 함수를 구현할 것이므로 raylib.h 를 인클루드 할 것이다.

그런데 game.h에서도 이미 한번 raylib를 인클루드 하므로, 다음과 같이 game.h와 paddle.h에 인쿨루드를 한번만 하게 만들어 주자.

#ifndef __RAYLIB__
#define __RAYLIB__
#include "raylib.h"
#endif

나. 구조체 만들기

페달은 4개의 점이 있으므로 페달을 구성하는 4개의 점을 구조체로 만들 것이다.

typedef struct Rect {
    float startX;
    float startY;
    float width;
    float height;
} Rect;

다. 변수와 함수 선언하기

그 외에 페달에는 스피드가 필요할 것이고, 페달과 벽과의 거리(padding)가 필요할 것이다.

그리고 꼭 필요한건 아닌데 인간이 생각하기에는 페달의 가장자리부터 생각하는 것보다는 페달의 중심부부터 생각하는게 직관적이므로 페달의 중심부를 x,y라고 하자.

이런식으로 하여 일단 생성자와 소멸자, 그리고 draw와 update를 선언하기로 하자. 그러면 paddle.h는 다음과 같이 될 것이다.

#ifndef __RAYLIB__
#define __RAYLIB__
#include "raylib.h"
#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 만들기

가. 생성자 만들기

생성자에는 네모의 각 좌표 값을 생성할 것이다. 정확한 좌표 값을 대입할 필요는 없고, 너비와 높이만 여기에서 정확하게 생성하면 된다.

Paddle::Paddle()
{
    pPadding = 10;
    pSpeed = 2;
    rect = {(float)pPadding, 0, 10, 100};
};

소멸자에는 아주 것도 안 할 것이므로 여기에서는 생략한다.

나. 좌표값 초기화 하기

paddle의 정확한 좌표를 알려면 전체 스크린의 사이즈를 알아야 한다.

그런데 전체 스크린은 game 클래스가 생성되고 나서야 생성되므로

game에서 전체 스크린을 만든 후 paddle의 좌표를 지정해주는 함수가 필요하다.

우리는 이를 Paddle::Init()로 만들기로 하자.

우리가 페들을 움직이게 할 때 y값만 움직이게 할 것이므로, x는 그대로 왼쪽 위로 두되 y값만 페들의 가운데로 만들어두자.

그러면 다음과 같이 만들 수 있다.

void Paddle::Init()
{
    x = (float)pPadding;
    y = GetScreenHeight() / 2; 
}

다. Draw 만들기

ball 클래스와 달리 이번에는 paddle 클래스 내에서 raylib를 사용할 것이다. 그러면 다음과 같이 구현할 수 있을 것이다.

void Paddle::Draw()
{
    // Draw Player Rectangle
    DrawRectangle(rect.startX, rect.startY, rect.width, rect.height, BLUE);
}

라. Update 만들기

raylib에서 키입력은 IsKeyDown()함수를 이용하면 된다. 위와 아래 키에 따라 y값을 바뀌게 해보자.

참고로, 직관적인 이해를 위하여 y값을 페들의 중심으로 설정했다. 따라서 y값이 변하면 rect의 startY도 같이 변하게 만들어줘야 하는 것을 잊지 말자.

void Paddle::Update()
{
    if (IsKeyDown(KEY_UP)) y -= pSpeed; 
    if (IsKeyDown(KEY_DOWN)) y += pSpeed; 
 
    rect.startY = y - rect.height;
}; 

마. paddle의 이동한계 만들어 보기

다음과 같이 paddle의 이동 한계선을 설정하자.

void Paddle::CheckPosition()
{
    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에서 플레이어 페달을 구현하자. 전체 소스는 다음과 같다.

game.cpp
#include "game.h"
 
#ifndef __RAYLIB__
#define __RAYLIB__
#include "raylib.h"
#endif
#include "ball.h"
#include "paddle.h"
 
Ball ball = Ball();
Paddle player = Paddle(); 
 
Game::Game(int width, int height, std::string title) 
{
    SetTargetFPS(60);               // Set our game to run at 60 frames-per-second
    InitWindow(width, height, title.c_str()); 
 
    screenWidth = width; 
    screenHeight = height;  
 
    ball.SetScreenSize(screenWidth, screenHeight);
    player.Init();
}
 
Game::~Game()
{
    CloseWindow();                  // Close window and OpenGL context
} 
 
bool Game::GameShouldClose() const
{
    return WindowShouldClose();
}
 
void Game::Tick()
{
    BeginDrawing();
    Update();
    Draw();
    EndDrawing();
 
}
 
void Game::Draw()
{
        ClearBackground(BLACK);
 
        // Center Line
        DrawLine(screenWidth  / 2, 0, screenWidth / 2, screenHeight, RAYWHITE);
 
        // Draw Ball
        DrawCircle(ball.x, ball.y, ball.radius, RAYWHITE);
 
        // Draw Player Rectangle
        player.Draw();
}
 
void Game::Update()
{
    ball.Update();
    player.Update();
}

사. 결과

다음 그림과 같이 나올 것이다.

Raylib Pong Player Paddle

4. 정리하기

일단 여기까지 공과 플레이어를 그려봤다.

지금까지의 소스파일은 다음과 같다.

raylib pong 제1강 소스파일

에네미 페달 그리기

1. paddle.h 선언

에네미 페달은 기존 paddle을 상속할 것이다. 그리고 업데이트 부분만 따로 구현하면 된다.

다음과 같다.

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좌표만 화면의 오른쪽으로 해주면 된다.

void CPUPaddle::Init()
{
    x = GetScreenWidth() - (float)pPadding - rect.width;
    y = GetScreenHeight()  / 2; 
 
    rect.startX = x;
}

나. update 함수

공의 y좌표에 따라 페달의 y 값이 변하게 하면된다. 그러면 다음과 같이 될 것이다.

페달은 y값이 페달의 정중앙을 의미하므로 rect.startY는 정중앙에서 페달의 높이의 2분의1을 빼야한다.

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;
 
}

다. draw함수

기존 페달 클래스의 draw를 상속받는 것으로 충분하므로 굳이 따로 정의해줄 필요가 없다.

3. game.cpp에서 구현

가. 생성

최상단에 다음과 같이 생성하자.

CPUPaddle AI = CPUPaddle();

나. 초기값 설정

Game::Game() 함수에 다음과 같이 초기값을 설정하도록 하자.

AI.Init()

다. 기타

나머지 update()와 draw()에도 각각

AI.update()와 AI.draw()를 넣어주면 된다.

그러면 다음 그림과 같이 된다.

공의 y위치에 따라 에메니 페달이 따라 움직이는 것을 알 수 있다.

Raylib Pong Enemy Paddle

4. 정리

지금까지 작성한 소스는 다음과 같다.

Raylib Pong Chaper2

페달과 공의 컬리젼 로직 구현하기

1. 기본 개념

페달과 공이 부딪히면 공이 반대편으로 튕겨가게 할 것이다. 공과 벽의 컬리젼과 마찬가지로 공의 방향 스피드만 바꿔주면 된다.

그런데 공과 페달의 부딪힘을 어느 단계에서 할 것인가가 고민된디.

game.cpp에서는 공과 페달 양자를 모두 불러올 수 있으므로 game.cpp에서 공의 충돌을 판단하는 것이 가장 편할 것이다.

그래서 다음과 같이 game::update() 함수를 작성했다.

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와 공과의 충돌 확인 
 
}

여기서 Vector2와 Rectangle은 모두 raylib에서 마련해준 구조체이다. Vector2는 float 두개의 구조체이고, rectnagle은 float 4개로 구성되어 있다.

2. paddle클래스에서 getRect() 함수

raylib에서 rectangle구조체를 만들어두었으므로 사실 우리는 rect구조체를 만들 필요는 없었다.

그러나 기존에 이미 rect구조체를 만들어서 사용하였으므로 이를 rectangle로 변환하여 리턴하는 함수가 필요하다.

paddle.h와 paddle.cpp에서 다음과 같이 구현했다.

Rectangle Paddle::getRect()
{
    return {rect.startX, rect.startY, rect.width, rect.height};
}

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씩 옮기면 볼이 다음 플레임에서는 이미 충돌 판정의 밖에 위치하게 되므로 재차 충돌 판정을 하는 불상사를 막을 수 있다.

void Ball::Bounce(bool dic)
{
    speed_x *= -1;
 
    if (dic == 1) 
    {
        x += 2;
    }else
    {
        x -= 2; 
    }
}

4. 정리

일단 이렇게 하면 간단하게나마 게임의 모양은 갖추게 된 것을 알 수 있다.

Raylib Pong Chapter3

게임스코어와 게임 리셋

1. 게임 스코어 그리기

가. 변수 설정하기

플레이어 점수와 에네미 점수를 저장하기로 한다.

따라서 game.h에 각각 변수를 선언하기로 한다.

        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 함수를 지원하는데, 이는 sprintf와 비슷하다고 보면 된다.

따라서 위 2개를 조합해서 스코어 점수를 그리면 다음 코드와 같다.

        // 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);

2. 스코어 점수 올리기

가. 개념

이제 스코어를 표현하는 것은 했으니 스코어 점수를 올려야 할 것이다.

플레이어가 왼쪽에 있으므로 오른쪽에 위치한 적이 공을 걷어내지 못하여 오른쪽 벽에 부딪히면 플레이어 점수가 오를 것이고,

그 반대면 적의 점수가 오를 것이다.

이러한 왼쪽과 오른쪽 벽에 대한 충돌 판정은 이전에 ball클래스에서 했었다. 따라서 ball클래스에서 왼쪽과 오른쪽 벽의 충돌 판정값을 불러와야 할 것이다.

나. enum class 작성하기

공이 어디에 부딪혔는지를 판단해야 하니까, 열거형을 클래스로 만들어서 리턴해줄 값을 구성하자.

ball.h에 다음과 같이 enum class를 만들자

enum class ColResult 
{
    None, Player, Enemy
}; 

다. 점수 획득 충돌 판정하여 리턴하기

기존에는 ball이 양옆에 닿으면 x_speed값을 반전시키게 하였지만, 이제는 점수를 획득하는 것으로 바꾸어야 한다.

따라서 다음과 같이 CheckCollision()함수를 변경하면 될 것이다.

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;
};

이 값은 Update()에서 불러오는 것이므로 Ball::Update()함수도 ColResult를 리턴하게 바꾸는 것을 잊지 말자.

라. 판정값 가져와서 점수 올리기

이제 판정 값을 가져왔으므로 game.cpp에서 다음과 같이 각각의 점수를 올리면 된다.

    ColResult ballResult = ball.Update(); 
 
    if (ballResult == ColResult::Player) 
    {
        PlayerScore++;
    }else if(ballResult == ColResult::Enemy)
    {
        EnemyScore++;
    };

3. 게임 리셋하기

가. 개념

공이 양 옆으로 빠지면 공과 플레이어 위치를 각각 원위치로 다시 세팅해주면 된다. 플레이어와 에네미는 이미 초기화하는 함수를 만들어 두었으므로 어렵지 않다 따라서 공의 Reset 함수를 만들면 된다.

공은 리셋될때마다 다른 방향으로 튀게 할 것이므로, 랜덤하게 속도를 바꿔줘야 한다.

나. 공의 리셋함수

ball.h에 reset()함수를 void로 선언하자.

이후에 ball.cpp에 다음과 같이 함수를 만든다.

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)];
 
}

GetRandomValue는 레이라이브에서 제공하는 랜더 값 리턴 함수이다. 0부터 1까지 중에서 하나를 고르게 하는 것이다. 우리는 speed_choice라는 int 배열 중에서 0과 1번째 값을 랜덤하게 고르게 된다. 따라서 기존의 공의 스피드를 랜덤하게 반전시킬 수 있다.

다. game.cpp

Update()함수 안에 다음과 같이 수정하면 된다.

    if (ballResult == ColResult::Player) 
    {
        PlayerScore++;
        ball.Reset();
        player.Init();
        AI.Init();
    }else if(ballResult == ColResult::Enemy)
    {
        EnemyScore++;
        ball.Reset();
        player.Init();
        AI.Init();
    };

4. 결론

Raylib Pong Chapter4

이제 게임을 완성하였다.

완성된 소스파일은 다음과 같다.

Raylib Pong Chapter4

여기까지 만들어봤으면 기본적인 객체지향에 대하여 이해가 되었을 것이다. 이후에는 Flappy Bird만들기를 만들어 보자.