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;
}
반응형