1. 기존 코드 정리하기
a. 프로젝트 복사하기
- 목적: 기존에 작업한 코드를 보존하면서 새로운 프로젝트를 생성하여 작업을 이어가기 위함.
- 방법: 기존 프로젝트를 복사해서 새 프로젝트로 만든다. 복사는 프로젝트 폴더 자체를 복사하여 새로운 위치에 붙여넣는 방식으로 이루어짐.
- 결과: 새로운 프로젝트 이름은
GameCoding3으로 설정되었으며, 기존 코드를 삭제하지 않고 작업을 새로 시작할 수 있다.
b. 불필요한 파일 삭제
- 프로젝트 복사 후, 사용하지 않는 파일들을 삭제. 이는 프로젝트에서 제거하는 것이 아니라 실제 파일을 디렉토리에서 삭제함.
.txt 파일도 함께 삭제.
- obj 파일:
obj 파일은 빌드의 중간과정에서 생성되는 파일로, 삭제해도 다시 생성되므로 지워도 무방.
- 주로
x64 폴더에 위치.
c. 폴더 정리하기
- 새로 정리된 폴더 구조:
- Resources: 리소스를 관리하는 폴더.
- Binaries: 빌드 결과물이 저장되는 폴더.
- Intermediate: 빌드 과정에서 중간에 생성되는 파일을 저장.
- 출력 디렉터리와 중간 디렉터리를 각각 설정:
- 출력 디렉터리 →
Binaries.
- 중간 디렉터리 →
Intermediate.
- 리소스는 소스 코드 외부에서 관리되며, 결과물은 올바르게 저장됨.
2. 스프라이트
🌟 스프라이트란?
- 정의: 게임 내에서 사용되는 그래픽 요소를 의미.
- 2D 게임: 캐릭터, 아이템, 배경 등이 포함.
- 3D 게임: 텍스처, 파티클, UI 요소 등도 스프라이트로 사용.
- 형태: 비트맵 이미지 또는 벡터 이미지.
- 기능: 움직임이나 상호작용 시 변화를 보여줌.
a. 리소스 파일 다운로드 및 경로 지정
- 리소스 파일 다운로드: 사용하려는 그래픽 파일을 다운로드.
- 경로 지정:
- 헤더 파일에 경로를 추가하여 프로젝트 전역에서 사용할 수 있도록 설정.
- 리소스 매니저를 통해 리소스 경로 정보를 관리.
- 초기화 코드:
- 초기화 시 실행되는 코드를 작성하여, 리소스 경로 설정 및 추가적인 옵션을 포함.
💙 오브젝트와 리소스 차이점
- 공유의 개념:
- 리소스는 하나의 자원을 여러 오브젝트가 공유.
- 예: 동일한 캐릭터 이미지를 여러 번 로드하지 않고 한 번 로드한 이미지를 재사용.
b. 텍스처 만들기
- 정의: 이미지를 로드하여 게임에서 사용할 텍스처로 관리.
- 코드 작성:
ResourceBase를 상속받아 텍스처를 생성.
- 텍스처를 관리하는 코드를 추가.
- 용어 정리:
Vector → Vec2로 변경 (혼동 방지).
- 정수 버전
Vec2Int 추가.
- 투명도(transparent):
- 이유:
- BMP 파일은 RGB만 포함 (24비트).
- 최신 파일에서는 RGBA(알파 채널 포함) 사용.
- 결론: 현재는 RGB만 포함된 BMP 파일을 사용하므로, 알파 채널을 추가해야 함.
c. 이미지 가져오기
- 역할: 텍스처를 기반으로 이미지를 가져오는 용도.
- 리소스 매니저:
- 텍스처 관리 함수 (
get, load) 작성.
loadTexture 함수 구현 → 스마트 포인터 사용을 권장.
- 메모리 관리:
d. 이미지 테스트하기
- 테스트 방법:
DevScene에서 테스트 진행.
- 텍스처를 로드하여 렌더링.
- 문제 발생:
- 테스트 중 이미지 파일 (
sword) 누락 시 에러 발생.
- 결과: 문제를 해결하고 이미지 렌더링 성공.
e. 스프라이트 생성하기
- 역할: 텍스처에서 필요한 부분만 잘라 사용.
- 코드 작성:
- 스프라이트 생성 코드 작성.
- 텍스처와 유사한 방식으로 관리.
f. 스프라이트 리소스 지정하기
- 역할: 리소스 매니저를 통해 스프라이트 관리.
- 함수 작성:
CreateSprite 함수 구현.
- 로드된 텍스처를 스프라이트로 변환.
g. 스프라이트 렌더링하기
- 과정:
- 텍스처를 기반으로 스프라이트 생성.
- 렌더링하여 특정 부분만 출력.
- 결과: 필요한 이미지 부분만 정확히 잘라 렌더링 성공.
3. 벡터 및 정수형 정의 (Stat, Vector, VectorInt)
using 키워드 및 정수형 타입 정의
using int8 = __int8;
using int16 = __int16;
using int32 = __int32;
using int64 = __int64;
using uint8 = unsigned __int8;
using uint16 = unsigned __int16;
using uint32 = unsigned __int32;
using uint64 = unsigned __int64;
- 의도: 다양한 크기의 정수형 타입을 정의하여 코드 가독성을 높이고, 특정 크기의 정수형을 명시적으로 사용할 수 있도록 합니다.
Stat 구조체 정의
struct Stat
{
int32 hp = 0;
int32 maxHp = 0;
float speed = 0;
};
Stat 구조체: 캐릭터나 게임 오브젝트의 상태를 나타내는 기본 데이터 구조.
hp: 현재 체력.
maxHp: 최대 체력.
speed: 이동 속도.
Vector 클래스
struct Vector
{
Vector() {}
Vector(float x, float y) : x(x), y(y) {}
Vector(POINT pt) : x((float)pt.x), y((float)pt.y) {}
};
- 벡터 기본 생성자 및 초기화:
Vector(): 기본 생성자.
Vector(float x, float y): x, y 좌표를 초기화하는 생성자.
Vector(POINT pt): POINT 구조체를 통해 초기화.
벡터 연산자 오버로딩
Vector operator+(const Vector& other)
{
Vector ret;
ret.x = x + other.x;
ret.y = y + other.y;
return ret;
}
operator+: 두 벡터의 합을 반환.
x와 y 좌표를 각각 더하여 새로운 Vector 반환.
Vector operator-(const Vector& other)
{
Vector ret;
ret.x = x - other.x;
ret.y = y - other.y;
return ret;
}
operator-: 두 벡터의 차를 반환.
x와 y 좌표를 각각 빼서 새로운 Vector 반환.
벡터 길이 및 정규화
float Length()
{
return ::sqrt(LengthSquared());
}
void Normalize()
{
float length = Length();
if (length < 0.00000000001f)
return;
x /= length;
y /= length;
}
Length: 벡터의 길이를 계산.
Normalize: 벡터를 정규화하여 방향만 유지하고 크기를 1로 만듦.
벡터 내적 및 외적
float Dot(Vector other)
{
return x * other.x + y * other.y;
}
float Cross(Vector other)
{
return x * other.y - y * other.x;
}
Dot: 벡터의 내적을 계산.
Cross: 벡터의 외적을 계산.
4. 텍스처 클래스
클래스 선언 및 기본 메서드
class Texture : public ResourceBase
{
public:
Texture();
virtual ~Texture();
Texture* LoadBmp(HWND hwnd, const wstring& path);
HDC GetDC();
};
Texture 클래스:
LoadBmp: 비트맵 이미지를 로드.
GetDC: 텍스처에 대한 디바이스 컨텍스트 반환.
텍스처 로드 메서드 구현
Texture* Texture::LoadBmp(HWND hwnd, const wstring& path)
{
HDC hdc = ::GetDC(hwnd);
_hdc = ::CreateCompatibleDC(hdc);
_bitmap = (HBITMAP)::LoadImage(nullptr, path.c_str(), IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE | LR_CREATEDIBSECTION);
if (_bitmap == 0)
{
::MessageBox(hwnd, path.c_str(), L"Image Load Failed", NULL);
}
HBITMAP prev = (HBITMAP)::SelectObject(_hdc, _bitmap);
::DeleteObject(prev);
BITMAP bit = {};
::GetObject(_bitmap, sizeof(BITMAP), &bit);
_size.x = bit.bmWidth;
_size.y = bit.bmHeight;
return this;
}
- 텍스처 로드 과정:
::CreateCompatibleDC: 호환되는 디바이스 컨텍스트 생성.
::LoadImage: 비트맵 파일을 로드.
::GetObject: 비트맵 정보 획득.
_size: 텍스처 크기를 설정.
5. 스프라이트 클래스
스프라이트 선언 및 생성자
class Sprite : public ResourceBase
{
public:
Sprite(Texture* texture, int32 x, int32 y, int32 cx, int32 cy);
virtual ~Sprite();
HDC GetDC();
int32 GetTransparent();
Vec2Int GetPos() { return Vec2Int{ _x,_y }; }
Vec2Int GetSize() { return Vec2Int{ _cx, _cy }; }
};
Sprite 클래스:
- 생성자: 텍스처와 위치, 크기를 기반으로 초기화.
GetDC: 텍스처의 디바이스 컨텍스트 반환.
GetTransparent: 투명도 정보 반환.
스프라이트 렌더링 구현
void DevScene::Render(HDC hdc)
{
Sprite* sprite = GET_SINGLE(ResourceManager)->GetSprite(L"Start_On");
::BitBlt(hdc, 0, 0, GWinSizeX, GWinSizeY, sprite->GetDC(), sprite->GetPos().x, sprite->GetPos().y, SRCCOPY);
}
- 스프라이트 렌더링:
::BitBlt: 스프라이트를 화면에 그리는 함수.
- 매개변수:
hdc: 대상 디바이스 컨텍스트.
sprite->GetDC(): 소스 디바이스 컨텍스트.
sprite->GetPos(): 스프라이트의 위치.
6. 리소스 매니저
클래스 선언 및 주요 메서드
class ResourceManager
{
public:
Texture* LoadTexture(const wstring& key, const wstring& path, uint32 transparent = RGB(255, 0, 255));
Sprite* CreateSprite(const wstring& key, Texture* texture, int32 x, int32 y, int32 cx, int32 cy);
};
LoadTexture: 텍스처를 로드하고 관리.
CreateSprite: 텍스처 기반으로 스프라이트를 생성.
텍스처 로드 구현
Texture* ResourceManager::LoadTexture(const wstring& key, const wstring& path, uint32 transparent)
{
if (_textures.find(key) != _textures.end())
return _textures[key];
fs::path fullPath = _resourcePath / path;
Texture* texture = new Texture();
texture->LoadBmp(_hwnd, fullPath.c_str());
texture->SetTransparent(transparent);
_textures[key] = texture;
return texture;
}
- 텍스처 관리:
- 이미 존재하는 텍스처는 새로 로드하지 않음.
- 경로를 결합하여 로드 경로 설정.