[DAY73] Character Synchronization : Animation, Attack, & Component

베리투스·2025년 11월 28일

TIL: Today I Learned

목록 보기
62/93
post-thumbnail

지금까지 배운 RPC와 Replication 기술을 총동원하여, 멀티플레이 캐릭터의 핵심인 이동(애니메이션), 시선(AimOffset), 공격(판정), 상태(HP)를 동기화했다. 단순히 변수만 맞추는 게 아니라, 서버의 권한(Authority)클라이언트의 예측(Prediction) 사이의 균형을 맞추는 것이 핵심이다.


📌 오늘의 목표

  • 애니메이션 동기화: CharacterMovementComponent의 복제 속성을 활용한 기본 이동 처리
  • AimOffset 동기화: 로컬의 컨트롤 회전값을 서버 및 타 클라이언트에게 전파하는 원리
  • 공격 동기화: Server RPCNetMulticast를 활용한 판정 및 이펙트 처리
  • 컴포넌트 동기화: ActorComponent를 통한 독립적인 상태(HP) 관리 및 복제 조건 설정

📚이론 및 원리

1. 애니메이션은 '변수'로 동기화한다

캐릭터가 걷거나 점프하는 동작을 모든 클라이언트에게 똑같이 보여주려면 어떻게 해야 할까?
CharacterMovementComponent는 이미 위치, 회전, 속도(Velocity) 등을 자동으로 동기화해주고 있다. (Replicate Movement 옵션)
따라서 애님 인스턴스에서는 이 동기화된 속도(Velocity)를 읽어와서 애니메이션 상태 머신을 구동하면 자연스럽게 해결된다.

2. 내 시선은 나만 안다 : AimOffset 동기화

캐릭터가 위아래를 쳐다보는 AimOffset은 단순히 애니메이션 변수만으로는 해결되지 않는다. 왜냐하면 '플레이어가 어디를 보고 있는지(Control Rotation)'는 보안상 서버와 내 컴퓨터(Owner)만 알고 있고, 다른 사람들은 모르기 때문이다.

  • 문제: 다른 사람 화면(Simulated Proxy)에서는 내가 하늘을 보고 있어도 정면을 보는 것처럼 나온다.
  • 해결:
    1. Client: Tick에서 내 컨트롤러의 Pitch(각도) 값을 읽어서 변수에 저장한다.
    2. Server RPC: 값이 변했다면 ServerRPCUpdateAimValue를 호출해 서버에게 "나 여기 보고 있어"라고 알린다. (Unreliable로 설정해도 무방하다. 시선은 계속 변하니까.)
    3. Replication: 서버는 이 값을 Replicated 변수에 저장하고, 다른 클라이언트들에게 전파한다.
    4. AnimInstance: 동기화된 Pitch 값을 애니메이션 블루프린트에 전달하여 허리를 굽히거나 젖히게 만든다.
// [PlayerCharacter.cpp]
void ADXPlayerCharacter::Tick(float DeltaTime)
{
    // 로컬 클라이언트라면 내 시선 각도를 서버로 보낸다
    if (IsLocallyControlled())
    {
        float NewPitch = GetControlRotation().Pitch;
        if (NewPitch != CurrentAimPitch)
        {
            ServerRPCUpdateAimValue(NewPitch);
        }
    }
}

// 서버는 받아서 변수를 갱신 -> Replicated 속성이므로 다른 클라들에게 자동 전파됨
void ADXPlayerCharacter::ServerRPCUpdateAimValue_Implementation(float InPitch)
{
    CurrentAimPitch = InPitch;
}

3. 공격: 판정은 서버가, 연출은 클라가

공격 버튼을 눌렀을 때의 처리는 '누가 무엇을 담당하느냐'가 가장 중요하다.

  • 1단계: 입력 (Client) -> 유저가 공격 키를 누르면 Server RPC를 호출한다.
  • 2단계: 판정 (Server) -> 서버는 데미지 판정(TakeDamage)을 수행하고, HP를 깎는다.
  • 3단계: 연출 (All Clients) -> 서버는 NetMulticast RPC를 통해 "공격 동작을 재생하라"고 명령한다. (최적화를 위해 공격한 본인은 제외하고 재생시킬 수도 있다.)

4. 액터 컴포넌트의 복제 (Actor Component Replication)

캐릭터의 HP나 스탯 관리는 별도의 ActorComponent로 분리하는 것이 좋다. 이때 컴포넌트 내부의 변수도 복제되려면 SetIsReplicatedByDefault(true) 설정과 GetLifetimeReplicatedProps 구현이 필수적이다.

특히 DOREPLIFETIME_CONDITION을 사용하면, 최대 체력(MaxHP) 같이 게임 중 잘 변하지 않거나 UI 표시용으로 주인만 알면 되는 정보를 조건부로 복제하여 네트워크 대역폭을 아낄 수 있다.


✅ 핵심 요약

구분동기화 방식핵심 포인트
이동/점프Replicated Movement컴포넌트가 동기화해준 Velocity 등을 애님 인스턴스가 참조
AimOffsetServer RPC + RepControl Rotation은 로컬 데이터이므로, RPC로 서버에 보내고 변수로 복제해야 함
공격Server RPC \rightarrow Multicast판정은 서버, 이펙트/몽타주는 멀티캐스트 (단, 로컬은 예측 가능)
HP/스탯Actor ComponentSetIsReplicatedByDefault(true) 설정 필수. 조건부 복제로 최적화
profile
Shin Ji Yong // Unreal Engine 5 공부중입니다~

0개의 댓글