인프런 커뮤니티 질문&답변

티르샷님의 프로필 이미지

작성한 질문수

[게임 프로그래머 입문 올인원] C++ & 자료구조/알고리즘 & STL & 게임 수학 & Windows API & 게임 서버

온라인 환경에서 Arrow, 제거와 발사 시 동기화 문제

23.12.26 13:03 작성

·

266

0

안녕하세요 Rookis님.

강의를 모두 완강하고 포트폴리오를 개선하고 있는 중입니다.

강의 중에 화살을 온라인에 추가할 것이라면 Creature와 GameObject 내용을 수정해야 한다고 하셔서 코드 이사는 완료한 상태입니다.

지금 Arrow는 MyPlayer에서 입력을 받고 State와 WeaponType을 체크하면 Player에서 Scene에 Arrow를 스폰해주는 방식으로 알고 있습니다.

이걸 서버에서는 몬스터나 플레이어를 만드는 GameObject::Create 함수를 파줘서 Arrow를 make_shared로 만들어서 발사 처리를 하게 만들었는데요

서버에서 몬스터가 제거되면 클라이언트에서도 정상적으로 몬스터가 제거되는걸 확인은 했습니다.

그러나 문제가 몇 가지 있습니다.

  1. Arrow 스마트 포인터가 해제되지 않는 현상

  2. Arrow 동기화가 종종 씹히는 현상

입니다.

 

먼저 1번입니다.

  • make_shared로 생성된 Arrow가 해제되지 않음

void Player::UpdateSkill()
{
	if (room == nullptr)
		return;

	if (info.weapontype() == Protocol::WEAPON_TYPE_SWORD)
	{
		// 내 앞에 있는 좌표
		CreatureRef creature = room->GetCreatureAt(GetFrontCellPos());
		
		if (creature)
		{
			if (creature->GetType() == Protocol::OBJECT_TYPE_PLAYER)
			{
				SetState(IDLE);
				return;
			}

			// 몬스터가 플레이어에게 피격
			creature->OnDamaged(shared_from_this());
		}
	}
	else if (info.weapontype() == Protocol::WEAPON_TYPE_BOW)
	{
		ArrowRef arrow = CreateArrow();
		
		arrow->SetDir(GetLookAtDir(GetFrontCellPos()));
		arrow->SetOwner(shared_from_this());

		arrow->room = room;
		arrow->info.set_posx(info.posx());
		arrow->info.set_posy(info.posy());
		arrow->SetState(IDLE, true);

		room->AddObject(arrow);
	}
	SetState(IDLE);
}

서버쪽의 UpdateSkill 함수인데 여기에서 arrow를 CreateArrow로 만들어서 room에 AddObject로 _arrows map을 만들어 관리를 하고 있습니다. (사실 룸에서 발사체를 관리하는 것이 옳은지도 의문이긴 합니다)

void GameRoom::Update()
{
	for (auto& item : _players)
	{
		item.second->Update();
	}

	for (auto& item : _monsters)
	{
		item.second->Update();
	}

	for (auto& item : _arrows)
	{
		item.second->Tick();
	}
}

그리고 Update 함수, AddObject, RemoveObject도 _arrows map을 처리하게 만들었습니다.

이렇게 처리하다 보니 화살을 제거할 때 몬스터가 피격받는 순간에 해당 화살을 RemoveObject를 실행하니 삭제 이후에 for문을 돌 때 오류가 발생하며 크래시가 발생했습니다.

Arrow가 _arrows 맵에서 관리되어서 스마트 포인터가 해제가 안되는 것 같은데, 화살을 어떤 때에서 제거해야 될지 모르겠습니다.

지금은 임시적으로 vector에 참조로 받아와서 Update 하단에서 erase하는 방법을 사용하고 있습니다.

 

  • 화살 동기화 문제

     

