게임 개발/[AssortRock] 콘솔 게임(CUI) 설계 및 분석

[PUSH PUSH] 게임 로직 구현 - AssortRock 17일차 오프라인 수업_220929

헛둘이 2022. 9. 30. 17:19
로직 변경 사항
  • Ball을 밀었을 때 그 위치가 Ball 혹은 벽이면 그 위치로 이동하지 못하게 하는 로직 추가
  • House에 Ball을 넣었을 때 House가 반짝거리게 하는 효과 넣기 (Pen의 도입)
  • 숙제 (플레이어가 제일 마지막에 그려지도록 하기)

 

 

 

이동불가 로직
// Player.cpp

if (map->GetPixel(ballPos) == L'▩'
	|| map->GetPixel(ballPos) == L'●')
{
	ballPos = prevBallPos;
	mPos = prevPos;
}
  • 만약 다음 볼이 움직인 위치를 가져와서 그 위치가 벽이거나 Ball일 경우
  • 움직이기 전 위치로 세팅해준다.

 

 

House에 볼을 넣었을 때 효과 처리
  • Ball이 들어갔는지 아닌지 여부에 따라 해당 칸에 bool 값으로 표시를 해주고
  • Map을 그릴 때 이 bool 값을 체크해서 반짝거리게 하는 로직
// Map.cpp

for (int iY = 0; iY < mSize.y; iY++)
{
	for (int iX = 0; iX < mSize.x; iX++)
	{
		//SET_COLOR(COLOR::DARK_SKY_BLUE);
		GOTO_XY( (short)(x + iX * 2), (short)(y + iY));

		if (mDatas[iY][iX].ch == L'★')
		{
			Pen pen(COLOR::GREEN, mDatas[iY][iX].ch);
		}

		if (mDatas[iY][iX].isLight == false)
		{
			Pen pen(mDatas[iY][iX].ch);
		}
		else
		{
			void* p = new int();
			srand((int)p);

			// 0 ~ 15
			COLOR col = (COLOR)((rand() % 15) + 1);
			Pen pen(col, mDatas[iY][iX].ch);

			delete p;
		}
			
			
	}
}
  • 위에서 언급한 bool 값은 Map을 구성하는 요소인 wchar_t를 bool과 같이 Pixel 구조체로 묶어서 세팅해줬다.
  • Pen의 생성자의 첫번째 인자로 전달 받은 색상을 펜으로 세팅하고,  두 번째 인자로 그려준다.
  • mDatas[iY][iX].ch는 wchar_t인데 이걸 Pen의 생성자에서 그려주게 된다.
  • isLight라는 bool값을 통해 House에 Ball이 들어왔는지 확인하게 되며,
  • isLight가 true라면 랜덤으로 색상을 넣어서 매 프레임마다 다른 색상을 가지도록 했다.
  • srand 시드값으로 동적 할당받은 주소를 사용하는 것도 눈여겨볼 필요가 있다.
  • 그려준 후에는 다시 원래 색상으로 돌려놔야 하는데 이것을 생성자, 소멸자에서 구현하였다.

 

// Common.h

class Pen
{
public:
	Pen(wchar_t ch)
	{
		mColor = COLOR::DARK_SKY_BLUE;
		SET_COLOR(mColor);
		std::wcout << ch;
	}

	Pen(WORD color, wchar_t ch)
	{
		mColor = color;
		SET_COLOR(mColor);
		std::wcout << ch;
	}
	~Pen()
	{
		mColor = COLOR::DARK_SKY_BLUE;
		SET_COLOR(mColor);
	}

private:
	WORD mColor;
};
  • 생성자에서 전달 받은 색상으로 그려주고 소멸자에서 다시 원래 색상으로 돌려주는 방식
  • 생성자 소멸자를 이런 식으로도 사용할 수 있구나 ㄷㄷ..

 

 

 

 

