하아찡

[C++] 몬스터 움직임 - 2 본문

C++/이것저것서버테스트

[C++] 몬스터 움직임 - 2

하아찡 2025. 3. 17. 19:59

언리얼 버전 5.5.3

서버언어 C++로 구성했습니다.


실행화면

몬스터 움직임 동기화

 

몬스터 어그로 및 어그로 풀림

 

 

 

몬스터 체력 표시


문제점.

도착좌표를 저장하고 몬스터 그리드에 넣다보닌깐 클라이언트 상에서는 몬스터가 해당 좌표에없지만 서버에는 해당 좌표에 있다고 처리돼서 몬스터가 맞는 처리가 됨.

 

해결 : 서버에서 움직임 처리를 해줌. 0.1초마다 몬스터 위치를 갱신해줌.

          몬스터 객체에다가 도착지 위치를 따로 저장시켜 위치값을 계속 갱신시킴.

 

SendBufferRef Monster::ServerMove(MonsterRef monster, float deltaTime)
{
	uint64 spawnerID = monster->SpawnerID;
	
	Vector3 NowPos(monster->objectInfo->posinfo().x(), monster->objectInfo->posinfo().y(), monster->objectInfo->posinfo().z());
	Vector3 DestPos(monster->movePosInfo->x(), monster->movePosInfo->y(), monster->movePosInfo->z());
	Vector3 Direction = (DestPos - NowPos).GetSafeNormal();
	Vector3 NewPosition = NowPos + (Direction * monster->objectInfo->speed() * deltaTime);
	
	Protocol::S_MOVE pkt;
	
	Protocol::PosInfo* pos = pkt.mutable_posinfo();
	pos->CopyFrom(monster->objectInfo->posinfo());
	pos->set_x(NewPosition.X);
	pos->set_y(NewPosition.Y);
	pos->set_movestate(Protocol::MOVE_STATE_RUN);

	//서버도 똑같이 이동된 좌표값을 가지게함.
	monster->posInfo->set_x(NewPosition.X);
	monster->posInfo->set_y(NewPosition.Y);
	monster->posInfo->set_movestate(Protocol::MOVE_STATE_RUN);
	
	// 해당위치 도착
	float Dist = monster->Distance2D(DestPos.X, DestPos.Y);
	if (Dist <= 30.0f)
	{
		pos->set_x(DestPos.X);
		pos->set_y(DestPos.Y);
		monster->posInfo->set_x(NewPosition.X);
		monster->posInfo->set_y(NewPosition.Y);
		
		
		if (monster->GetState() == MonsterState::MOVE) {
			monster->posInfo->set_movestate(Protocol::MOVE_STATE_IDLE);
			pos->set_movestate(Protocol::MOVE_STATE_IDLE);
			monster->SetState(MonsterState::IDLE);
			monster->bmovePos = false;; // 이동 중지
		}
		
	}

	SendBufferRef sendBuffer = ClientPacketHandler::MakeSendBuffer(pkt);
	
	return sendBuffer;
}

 

 

 

Room.cpp

void Room::ProcessMonster()
{
	for (auto item : _monsters) {
		if(item.second == nullptr) continue;
		MonsterRef monster = item.second;
		MonsterState monsterState = monster->GetState();
		if (monsterState == MonsterState::IDLE || monsterState == MonsterState::MOVE) {
			// TODO 대기상태이거나 움직이는중일때만 몬스터 어그로 가능
			float x = _levelMonsterSpawnLocation[monster->SpawnerID].x;
			float y = _levelMonsterSpawnLocation[monster->SpawnerID].x;
			float AggroRange = monster->GetAggroRange();
			int searchRange = std::ceil(AggroRange / GRID_SIZE); // 서치범위
			for (auto playerid : GetNearbyPlayers(x, y, searchRange))
			{
				ObjectRef player = _objects[playerid];
				if(!player) continue;

				if (monster->Distance2D(player->posInfo->x(), player->posInfo->y()) > AggroRange)
					continue;

				// TODO 해당 몬스터 근처에 사람검색 일단은 바로 잡힌사람으로 어그로 
				monster->SetTarget(playerid);
				break; // 지금은 처음잡은사람 바로 나감
			}
		}
		else if(monsterState == MonsterState::AGGRO || monsterState == MonsterState::ATTACK)
		{
			// TODO 어그로 상태이거나 공격상태일때만 확인 스폰위치에서 타겟이 거리를 벗어나면 어그로 잃음
			uint64 spawnerID = monster->SpawnerID;
			float x = _levelMonsterSpawnLocation[spawnerID].x;
			float y = _levelMonsterSpawnLocation[spawnerID].y;
			//Vector3 SpawnerVector(x, y, 0);
			float AggroRange = monster->GetAggroRange();
			uint64 TargetID = monster->GetTarget();
			if (TargetID == 0) {
				// TODO 타겟아이디값이없는데 상태값이 이상함으로 상태값을 돌려줌
				monster->LostTarget();
			}

		}

		if (monsterState != MonsterState::ATTACK)
		{
			// 일단 움직여
			SendMonsterMove(monster);
		}
	}

}

void Room::SendMonsterMove(MonsterRef monster)
{
	if (monster == nullptr) return;
	uint64 objectid = monster->objectInfo->object_id();
	// 움직이기전 그리드 데이터를 삭제
	RemoveMonsterToGrid(objectid, monster->posInfo->x(), monster->posInfo->y());
	if (monster->GetState() == MonsterState::AGGRO)
	{
		ObjectRef object = _objects[monster->GetTarget()];
		if (object != nullptr) {
			// 몬스터 어글자 위치 설정
			monster->SetDestPos(monster, _objects[monster->GetTarget()]);

			if (object->Distance2D(_levelMonsterSpawnLocation[monster->SpawnerID].x, _levelMonsterSpawnLocation[monster->SpawnerID].y) >= monster->GetAggroRange()) {
				// 어그로 범위를 벗어남
				monster->LostTarget();
			}
			else {
				// 어그로 범위 안에있을경우 움직임
				Broadcast(monster->ServerMove(monster, (updateTick / (float)1000)), NotObjectID);
			}
			
		}
		else {
			monster->LostTarget();
		}
			
	}
	else
	{
		if (monster->bmovePos)
		{
			// 몬스터 움직임 예상
			Broadcast(monster->ServerMove(monster, (updateTick / (float)1000)), NotObjectID);
		}
		else {
			// 몬스어 어슬렁어슬렁 위치 설정
			monster->SetDestPos(monster, _levelMonsterSpawnLocation);
		}
	}
		
	//움직인 후 위치 값을 그리드 데이터에 추가
	AddMonsterToGrid(objectid, monster->posInfo->x(), monster->posInfo->y());
}

 


이것저것 생각해보고 해보고 했는데 제가 생각하기에 최선의 방법이라고 생각해서 작성해봤습니다...

더 좋은 방법이있다면, 가르침을 주신다면 감사하겠습니다...(너무 많은일이 있었어요...)

반응형