[WinAPI] 과제 회고

22일부터 26일까지, 아주 간단한 슈팅게임을 만들어보는 과제가 주어졌다. 무슨 문제가 생겼고, 어떻게 해결했는지 기록해야지.
깃허브 레포지토리 링크

hp포션과 미니 몬스터의 구현


기존에 주어진 코드에서 hp포션, 미니 몬스터를 구현하였다.

기존에 있는 대형 몬스터는 죽으면 hp포션을 드랍하게 했으며,
미니 몬스터는 플레이어를 쫓아오도록 설정하였다.


// 미니 몬스터 클래스의 일부.

if (IsValid(m_pTarget))
	{
		Vec2 vTargetPos = m_pTarget->GetPos();
		m_pTarget->SetName(L"Player");
		Vec2 vPos = GetPos();

		Vec2 vDir = vTargetPos - vPos;
		vDir.Normalize();

		vPos.x += vDir.x * m_fSpeed * DT;
		vPos.y += vDir.y * m_fSpeed * DT;

		

		SetPos(vPos);
	}

	if (m_HP == 0)
		SetDead();
// 충돌 처리의 일부
void CSmallMonster::BeginOverlap(CCollider* _pOther)
{
	if (_pOther->GetOwner()->returnLayer() == LAYER::PLAYER_PROJECTILE)
		m_HP -= 1;
}

그런데 버그가 생겼다. 미니 몬스터가 플레이어에 닿으면 더이상 플레이어의 총알에 죽지 않는 무적상태가 되는 것이다. 충돌처리에서 문제가 생긴 것일까?

우선, 버그를 고친 경우는 HP가 0일때 사라지는 것이 아니라, 0 이하 일때 사라지도록 고치니 버그가 해결되었다.

	if (m_HP <= 0)
		SetDead();

다음 문제는 일정 시간마다 아이템이 랜덤한 위치에 배치되도록 하는 것이다.

몬스터 객체 안에다가 m_CoolTime 변수를 선언하고 DT 매크로를 활용하였다. 여기서 DT는 아래와 같고, 더 자세한 사항은 깃허브에 있는 파일을 참조하자.

#define DT CTimeMgr::GetInst()->GetDeltaTime();
if (m_CoolTime > 5.f)
	{
		CLevel* pCurLevel = CLevelMgr::GetInst()->GetCurLevel();


		CMMissile* pMMissile = new CMMissile;
		pMMissile->SetPos(GetPos());
		pMMissile->SetScale(Vec2(25.f, 25.f));

		pCurLevel->AddObject(pMMissile, LAYER::MONSTER_PROJECTILE);

		m_CoolTime = 0;
	}

	m_CoolTime += DT;

이미지 출력


BitBlt, TransparentBlt 함수를 사용해서 이미지를 출력해보기로 하였다.

그런데 비트맵 파일(bmp)을 넣어주었는데도 출력되지 않아서 무슨 문제인가 생각을 해보았다.

해결 방법은 그림판을 통해서 256색 비트맵 파일로 저장해주니 잘 출력되었다.

아쉽게도 위의 두 함수는 PNG, JPG형식의 파일을 출력하지는 못한다.

GdiPlus를 공부하고 이용해볼 수 밖에.

HP포션 드랍 버그

CMonster.cpp 파일 중에서


// tick 논리 연산

void CMonster::tick()
{
	// 논리 연산 하는곳

	// 의도 : 큰 몬스터가 죽으면 죽은 자리에 HP 포션 드랍
	// 버그 발생. 포션이 같은자리에 2개 드랍됨. (원래 tick()에 있었음.)

	if (m_HP <= 0)
	{
		CLevel* pCurLevel = CLevelMgr::GetInst()->GetCurLevel();

		CHP_Potion* pHP_Potion = new CHP_Potion;
		pHP_Potion->SetScale(Vec2(40.f, 40.f));

		Instantiate(pHP_Potion, GetPos(), LAYER::ITEM);
		SetDead();
	}

	// 5초마다 플레이어가 있는 쪽으로 미사일을 쏨.
	if (m_CoolTime > 5.f)
	{
		CLevel* pCurLevel = CLevelMgr::GetInst()->GetCurLevel();

		CMMissile* pMMissile = new CMMissile;
		pMMissile->SetPos(GetPos());
		pMMissile->SetScale(Vec2(25.f, 25.f));
		pMMissile->SetTarget((CPlayer*)m_pTarget);
		pMMissile->SetDir((m_pTarget->GetPos() - GetPos()).Normalize()); // 플레이어가 있는 방향으로 직선 미사일을 쏘게 하기 위한 장치
		pCurLevel->AddObject(pMMissile, LAYER::MONSTER_PROJECTILE);

		m_CoolTime = 0;
	}

	// 3초마다 랜덤하게 다른 방향으로 움직이면 더 재밌겠지
	if (m_MovingTime > 3.f)
	{
		SetDir();
		m_MovingTime = 0;
	}

	// 쿨타임을 뭔가 더 좋은 방법으로 묶으면 좋을텐데.. 변수를 많이 만들지 않고.
	m_CoolTime += DT;
	m_MovingTime += DT;


	// 움직이는 곳.
	Vec2 vPos = GetPos();

	float fRadian = ((float)GetDir() * PI) / 180.f;

	float	delta_x = m_fSpeed * cosf(fRadian) * DT;
	float	delta_y = m_fSpeed * sinf(fRadian) * DT;

	vPos.x += delta_x;
	vPos.y += delta_x;


	SetPos(vPos);


	

	CObj::tick();
}

