MMORPG에서 내가 공격을 했다면, 나의 행동을 다른 플레이어에게 브로드 캐스팅 해줘서 동기화를 해준다.
그런데 이 때, 맵에 있는 모든 플레이어한테 보낼 필요가 있을까?
내가 했던 MMORPG에서는 다른 플레이어와 일정거리가 멀어지면 해당 플레이어는 컬링이 되어서 렌더링 되지 않는다.
이렇게 렌더링 되지 않는 플레이어와는 통신을 하지 않는게 서버에 부하도 가지 않고 좋은 방법일 것 같다.
그래서 이번에 내가 작업 중인 프로젝트에도 이를 적용시켜보고자 한다.
어떤식으로 하면 좋을지 생각했는데 다음과 같은 방법으로 해보았다.
- void Room::HandleMove(Protocol::C_MOVE pkt)에서 클라이언트의 현재 위치를 전달 받음.
- 클라이언트의 위치를 서로 비교 후 렌더링 거리보다 멀 경우 그 클라이언트는 제외하고 패킷 전송.
Unreal CullDistance Volume
우선 언리얼에서 제공하는 기능으로 간단하게 컬링을 적용시켜 주었다.
서버의 Room 코드와 Object 코드를 수정 해주었다.
struct Vector3D
{
float x;
float y;
float z;
float DistanceTo(const Vector3D& other) const
{
return sqrt(pow(x - other.x, 2) + pow(y - other.y, 2) + pow(z - other.z, 2));
}
};
class Object : public enable_shared_from_this<Object>
{
...
Vector3D vector3D{ 0.f ,0.f ,0.f };
...
};
void Room::SendByDist(SendBufferRef sendBuffer, PlayerRef player, uint64 senderId)
{
if (player->objectInfo->object_id() != senderId)
return;
for (auto& item : _objects)
{
PlayerRef Otherplayer = dynamic_pointer_cast<Player>(item.second);
if (Otherplayer == nullptr)
continue;
if (Otherplayer->objectInfo->object_id() == senderId)
continue;
//거리 측정해서 3000.f 이내면 데이터 보냄.
float Dist = player->vector3D.DistanceTo(Otherplayer->vector3D);
cout << "거리 : " << Dist << " 아이디 : " << Otherplayer->objectInfo->object_id() << endl;
if (Dist < 3000.f)
{
if (gameSessionRef session = Otherplayer->session.lock())
session->Send(sendBuffer);
}
}
}
SendByDist 함수를 만들어서 데이터를 보낸 플레이어와 다른 플레이어들의 거리를 체크해서 3000.f 보다
거리가 짧을 경우 데이터를 보내도록 해줬다.
잘 작동하는거 같은데, 플레이어끼리 언리얼의 distance culling이 적용이 안된다.
찾아보니 static mesh에만 적용되기 때문에 움직이는 오브젝트는 적용이 안된다고 함.
그래서 S_MOVE 패킷에 bool CanRendering을 추가해준 다음,
서버에서 거리 연산을 한 다음, 지정된 컬링 거리보다 멀어지면 bool CanRendering을 false로 만들어서 클라이언트에 한번만 전달하도록 해줬다.
void UMyGameInstance::HandleMove(const Protocol::S_MOVE& MovePkt)
{
...
if (MovePkt.canrendering())
Player->SetActorHiddenInGame(false);
else
Player->SetActorHiddenInGame(true);
...
}
void Room::SendByDist(PlayerRef player, Protocol::S_MOVE pkt, uint64 senderId)
{
bool sendOneTime = false;
if (player->objectInfo->object_id() != senderId)
return;
for (auto& item : _objects)
{
PlayerRef Otherplayer = dynamic_pointer_cast<Player>(item.second);
if (Otherplayer == nullptr)
continue;
if (Otherplayer->objectInfo->object_id() == senderId)
continue;
//거리 측정해서 3000.f 이내면 데이터 보냄.
float Dist = player->vector3D.DistanceTo(Otherplayer->vector3D);
cout << "거리 : " << Dist << " 아이디 : " << Otherplayer->objectInfo->object_id() << endl;
if (Dist < 3000.f)
{
SendBufferRef sendBuffer = ClientPacketHandler::MakeSendBuffer(pkt);
if (gameSessionRef session = Otherplayer->session.lock())
session->Send(sendBuffer);
sendOneTime = true;
}
if (Dist >= 3000.f && !sendOneTime)
{
pkt.set_canrendering(false);
SendBufferRef sendBuffer = ClientPacketHandler::MakeSendBuffer(pkt);
if (gameSessionRef session = Otherplayer->session.lock())
session->Send(sendBuffer);
sendOneTime = true;
}
}
}
잘 동작한다.
이제 이거를 이동 뿐만 아니라 공격과 같은 기존에 BroadCasting을 사용하던 부분들을 사중해주면 될 것같다.
'언리얼 엔진 서버 연동' 카테고리의 다른 글
몬스터(NPC) AI 적용 및 길찾기 (2) (0) | 2024.05.28 |
---|---|
몬스터(NPC) AI 적용 및 길찾기 (1) (0) | 2024.05.26 |
IOCP 서버 구조 + 서버 스트레스 테스트 (0) | 2024.04.23 |
플레이어 사망 처리, 채팅 (0) | 2024.04.21 |
플레이어의 공격과 피격(4) (0) | 2024.04.20 |