WinAPI Programming
WinAPI_2020_0602_화_충돌처리_똥피하기 게임
HI2
2020. 6. 3. 02:56
2020_0601_Framework.zip
0.03MB
[프로그램 설계]
1. vector class를 이용하여 n개의 Enemy* 오브젝트 풀 생성
2. 생성된 오브젝트 풀은 화면 상단에 한 줄로 나열하여 현재 상태 체크
3. 화면 중앙으로부터 540 * 1080 pixel 크기의 게임영역 지정
4. Player는 게임영역내에서 좌, 우로만 이동가능하도록 제한
5. Enemy는 게임영역 상단에서부터 좌,우 범위 내에서 랜덤위치에 출현
6. 게임영역 하단에 도착 시 전체영역 상단 최초 지점으로 위치 초기화
7. delay timer를 이용하여 각각의 Enemy는 랜덤한 타이밍으로 다시 재활성되어 5~6 반복
8. 한번 바닥으로 떨어진 Enemy는 색상을 변경하여 재활용
9. Enemy 색상에 따라 Player와 충돌시 구분된 효과 구현
[구현 중점]
1. 오브젝트 풀
- Debug를 위한 오브젝트 풀 가시화
2. Restart 기능
- 최적화된 객체 초기화
Circle & Rect 충돌처리 프로젝트에서 수정하여 작성함
추가된 코드
CollisionScene
더보기
// 멤버 변수
private:
RECT rect_PlayArea;
bool isPlaying;
HBRUSH brushes[7];
// 멤버 함수
public:
void InitBrush();
bool OnGround(Enemy* enemy);
void RestartGame();
///////////////////////////////////////////////////////////////////////////////////////////////
bool CollisionScene::Init()
{
// 게임 플레이 영역 설정
LONG center_x = bufferRT.right * 0.5;
LONG center_y = bufferRT.bottom * 0.5;
rect_PlayArea = { center_x - 270, center_y - 420, center_x + 270, center_y + 420 }; // 540 * 960
// 플레이어 생성
m_pPlayer = new Player;
m_pPlayer->SetPos(center_x, rect_PlayArea.bottom - m_pPlayer->Size().y);
m_pPlayer->rect_PlayArea = rect_PlayArea;
// Enemy 생성
enemyCount = 30;
for (int i = 0; i < enemyCount; i++)
{
Enemy* enemy = new Enemy;
enemy->SetRandomSize(); // Pos 설정전 Size 먼저 재설정(순서 준수)
POINT pos = { 50 * (i + 1), bufferRT.top + 50 };
enemy->startPos = pos;
enemy->SetPos(pos);
enemy->rect_PlayArea = rect_PlayArea;
enemys.emplace_back(enemy);
}
// Brush 초기화
InitBrush();
return true;
}
void CollisionScene::Update()
{
if (GetAsyncKeyState(VK_F3))
RestartGame();
if (isPlaying)
{
m_pPlayer->Update(); // 플레이어를 가장 먼저 Update
if (m_pPlayer->GetHP() == 0)
isPlaying = false; // 플레이어 HP가 0이되면 게임 종료
for (int i = 0; i < enemyCount; i++)
{
enemys[i]->Update();
if (enemys[i]->isActive)
{ // Active 상태의 Object만 충돌검사
if (Collision(enemys[i]))
{ // 플레이어와 충돌
int color = enemys[i]->Color();
m_pPlayer->TouchEnemy(color); // Enemy객체의 색상에 따라 충돌시 효과 구분
int colorNext = (color + 1) % 7;
enemys[i]->Color(colorNext);
enemys[i]->SetBrush();
enemys[i]->Reset();
}
else if (OnGround(enemys[i]))
{ // 바닥에 도달
int color = enemys[i]->Color();
int colorNext = (color + 1) % 7;
enemys[i]->Color(colorNext);
enemys[i]->SetBrush();
enemys[i]->Reset();
}
} // End of if (enemy -> isActive)
} // End of For Loop (enemys)
} // End of if (isPlaying)
}
void CollisionScene::Render()
{
if (isPlaying) // 게임 진행중일 때만 오브젝트 출력
{
for (int i = 0; i < enemyCount; i++)
//if(enemys[i]->isActive) // 디버그를 위해 오브젝트 풀을 화면 상단에 출력
enemys[i]->Render();
m_pPlayer->Render(); // 최상단에 표시하기 위해 플레이어를 가장 나중에 출력
}
RenderUI(); // UI는 항상 출력
}
bool CollisionScene::OnGround(Enemy* enemy)
{
if (enemy->GetPos().y >= rect_PlayArea.bottom + 30) // 바닥에 떨어지면 true 반환
return true;
return false;
}
void CollisionScene::RestartGame()
{ // 오브젝트 생성 과정 없이 데이터만 초기화
isPlaying = false; // 게임 진행중에 호출시 껐다가 켠다
m_pPlayer->HP = 100;
m_pPlayer->Speed(10.0);
LONG center_x = bufferRT.right * 0.5;
m_pPlayer->SetPos(center_x, rect_PlayArea.bottom - m_pPlayer->Size().y);
for (int i = 0; i < enemyCount; i++)
{
enemys[i]->isActive = false;
enemys[i]->InitEnemy(); // 생성자에서만 호출되던 Init함수를 별도로 호출해준다
enemys[i]->SetRandomSize(); // Pos 설정전 Size 먼저 재설정(순서 준수)
POINT pos = { 50 * (i + 1), bufferRT.top + 50 };
enemys[i]->startPos = pos;
enemys[i]->SetPos(pos);
}
isPlaying = true;
}
void CollisionScene::RenderUI()
{
// 전체영역 우측 UI
wstring str = L"F1 : 원";
TextOut(hdc, bufferRT.right - 300, bufferRT.top + 200, str.c_str(), str.length());
str = L"F2 : 사각형";
TextOut(hdc, bufferRT.right - 300, bufferRT.top + 240, str.c_str(), str.length());
str = L"F3 : 다시하기";
TextOut(hdc, bufferRT.right - 300, bufferRT.top + 280, str.c_str(), str.length());
str = L"이동 : → ← ↑ ↓";
TextOut(hdc, bufferRT.right - 300, bufferRT.top + 320, str.c_str(), str.length());
str = L"HP : " + to_wstring(m_pPlayer->GetHP());
TextOut(hdc, bufferRT.right - 300, bufferRT.top + 360, str.c_str(), str.length());
str = L"SPEED : " + to_wstring(m_pPlayer->Speed());
TextOut(hdc, bufferRT.right - 300, bufferRT.top + 400, str.c_str(), str.length());
// 게임영역 우측 UI
int left = rect_PlayArea.right + 50;
int top = rect_PlayArea.top + 50;
int right = left + 50;
int bottom = top + 50;
for (int i = 0; i < 7; i++)
{
SelectObject(hdc, brushes[i]);
Ellipse(hdc, left, top, right, bottom);
switch (i)
{
case 0: str = L"게임오버"; break;
case 1: str = L"체력+1"; break;
case 2: str = L"체력-1"; break;
case 3: str = L"스피드+1"; break;
case 4: str = L"스피드-1"; break;
case 5: str = L"미구현"; break;
case 6: str = L"미구현"; break;
}
TextOut(hdc, right + 25, top + 10, str.c_str(), str.length());
top += 50;
bottom += 50;
}
if (!isPlaying)
str = L"Game Over!";
else
str = L"Game is Running";
TextOut(hdc, bufferRT.right - 300, bufferRT.top + 440, str.c_str(), str.length());
// 게임 영역 라인
MoveToEx(hdc, rect_PlayArea.left, rect_PlayArea.top, nullptr);
LineTo(hdc, rect_PlayArea.right, rect_PlayArea.top);
LineTo(hdc, rect_PlayArea.right, rect_PlayArea.bottom);
LineTo(hdc, rect_PlayArea.left, rect_PlayArea.bottom);
LineTo(hdc, rect_PlayArea.left, rect_PlayArea.top);
}
void CollisionScene::InitBrush()
{
brushes[0] = CreateSolidBrush(BLACK);
brushes[1] = CreateSolidBrush(RED);
brushes[2] = CreateSolidBrush(GREEN);
brushes[3] = CreateSolidBrush(BLUE);
brushes[4] = CreateSolidBrush(CYAN);
brushes[5] = CreateSolidBrush(MAGENTA);
brushes[6] = CreateSolidBrush(YELLOW);
}
Player
더보기
public:
RECT rect_PlayArea;
int HP;
public:
void TouchEnemy(int color);
int GetHP() const { return HP; }
void SetHP(int hp) { HP = hp; }
void AddHP(int add) { HP += add; }
///////////////////////////////////////////////////////////////////////////////////////////////
void Player::TouchEnemy(int color)
{
switch (color)
{
case 0: HP = 0; break; // BLACK
case 1: ++HP; break; // RED
case 2: --HP; break; // GREEN
case 3: ++speed; break; // BLUE
case 4: --speed; break; // CYAN
//case 5: ; break; // MAGENTA, 미구현
//case 6: ; break; // YELLOW, 미구현
default: break;
}
}
Enemy
더보기
public:
bool isActive; // Object Pool 활용을 위한 활성 스위치
POINT startPos; // Obejct Pool 표시를 위한 디버그용 위치값
int delay; // 랜덤 낙하를 위한 변수
RECT rect_PlayArea; // 게임 플레이 영역
public:
void FallDown();
void CountToActive();
void Reset();
void SetDelay();
///////////////////////////////////////////////////////////////////////////////////////////////
void Enemy::Update()
{
if (isActive)
FallDown(); // 활성 상태일 때는 낙하 시킨다
else
CountToActive(); // 비활성 상태일 때는 delay Counter를 갱신한다
}
void Enemy::InitEnemy()
{
// 기존 코드는 생략, 추가한 코드만 표시
SetDelay(); // Random한 낙하를 위해 각 Enemy 마다 delay 랜덤설정
delay += 240; // 최초 실행시 240프레임 추가 대기(사용자가 게임플레이를 준비할 시간)
}
void Enemy::SetRandomPos()
{
LONG x, y;
x = (rand() % (rect_PlayArea.right - rect_PlayArea.left)) + rect_PlayArea.left;
y = rect_PlayArea.top;
m_tPos.x = x;
m_tPos.y = y;
}
void Enemy::FallDown()
{ // 낙하
m_tPos.y += speed;
}
void Enemy::CountToActive()
{ // delay Counter
--delay;
if (delay == 0)
{
SetRandomPos();
Speed(rand() % 12 + 2.0);
isActive = true;
}
}
void Enemy::Reset()
{ // 충돌 시 실행되는 코드
isActive = false;
m_tPos = startPos;
SetDelay();
}
void Enemy::SetDelay()
{
delay = rand() % 120 + 60; // Update 프레임 단위
}