숙제
  • 숙제로 Player가 제일 나중에 그려지도록 하여 House와 Player가 겹칠 때 Player가 보이도록 하는 것
  • 로직 자체는 간단했는데, 이걸 해결하고 나니 Ball을 House에 넣으면 모양이 House에서 반짝거리는 로직이다보니
  • 벽이거나, Ball일 때 이동하지 못하게 하는 로직에 걸리지 않아 House에 넣은 공들이 겹쳐지는 버그가 있었다.
  • 이전에 Ball의 객체들을 list에 담아서 static 변수로 담은 후 static 함수로 공유하는 것에 감명을 받아서
  • House 내부에서 static 멤버변수 list를 만들어서 Ball이 들어온 House를 push하고 그걸 static 함수로 공유하는 방법으로 구현해봤다.
  • 다 만들고 나서 떠오른 생각인데 isLight를 활용했으면 좋았을걸 하는 아쉬움이 든다. 
  • 다음에 새로 처음부터 만들어볼 때 적용할 생각임
// House.h

class House : public GameObject
{
public:
	static std::list<House*>& GetFullHouses() { return mFullHouses; }
	static void ClearFullHouse() { mFullHouses.clear(); }
	House();
	House(Pos pos);
	~House();

	virtual void Update(Map* map) override;
	virtual void Render() override;

private:
	static std::list<House*> mFullHouses;
};
  • 위에서 언급했듯 list에서 mFullHouses라는 멤버를 통해 공이 들어온 list를 관리
  • 그걸 공유할 때는 참조로 공유해서 밖에서 mFullHouses에 접근할 수 있도록 함
  • 찬찬히 생각해보니 이럴게 아니라 AddFullHouse 같은 함수로 처리할걸 그랬다. 암튼
  • ClearFullHouse()는 외부에서 mFullHouses를 비워주는 것 (매 프레임 쌓일 것을 우려)

 

// Map.cpp

void Map::IntersectHouseToBall()
{
	std::vector<GameObject*> houses;
	for (size_t i = 0; i < 128; i++)
	{
		GameObject* temp = nullptr;
		if (mGameObjects[i] != nullptr)
		{
			temp = mGameObjects[i];

			if (temp->GetWChar_t() == L'◆')
			{
				houses.push_back(temp);
			}
		}
	}

	std::list<Ball*> balls = Ball::GetBalls();
	std::list<House*>& fullHouses = House::GetFullHouses();

	for ( auto ball : balls)
	{
		for (GameObject* house : houses)
		{
			Pos ballPos = (*ball).GetPos();
			Pos housePos = house->GetPos();

			if (ballPos == housePos)
			{
				mDatas[housePos.y][housePos.x].isLight = true;
				fullHouses.push_back(dynamic_cast<House*>(house));
			}
		}
	}

	for (GameObject* house : houses)
	{
		Pos housePos = house->GetPos();
		mDatas[housePos.y][housePos.x].ch = L'◆';
	}
}
  • 실질적으로 mFullHouses에 push하는 부분
  • houses를 순회하다가  housePos와 ballPos가 일치한다면 fullHouses에 push 해주는 모습이다.
  • house가 GameObject* 형태로 되어 있어서 dynamic_cast를 사용해줬다.

 

 

 

 

 

// Player.cpp

void Player::OnCollisionBalls(Map* map, DIRECTION dir, Pos& prevPos)
{
	// 공 이랑 플레이어 충돌체크
	std::list<Ball*>& balls = Ball::GetBalls();
	std::list<House*>& fullHouses = House::GetFullHouses();

	for (std::list<Ball*>::iterator iter = balls.begin()
		; iter != balls.end()
		; iter++)
	{
		Pos ballPos = (*iter)->GetPos();
		Pos prevBallPos = ballPos;
		if (mPos == ballPos)
		{
			Move(dir, ballPos);

			// 공 벽충돌 체크
			if (map->GetPixel(ballPos) == L'▩'
				|| map->GetPixel(ballPos) == L'●')
			{
				ballPos = prevBallPos;

				// 플레이어도 움직이지 못하게 돌려주어야 한다.
				mPos = prevPos;
			}

			else if (map->GetPixel(ballPos) == L'◆')
			{
				for (House* fullHouse : fullHouses)
				{
					if (fullHouse->GetPos() == ballPos)
					{
						ballPos = prevBallPos;
						mPos = prevPos;
					}
				}
			}

			(*iter)->SetPos(ballPos);
			map->SetGameObjectInMap((*iter)->GetWChar_t(), ballPos, prevBallPos);
		}
	}
}
  • 기존 충돌체크 부분에서 Ball이 이동한 좌표가 House라면 fullHouses를 순회하며 House에 Ball이 들어가 있는지 확인
  • 들어가있다면 벽이나 Ball을 만난 것과 같이 이동을 취소한다.