// 충돌처리
void CMonster::BeginOverlap(CCollider* _pOther)
{
	if (_pOther->GetOwner()->returnLayer() == LAYER::PLAYER_PROJECTILE)
	{
		--m_HP;
	}
		

}

위 코드의 의도는 몬스터가 다른 객체와 충돌했는데 그 객체가 플레이어의 탄환이었을 경우 HP가 1 깎이며, HP가 0 이하로 내려갔을 때 죽으면서 HP포션을 드랍하려는 의도였다.

그런데 문제는 HP포션이 죽을 때 한 번에 두번 드랍된다는 문제가 있었다.

이 문제는 아래와 같이 해결하였다.

void CMonster::tick()
{
	// 논리 연산 하는곳

	// 5초마다 플레이어가 있는 쪽으로 미사일을 쏨.
	if (m_CoolTime > 5.f)
	{
		CLevel* pCurLevel = CLevelMgr::GetInst()->GetCurLevel();

		CMMissile* pMMissile = new CMMissile;
		pMMissile->SetPos(GetPos());
		pMMissile->SetScale(Vec2(25.f, 25.f));
		pMMissile->SetTarget((CPlayer*)m_pTarget);
		pMMissile->SetDir((m_pTarget->GetPos() - GetPos()).Normalize()); // 플레이어가 있는 방향으로 직선 미사일을 쏘게 하기 위한 장치
		pCurLevel->AddObject(pMMissile, LAYER::MONSTER_PROJECTILE);

		m_CoolTime = 0;
	}

	// 3초마다 랜덤하게 다른 방향으로 움직이면 더 재밌겠지
	if (m_MovingTime > 3.f)
	{
		SetDir();
		m_MovingTime = 0;
	}

	// 쿨타임을 뭔가 더 좋은 방법으로 묶으면 좋을텐데.. 변수를 많이 만들지 않고.
	m_CoolTime += DT;
	m_MovingTime += DT;


	// 움직이는 곳.
	Vec2 vPos = GetPos();

	float fRadian = ((float)GetDir() * PI) / 180.f;

	float	delta_x = m_fSpeed * cosf(fRadian) * DT;
	float	delta_y = m_fSpeed * sinf(fRadian) * DT;

	vPos.x += delta_x;
	vPos.y += delta_x;


	SetPos(vPos);


	

	CObj::tick();
}

void CMonster::BeginOverlap(CCollider* _pOther)
{
	if (_pOther->GetOwner()->returnLayer() == LAYER::PLAYER_PROJECTILE)
	{
		--m_HP;

		// 의도 : 큰 몬스터가 죽으면 죽은 자리에 HP 포션 드랍
		// 버그 발생. 포션이 같은자리에 2개 드랍됨. (원래 tick()에 있었음.)
		// 버그 해결: tick()함수에서 BeginOverlap (지금 이자리)으로 옮기니 해결되었음.

		if (m_HP <= 0)
		{
			CLevel* pCurLevel = CLevelMgr::GetInst()->GetCurLevel();

			CHP_Potion* pHP_Potion = new CHP_Potion;
			pHP_Potion->SetScale(Vec2(40.f, 40.f));

			Instantiate(pHP_Potion, GetPos(), LAYER::ITEM);
			SetDead();
		}
	}
		
}

죽을 때 조건을 tick에서 BeginOverlap으로 옮겨주니 해결되었다.
아무래도 연산이 빠르게 진행되다보니 객체가 두번 생성되는 중복연산이 있었나보다.

BeginOverlap에서는 충돌한 그 순간에만 연산을 1번 진행하기 때문에 저 자리에 있다면 1번만 hp포션을 드랍할 수 밖에 없다.


© 2022.07. by Wookey_Kim

Powered by Hydejack v7.5.2