Unity/연습프로젝트

[Unity]연습 프로젝트 3.패킷 설계

myjeongjun 2025. 2. 21. 21:55

이제 무엇이 필요한가?

 

다음으로는 어떤 패킷이 필요한지를 정해 둬야합니다.3D그래픽의 게임이니 이동 동기화를 위해선 위치 x,y,z 회전 x,y,z같은 플레이어에 대한 정보를 주고받는 패킷이 필요합니다.

 

패킷을 직렬화시 주의할점은 유니티에서 Vector3같은 자료형은 유니티에서만 한정되기때문에 Vector3를 바로 보내주면 알아먹지 못하기때문에 불가능합니다.

 

앞서서했던 미니 프로젝트의 경우 SendBuffer를 만들어주었지만

https://myjeongjun.tistory.com/116

 

[서버 공부] 미니 프로젝트: 채팅 방 만들기 1편

지금까지 배운 소켓프로그래밍으로 채팅을 주고 받는걸 구현해보고자 한다. 채팅방 시스템이 어떻게 구현되어있는지를 예상해서 구성해보자면 1.더미 클라이언트 2개를 두고(완성하고 나면 2

myjeongjun.tistory.com

이번에는 강의에서 배웠던 ProtoBuf를 이용해서 패킷을 주고받을까 합니다.

 

ProtoBuf를 사용하기위해 미리 패킷에 대해 정의해주고 이 스크립트를 서버,클라양쪽에 똑같이 넣어주면 되는데 

유니티에서 사용하기위해선 dll을 넣어줘야합니다. 하지만 유니티 버전문제인지 사람마다 차이는있지만 특정 dll을 추가로 넣지않으면 작동되지않은 문제가있습니다 저같은경우에는 System.Runtime.Complier와 또다른 dll을 요구했는데 그 파일은 더 이상 공식적으로 다운받을수도 없는 상태였습니다.

이틀동안 방법을 찾다가 결국 다른 버전의 ProtoBuf 컴파일러를 사용했더니 해결되더군요.

 

이제 주고받을 패킷에는 x,y,z정보 플레이어id등을 정의해두면 되겠습니다.

syntax = "proto3";
package Protocol;

import "google/protobuf/timestamp.proto";
option csharp_namespace = "Google.Protobuf.Protocol";

enum MsgId 
{
  S_ENTER_GAME = 0;
  S_LEAVE_GAME = 1;
  S_SPAWN = 2;
  S_DESPAWN = 3;
  C_MOVE = 4;
  S_MOVE = 5;
  C_ROTATE = 6;
  S_ROTATE = 7;
}

message S_EnterGame 
{
  PlayerInfo player = 1;
}

message S_LeaveGame 
{
}

message S_Spawn 
{
  repeated PlayerInfo players = 1;
}

message S_Despawn 
{
  repeated int32 playerIds = 1;
}

message C_Move 
{
    PositionInfo posInfo = 1;
    RotateInfo rotateInfo = 2;
}

message S_Move 
{
  int32 playerId =1;
PositionInfo posInfo = 2;
RotateInfo rotateInfo = 3;
}


message PlayerInfo 
{
  int32 playerId = 1;
  string name = 2;
  float posX = 3;
  float posY = 4;
  float posZ = 5;

  float rotX = 6;
  float rotY = 7;
  float rotZ = 8;
}

message PositionInfo
{
    float posX = 1;
    float posY = 2;
    float posZ = 3;
}

message RotateInfo
{
    float rotateX = 1;
    float rotateY = 2;
    float rotateZ = 3;
}

 

플레이어의 이동,애니메이션 구현은 이 글의 주제와는 떨어져있으니 생략하고 동기화 부분으로 바로 넘어가보고자 합니다.

 

제가 구상한 상속구조는 CharacterManager -> playerManager와 같습니다. playerManager는 이제 Animation,Locomotion등을 통틀어서 관리하는 구조입니다.

 

멀티 플레이의 핵심중 하나는 "나"의 Player와 "다른 사람"의 Player를 구분시켜놓아야합니다.서로 다른 사람들의 입장에서 자신의 캐릭터는 Player이긴하나 나와 똑같은 Player로 인식하지 못하게 해야합니다.그렇지않으면 내 입력에 의해 다른 사람도 동작해버릴테니까요.

 

그렇다면 방법은 프리팹을 분리해서 MyPlayer와 Player를 구분 후 Player프리팹은 자신의 입력이아닌 다른 사람의 입력에 의한 변화된 위치,회전,애니메이션을 패킷으로 만들어서 전송시킨후 받아서 업데이트 시키게 만들어야합니다.

 

일단 스폰 패킷을 받아서 접속시 같은 장소에 소환하는것 까진성공했습니다.

하지만 서로의 위치정보를 주고받고 있진 않습니다.

 

약 5시간정도 씨름해서 위치정보를 주고받을수있게 되었습니다..

 

CharacterLocomotionManager -> PlayerLocomotionManager의 상속구조에서 나의 Player는 InputManager를 통해 PlayerLocomotionManager으로 동작을 수행하면서 패킷을 보냅니다. (update 될때마다 보내고있어서 부하가 심해질거같은데, 보완 방법은 일정 시간마다 보내고, 보간하는 방식을 생각했습니다. 클라이언트 수가 많아지고 실제로도 그런지 테스트후에 수정해보도록 하겠습니다.)

 

그럼 다른 클라이언트들은 업데이트된 위치정보가 담긴 패킷을 받고 그 위치를 적용시켜주면됩니다. 바로 그 위치로 이동시키면 부자연스러우니 Lerp or Slerp를 이용해서 부드럽게 이동할수있도록 해줍니다.

 

아직은 부자연스러우나 위치는 정확하게 전달되고있는걸로 보입니다.

 

다음 단계인 회전추가,

 

이제 애니메이션 추가인데, 이건 아마 애니메이션 실행시 해당 애니메이션의 이름을 보내고 받은 다른 Player는 그 이름을 가지고 애니메이션을 실행시키는 식으로 동작할 겁니다.

+수정)좀더 생각해보니 아직 구현하진 않았지만 걷기같은 경우에는 걸을때마다 패킷을 보내면 패킷을 너무 자주받게되어 제대로 실행이 안될 것 같더라고요

그냥 위치 변화를 계산해서 애니메이션을 실행시키는 방법이 괜찮아 보입니다.