게임 개발/[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을 만난 것과 같이 이동을 취소한다.