Game Programming in C++
Game Programming in C++ Chapter1 - 정리
skyho
2025. 8. 22. 21:19
반응형

책의 소스 코드를 직접 작성해보면서 정리한 글입니다. (실습환경: windows)
책 소스 코드: GitHub - gameprogcpp/code: Game Programming in C++ Code
Chapter1에서는 Game이라는 하나의 객체를 만들었다.
Game 객체의 동작은 다음과 같다.
1. processInput(): 사용자 입력을 받아 paddle의 방향을 설정
2. updateGame(): delta time을 기준으로 게임 세상을 업데이트
3. generateOutput(): 업데이트 된 게임을 화면에 출력
void Game::runLoop()
{
while (_running)
{
processInput();
updateGame();
generateOutput();
}
}
전체 소스
1. game.hpp
#include "SDL3/SDL.h"
struct Vector2
{
float x, y;
};
class Game
{
public:
Game();
bool initialize(int w = 1024, int h = 768); //초기화 함수
void runLoop(); // 게임 실행 함수
void shutdown(); // 게임 종료 함수
private:
void generateOutput(); // 화면 갱신 함수
void processInput(); // 사용자 입력 처리
void updateGame(); // 게임 갱신
SDL_Window* _sdl_window;
int _w;
int _h;
bool _running;
SDL_Renderer* _sdl_renderer;
const float _thickness; // 벽 두께
Vector2 _paddle_pos; // paddle 위치
Vector2 _ball_pos; // ball 위치
const float _paddle_h; // paddle 높이
int _paddle_dir; // paddle 방향(위/아래)
Uint64 _ticks_cnt;
Vector2 _ball_velicity; // ball 방향
};
2. game.cpp
#include "Game.hpp"
Game::Game() : _sdl_window(nullptr), _w(1024), _h(768), _running(true), _sdl_renderer(nullptr),
_thickness(15.0f), _paddle_h(100.0f), _paddle_dir(0), _ticks_cnt(0)
{
}
//initialize SDL and Create window
bool Game::initialize(int w, int h)
{
bool sdl_result = SDL_Init(SDL_INIT_VIDEO);
if (!sdl_result)
{
SDL_Log("Unable to initialize SDL[%d]: [%s]", sdl_result, SDL_GetError());
return false;
}
_sdl_window = SDL_CreateWindow(
"Game Programming in C++ (Chapter 1)", // Window title
w, // Width of window
h, // Height of window
0 // Flags (0 for no flags set)
);
if (_sdl_window == nullptr)
{
SDL_Log("Failed to create window: %s", SDL_GetError());
return false;
}
_sdl_renderer = SDL_CreateRenderer( //SDL_CreateRenderer is changed in SDL3
_sdl_window,
NULL // the name of rendering driver to init, if it is NULL, SDLL will choose one.
);
if (_sdl_renderer == nullptr)
{
SDL_Log("Failed to create renderer: %s", SDL_GetError());
return false;
}
_w = w;
_h = h;
_ball_pos.x = static_cast<float>(w) / 2.0f;
_ball_pos.y = static_cast<float>(h) / 2.0f;
_paddle_pos.x = 10.0f;
_paddle_pos.y = static_cast<float>(h) / 2.0f;
_ball_velicity.x = -200.0f;
_ball_velicity.y = 235.0f;
return true;
}
void Game::runLoop()
{
while (_running)
{
processInput();
updateGame();
generateOutput();
}
}
void Game::shutdown()
{
SDL_DestroyRenderer(_sdl_renderer);
SDL_DestroyWindow(_sdl_window);
SDL_Quit();
}
void Game::generateOutput()
{
//set draw color to blue
SDL_SetRenderDrawColor(
_sdl_renderer,
0, //R
0, //G
255, //B
SDL_ALPHA_OPAQUE // 투명도(transparency) / 255 = 완전한 색, 0 = 완전히 투명
);
// blue 색상으로 전체를 칠한다
SDL_RenderClear(_sdl_renderer);
//draw color to white
SDL_SetRenderDrawColor(_sdl_renderer, 255, 255, 255, 255);
SDL_FRect wall{
0.0f, //top of left x
0.0f, //top of left y
static_cast<float>(_w), //width
_thickness //high
};
//top wall
SDL_RenderFillRect(_sdl_renderer, &wall);
//bottom wall
wall.y = static_cast<float>(_h) - _thickness;
SDL_RenderFillRect(_sdl_renderer, &wall);
//right wall
wall.x = static_cast<float>(_w) - _thickness;
wall.y = 0;
wall.w = _thickness;
wall.h = static_cast<float>(_h);
SDL_RenderFillRect(_sdl_renderer, &wall);
//paddle
SDL_FRect paddle{
_paddle_pos.x,
_paddle_pos.y - _paddle_h / 2,
_thickness,
_paddle_h
};
SDL_RenderFillRect(_sdl_renderer, &paddle);
//ball
SDL_FRect ball{
_ball_pos.x - _thickness / 2,
_ball_pos.y - _thickness / 2,
_thickness,
_thickness
};
SDL_RenderFillRect(_sdl_renderer, &ball);
//렌더링한 화면을 출력한다.
SDL_RenderPresent(_sdl_renderer);
}
void Game::processInput()
{
SDL_Event event;
while (SDL_PollEvent(&event)) //if event is occurred in queue, it will return TRUE
{
switch (event.type)
{
case SDL_EVENT_QUIT: //windows x icon is pressed
_running = false;
break;
}
}
const bool* state = SDL_GetKeyboardState(NULL);
if (state[SDL_SCANCODE_ESCAPE]) //Esc is pressed
{
_running = false;
}
_paddle_dir = 0;
if (state[SDL_SCANCODE_W])
{
_paddle_dir -= 1;
}
if (state[SDL_SCANCODE_S])
{
_paddle_dir += 1;
}
}
void Game::updateGame()
{
Uint64 current_time = SDL_GetTicks();
while (current_time < _ticks_cnt + 16) //16ms
{
current_time = SDL_GetTicks();
}
//delta time = last frame - current frame
//converting to per second
float delta_time = (SDL_GetTicks() - _ticks_cnt) / 1000.0f;
//delta time validation
//maximum delta time is 0.05 second
if (delta_time > 0.05f)
{
delta_time = 0.05f;
}
_ticks_cnt = SDL_GetTicks(); // update tick count for next frame
if (_paddle_dir != 0)
{
//moving speed is 300.0f/sec
_paddle_pos.y += _paddle_dir * 300.0f * delta_time;
//if out of wall
if (_paddle_pos.y < _paddle_h / 2.0f + _thickness)
{
_paddle_pos.y = _paddle_h / 2.0f + _thickness;
}
else if (_paddle_pos.y > (static_cast<float>(_h) - _paddle_h / 2.0f - _thickness))
{
_paddle_pos.y = static_cast<float>(_h) - _paddle_h / 2.0f - _thickness;
}
}
//change in the ball position
_ball_pos.x += _ball_velicity.x * delta_time;
_ball_pos.y += _ball_velicity.y * delta_time;
//collision detection
float diff = _paddle_pos.y - _ball_pos.y;
diff = (diff > 0.0f) ? diff : (diff * -1);
if (
diff <= (_paddle_h / 2.0f) && // Our y-difference is small enough
((20.0f <= _ball_pos.x) && (_ball_pos.x <= 25.0f)) && // We are in the correct x-position
_ball_velicity.x < 0.0f // The ball is moving to the left
)
{
_ball_velicity.x *= -1.0f;
}
else if (_ball_pos.x <= 0.0f) // game over
{
//_is_running = false;
_ball_pos.x = static_cast<float>(_w) / 2.0f;
_ball_pos.y = static_cast<float>(_h) / 2.0f;
}
//collision detection with the right wall
else if ((_ball_pos.x >= (static_cast<float>(_w) - _thickness)) && (_ball_velicity.x > 0.0f))
{
_ball_velicity.x *= -1;
}
//collision detection with the top wall
if ((_ball_pos.y <= _thickness) && (_ball_velicity.y < 0.0f))
{
_ball_velicity.y *= -1;
}
//collision detection with the bottom wall
else if ((_ball_pos.y >= (static_cast<float>(_h) - _thickness)) && (_ball_velicity.y > 0.0f))
{
_ball_velicity.y *= -1;
}
}
3. Main.cpp
#include "Game.h"
int main(int argc, char** argv)
{
Game game;
bool success = game.Initialize();
if (success)
{
game.RunLoop();
}
game.Shutdown();
return 0;
}반응형