클라이언트에서는 Projectile 클래스에서 _owner에 대한 정보를 들고 있고 Arrow를 Scene에서 소환할 때 GetOwner 함수로 화살의 주인에 대한 포인터를 들고 있도록 했습니다.

기존에 발사할 때도 S_Move 패킷을 그대로 이용해서 Arrow의 _owner 포인터를 이용해서 위치를 초기화해주고 있습니다.

그런데 두 클라이언트를 접속시켜서 2를 눌러 WeaponType을 Bow로 바꿔서 공격 키로 테스트해보면 종종 반대쪽 플레이어의 화살이 안보이는 경우가 있습니다.

여기에 몇 개의 질문이 있는데요

1) 화살을 생성하고 클라이언트에서 패킷을 전송하는 것이 맞는지

2) 맞다면 어떤 정보를 패킷에 담아야 하는지

3) 서버에서는 어떤 처리를 해주어야 하는지

잘 모르겠습니다.

일단은 Make, Handle 함수를 파서 ObjectInfo를 넣어놓긴 했습니다.

하루 종일 코드를 조작해봐도 해결을 못하고 있습니다.. 도와주세요..

 

답변 2

1

Rookiss님의 프로필 이미지
Rookiss
지식공유자

2023. 12. 26. 13:09

여기부턴 응용이라 스스로 해결을 하셔야 합니다.

Update에서 제거하면 크래시가 나는 문제는
- &가 아닌 복사본으로 업데이트를 돌거나
- 삭제 예약을 해서 모든 Update가 끝나면 일괄적으로 처리해주기
등으로 처리해주시면 됩니다.

스마트 포인터 문제는 shared, weak를 적절히 사용하시면 되겠습니다.

투사체도 당연히 서버측 관리대상입니다.
사실 서버에서는 스폰 패킷만 보내고
이후에는 클라와 서버가 각각 좌표를 따로 계산해서 동기화하는 것이 일반적입니다.
투사체는 그 정도 중요도는 아니기 때문이죠.

하루 종일 코드를 조작해봐도 해결을 못할 수 있지만,
결국 회사에 들어가면 그래도 어떻게든 해결을 해야 합니다.
더더더 고민을 통해 해결하는 습관을 들이셔야 하는데
물론 이게 말이 쉽지 정말 고통스러운건 당연합니다.
저도 일주일씩 헤딩하고 이런 문제가 초반엔 아주 많았지만
그게 다 경험치로 돌아오니 더 고민해보시기 바랍니다

티르샷님의 프로필 이미지
티르샷
질문자

2023. 12. 26. 13:17

답변 감사합니다 ^^

티르샷님의 프로필 이미지
티르샷
질문자

2023. 12. 27. 01:50

하루 종일 더 고민해서 위의 문제들을 모두 해결했습니다!!

성장한 기분이 드네요. 정말 감사합니다 루키스님

0

인프런 AI 인턴님의 프로필 이미지

2023. 12. 26. 13:06

안녕하세요, 인프런 AI 인턴입니다.

강의 내용과 관련하여 화살을 처리하는 부분에 대해 질문을 주셨네요. 서버와 클라이언트 간의 화살 동기화 문제와 화살의 생성 및 제거에 대해 어떻게 처리해야 하는지에 대한 질문입니다.

유사한 상황을 해결한 다음의 답변이 도움이 될 것 같습니다. 강사님께서 화살의 서버에서의 이동 처리 방법과 클라이언트와 서버 사이의 화살 동기화에 대한 해결 방법을 제시하고 계십니다.

위 링크에서 강사님께서 제시한 방법들을 확인하시고, 여러분의 프로젝트에 적용해 보시기 바랍니다. 화살의 생성, 이동, 소멸 로직을 서버와 클라이언트에서 어떻게 처리해야 하는지에 대한 가이드를 얻으실 수 있을 겁니다.

도움이 되길 바라며, 추가적인 질문이 있으시면 언제든 게시판을 통해 질문해 주시기 바랍니다.