애니메이션 시스템 구현
오늘은 SDL2를 사용하여 좀 더 복잡한 애니메이션 시스템을 구현하는 방법을 학습하겠습니다. 다양한 캐릭터의 움직임을 표현하는 방법을 배워보겠습니다.
1. 애니메이션 시스템 개요
애니메이션 시스템은 여러 프레임으로 이루어진 스프라이트 시트를 사용하여 캐릭터나 객체의 움직임을 표현합니다. 각 애니메이션은 여러 프레임으로 구성되며, 각 프레임은 일정 시간 동안 화면에 표시됩니다.
2. 애니메이션 클래스 설계
애니메이션을 관리하기 위해 Animation
클래스를 설계합니다. 이 클래스는 프레임 정보를 저장하고, 애니메이션을 업데이트하는 역할을 합니다.
헤더 파일 작성
include/Animation.h
파일을 생성하고 다음과 같이 작성합니다.
#ifndef ANIMATION_H
#define ANIMATION_H
#include <vector>
#include <SDL2/SDL.h>
class Animation {
public:
Animation(int frameWidth, int frameHeight, int numFrames, Uint32 frameDuration);
void AddFrame(int x, int y);
void Update();
void Render(SDL_Renderer* renderer, SDL_Texture* texture, int x, int y);
private:
std::vector<SDL_Rect> frames;
int currentFrame;
int frameWidth;
int frameHeight;
int numFrames;
Uint32 frameDuration;
Uint32 lastFrameTime;
};
#endif // ANIMATION_H
소스 파일 작성
src/Animation.cpp
파일을 생성하고 다음과 같이 작성합니다.
#include "Animation.h"
Animation::Animation(int frameWidth, int frameHeight, int numFrames, Uint32 frameDuration)
: frameWidth(frameWidth), frameHeight(frameHeight), numFrames(numFrames), frameDuration(frameDuration),
currentFrame(0), lastFrameTime(0) {}
void Animation::AddFrame(int x, int y) {
SDL_Rect frame;
frame.x = x;
frame.y = y;
frame.w = frameWidth;
frame.h = frameHeight;
frames.push_back(frame);
}
void Animation::Update() {
Uint32 currentTime = SDL_GetTicks();
if (currentTime > lastFrameTime + frameDuration) {
currentFrame = (currentFrame + 1) % numFrames;
lastFrameTime = currentTime;
}
}
void Animation::Render(SDL_Renderer* renderer, SDL_Texture* texture, int x, int y) {
SDL_Rect destRect = { x, y, frameWidth, frameHeight };
SDL_RenderCopy(renderer, texture, &frames[currentFrame], &destRect);
}
3. 게임 엔진 수정
애니메이션 클래스를 사용하여 캐릭터 애니메이션을 구현합니다.
헤더 파일 수정
include/GameEngine.h
파일을 수정하여 Animation
클래스를 포함합니다.
#ifndef GAMEENGINE_H
#define GAMEENGINE_H
#include <SDL2/SDL.h>
#include <SDL2/SDL_image.h>
#include "Animation.h"
class GameEngine {
public:
GameEngine();
~GameEngine();
bool Initialize(const char* title, int width, int height);
void Run();
void Shutdown();
private:
SDL_Window* window;
SDL_Renderer* renderer;
SDL_Texture* texture;
Animation* animation;
bool isRunning;
Uint32 frameStart;
int frameTime;
void HandleEvents();
void Update();
void Render();
bool LoadTexture(const char* filePath);
};
#endif // GAMEENGINE_H
소스 파일 수정
src/GameEngine.cpp
파일을 다음과 같이 수정합니다.
#include "GameEngine.h"
#include <iostream>
const int FPS = 60;
const int FRAME_DELAY = 1000 / FPS;
GameEngine::GameEngine()
: window(nullptr), renderer(nullptr), texture(nullptr), animation(nullptr), isRunning(false), frameStart(0), frameTime(0) {}
GameEngine::~GameEngine() {
Shutdown();
}
bool GameEngine::Initialize(const char* title, int width, int height) {
if (SDL_Init(SDL_INIT_VIDEO) != 0) {
std::cerr << "SDL_Init Error: " << SDL_GetError() << std::endl;
return false;
}
window = SDL_CreateWindow(title, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, width, height, SDL_WINDOW_SHOWN);
if (window == nullptr) {
std::cerr << "SDL_CreateWindow Error: " << SDL_GetError() << std::endl;
return false;
}
renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC);
if (renderer == nullptr) {
std::cerr << "SDL_CreateRenderer Error: " << SDL_GetError() << std::endl;
return false;
}
if (!(IMG_Init(IMG_INIT_PNG) & IMG_INIT_PNG)) {
std::cerr << "IMG_Init Error: " << IMG_GetError() << std::endl;
return false;
}
if (!LoadTexture("path/to/your/sprite_sheet.png")) {
return false;
}
// 애니메이션 생성
animation = new Animation(64, 64, 4, 200); // 프레임 너비, 높이, 총 프레임 수, 프레임 지속 시간 (밀리초 단위)
for (int i = 0; i < 4; ++i) {
animation->AddFrame(i * 64, 0); // 프레임 추가
}
isRunning = true;
return true;
}
bool GameEngine::LoadTexture(const char* filePath) {
SDL_Surface* tempSurface = IMG_Load(filePath);
if (tempSurface == nullptr) {
std::cerr << "IMG_Load Error: " << IMG_GetError() << std::endl;
return false;
}
texture = SDL_CreateTextureFromSurface(renderer, tempSurface);
SDL_FreeSurface(tempSurface);
if (texture == nullptr) {
std::cerr << "SDL_CreateTextureFromSurface Error: " << SDL_GetError() << std::endl;
return false;
}
return true;
}
void GameEngine::Run() {
while (isRunning) {
frameStart = SDL_GetTicks();
HandleEvents();
Update();
Render();
frameTime = SDL_GetTicks() - frameStart;
if (FRAME_DELAY > frameTime) {
SDL_Delay(FRAME_DELAY - frameTime);
}
}
}
void GameEngine::HandleEvents() {
SDL_Event event;
while (SDL_PollEvent(&event)) {
if (event.type == SDL_QUIT) {
isRunning = false;
}
if (event.type == SDL_KEYDOWN) {
switch (event.key.keysym.sym) {
case SDLK_ESCAPE:
isRunning = false;
break;
// 여기에 추가 입력 처리를 구현합니다.
default:
break;
}
}
}
}
void GameEngine::Update() {
animation->Update();
}
void GameEngine::Render() {
// 화면을 검은색으로 지우기
SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255);
SDL_RenderClear(renderer);
// 애니메이션 렌더링
animation->Render(renderer, texture, 200, 150);
// 렌더링 결과를 화면에 출력
SDL_RenderPresent(renderer);
}
void GameEngine::Shutdown() {
if (animation) {
delete animation;
animation = nullptr;
}
if (texture) {
SDL_DestroyTexture(texture);
texture = nullptr;
}
if (renderer) {
SDL_DestroyRenderer(renderer);
renderer = nullptr;
}
if (window) {
SDL_DestroyWindow(window);
window = nullptr;
}
IMG_Quit();
SDL_Quit();
}
4. 스프라이트 시트 준비
애니메이션을 위해 여러 프레임이 포함된 스프라이트 시트 이미지를 준비합니다. 예제에서는 path/to/your/sprite_sheet.png
경로에 스프라이트 시트를 배치합니다. 이 경로를 실제 이미지 파일 경로로 변경해야 합니다.
5. 프로젝트 빌드 및 실행
- Visual Studio에서 CMake 프로젝트 열기:
- Visual Studio를 실행하고,
File
->Open
->CMake...
를 선택합니다. GameEngine
디렉토리를 선택하여 프로젝트를 엽니다.
- Visual Studio를 실행하고,
- SDL2_image 라이브러리 설치:
- SDL2_image 라이브러리를 프로젝트에 설정합니다.
- 프로젝트 빌드:
- Visual Studio 상단의
Build
메뉴에서Build All
을 선택하여 프로젝트를 빌드합니다.
- Visual Studio 상단의
- 프로젝트 실행:
Debug
메뉴에서Start Without Debugging
을 선택하여 프로그램을 실행합니다.- 윈도우 창이 생성되고, 애니메이션이 화면에 렌더링됩니다.
마무리
오늘은 SDL2를 사용하여 복잡한 애니메이션 시스템을 구현하는 방법을 학습했습니다. 다양한 캐릭터의 움직임을 표현하는 방법을 배웠습니다. 다음 단계에서는 타일맵을 렌더링하는 방법을 배우고, 게임 맵을 만드는 기초를 학습하겠습니다.
질문이나 추가적인 피드백이 있으면 언제든지 댓글로 남겨 주세요.
Day 11 예고
다음 날은 "타일맵 렌더링"에 대해 다룰 것입니다. 타일맵을 사용하여 게임 맵을 생성하고 렌더링하는 방법을 배워보겠습니다.
'-----ETC----- > C++로 배우는 게임 엔진 개발' 카테고리의 다른 글
[C++로 배우는 게임 엔진 개발] Day 8: 2D 그래픽스 기초 (SDL2) (0) | 2024.08.01 |
---|---|
[C++로 배우는 게임 엔진 개발] Day 9: 스프라이트 렌더링 (0) | 2024.08.01 |
[C++로 배우는 게임 엔진 개발] Day 11: 타일맵 렌더링 (0) | 2024.08.01 |
[C++로 배우는 게임 엔진 개발] Day 12: 간단한 물리 엔진 구현 (AABB 충돌) (0) | 2024.08.01 |
[C++로 배우는 게임 엔진 개발] Day 13: 사운드 시스템 기초 (SDL_mixer) (0) | 2024.08.01 |