Skip to content

플레이어 이동 동기화 구현 #6

@sonoasy

Description

@sonoasy

플레이어 이동 동기화 구현

TCP 기반 데디케이티드 서버를 통한 멀티플레이어 이동 동기화 기능 구현


1. 네트워크 프로토콜 설계

  • 패킷 타입 정의 (PacketType enum)
    • Connect - 접속
    • Disconnect - 퇴장
    • PlayerMove - 이동 요청
    • PlayerState - 상태 업데이트
    • GameState - 전체 게임 상태
  • 패킷 클래스 구현
    • NetworkPacket - 기본 패킷
    • PlayerMovePacket - 이동 패킷
    • PlayerStatePacket - 상태 패킷
    • ConnectPacket / DisconnectPacket
    • GameStatePacket - 초기 게임 상태
  • JSON 직렬화 구현
    • Unity: JsonUtility 사용
    • Server: System.Text.Json 사용

3. TCP 서버 구현

  • 포트 12345에서 TCP 서버 대기
  • 멀티 클라이언트 동시 처리 (ConcurrentDictionary)
  • 플레이어 고유 ID 자동 부여
  • 비동기 패킷 수신/전송
  • 플레이어 이동 패킷 처리
  • 전체 클라이언트 브로드캐스트
  • 접속/퇴장 처리
  • 디버깅 로그 추가

4. Unity 클라이언트 구현

  • NetworkClient.cs - 네트워크 통신 담당
    • 서버 연결 (127.0.0.1:12345)
    • 비동기 패킷 수신
    • 패킷 타입별 처리
    • 메인 스레드 디스패처
  • 다른 플레이어 관리
    • Dictionary<ulong, GameObject> 로 관리
    • 플레이어 생성/업데이트/삭제
    • 위치 보간 준비 (Lerp)
  • PlayerMove.cs 수정
    • NetworkClient 참조 추가
    • 이동 시 서버로 전송
    • Inspector 연결 설정

주요 이슈

Issue #1: JSON 직렬화 라이브러리 불일치

문제: Unity는 Newtonsoft.Json, Server는 System.Text.Json
해결: 각 플랫폼에 맞는 PacketSerializer 별도 구현

Issue #2: Int2 직렬화 실패

문제:

"TargetGridPos": {}  // 값이 비어있음

원인:

  • Unity JsonUtility는 Property 직렬화 안 함 (Field만)
  • Server System.Text.Json은 Field 직렬화 안 함 (Property만)

해결:

  • Unity Int2: Field 사용 (public int X;)
  • Server Int2: Property 사용 (public int X { get; set; })
  • 대소문자 통일 (X, Y - 대문자)

결과:

"TargetGridPos": {"X":-6, "Y":-8} 

Issue #3: TCP 패킷 경계 문제

문제: 여러 패킷이 합쳐져서 파싱 오류
현재 상태: 가끔 발생, 대부분 정상 작동
TODO: 다음 단계에서 패킷 길이 헤더 추가 예정

Issue #4: PlayerId가 항상 0

문제: Unity에서 전송하는 PlayerId가 0
현재 상태: 서버는 정상 작동, 단일 플레이어 테스트 문제없음
TODO: 다음 단계에서 수정 예정


테스트 결과

서버 로그:

크레이지 아케이드 서버 시작! 포트 12345
[접속] 플레이어 1
[전송] 플레이어 1에게 게임 상태 전송
[디버그] 받은 JSON: {"Type":10,"PlayerId":0,"TargetGridPos":{"X":-6,"Y":-8}}
[이동] 플레이어 1: (-6, -8)

Unity 로그:

서버 연결 성공!
내 플레이어 ID: 1
이동 전송: (-6, -8)
이동 전송: (-7, -8)

성능

  • 지연시간: ~10ms (로컬)
  • 패킷 크기: ~100-200 bytes (JSON)
  • 처리 속도: 초당 수백 개 패킷 처리 가능

변경된 파일

신규 파일

Server/
├── Program.cs (신규)
├── NetworkPacket.cs (신규)
├── NetworkPackets.cs (신규)
├── PacketSerializer.cs (신규)
├── Int2.cs (Unity에서 복사)
├── PlayerState.cs (Unity에서 복사)
└── PlayerStats.cs (Unity에서 복사)

UnityProject/CrazyArcade/Assets/Scripts/
├── Network/ (신규 폴더)
│   ├── NetworkClient.cs (신규)
│   ├── NetworkPacket.cs (신규)
│   ├── NetworkPackets.cs (신규)
│   └── PacketSerializer.cs (신규)
└── GameCore/
    └── Int2.cs (수정 - Field로 변경)

수정된 파일

PlayerMove.cs - NetworkClient 연동 추가
Int2.cs - Property → Field, 대소문자 통일
.gitignore - Server 빌드 파일 제외 추가

다음 단계

  • 패킷 길이 헤더 추가 (안정성)
  • PlayerId 동기화 수정
  • 다른 플레이어 시각화 테스트 (2명 동시 접속)

후속 작업

  • 이동 보간 개선 (부드러운 이동)
  • 물풍선 동기화 (#이슈번호)
  • 아이템 효과 동기화 (#이슈번호)
  • 방 시스템 (#이슈번호)

비고

핵심은 Unity/Server 간 JSON 직렬화 차이 해결

  • Unity는 Field만 직렬화
  • Server는 Property만 직렬화
  • 두 방식 모두 호환되도록 설계

1. JSON 직렬화 플랫폼 차이

  • Unity JsonUtility: Field만, 가볍고 빠름
  • .NET System.Text.Json: Property 기본, 유연함
  • 크로스 플랫폼 시 신중한 선택 필요

2. TCP 스트림 특성

  • 패킷 경계 없음 (스트림 기반)
  • 길이 헤더 또는 구분자 필요
  • 여러 패킷이 한 번에 읽힐 수 있음

3. Unity-Server 코드 공유 전략

  • 데이터 구조는 공유 가능
  • 직렬화 코드는 플랫폼별 구현
  • 조건부 컴파일 #if UNITY 활용

Related: #물풍선동기화이슈 #방시스템이슈

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions