UDN
Search public documentation:

VariableReplicationKR
English Translation
日本語訳
中国翻译

Interested in the Unreal Engine?
Visit the Unreal Technology site.

Looking for jobs and company info?
Check out the Epic games site.

Questions about support via UDN?
Contact the UDN Staff

변수 복제

문서 요약: 한 게임 인스턴스에서 다른 게임 인스턴스로 변수를 복제하는 방법에 대해 설명합니다.

문서 변경 내역: 마지막으로 Michiel Hendriks가 업데이트, Two.VariableReplication 에서 가져옴. Mike Lambert (UdnStaff?) 원저

개요

변수는 복제 구문을 통해 전송할 수 있는 두 가지 데이터 유형 중 하나입니다(나머지 하나는 함수임). 여기서는 한 Unreal 인스턴스에서 다른 인스턴스로 데이터를 복제하는 방법에 대해 논의합니다. 상당히 많은 작은 요소가 처음의 예상보다는 좀 더 복잡한 이 프로세스를 구성하고 있습니다.

변수는 로컬 시스템의 변경 사항과 관련하여 타 시스템을 최신 상태로 유지하기 위해 복제됩니다. 대부분의 경우 Unreal 서버에서는 변경된 정보를 알리기 위해 변수를 클라이언트에 복제한다고 할 수 있습니다. 플레이어의 속도가 바뀌었다면 두 네트워크 업데이트 중에 이 플레이어의 동작을 클라이언트가 올바르게 시뮬레이션할 수 있도록 클라이언트에 복제해야 합니다.

이 문서는 NetworkingTome의 일환입니다.

변수 데이터 신뢰성

이에 대한 간단한 답은 변수는 항상 신뢰할 수 있다는 것입니다. 액터의 변수가 복제되면 해당 오브젝트의 변수와 관련한 리타이어먼트가 밖으로 나가는 패킷 ID의 사본을 받게 됩니다. 이 패킷에 대해 부정적인 평가가 나타나면 해당 패킷 ID와 연결된 변수를 채널의 Dirty 배열에 넣습니다. 액터를 다음 번에 복제할 때 다른 식으로 복제되지 않았다면 이 변수를 모두 추가합니다. 따라서 변수 데이터가 서로 간에 교환되게 보장할 수 있습니다. 그러나 업데이트 2를 처리하기 전에 업데이트 1을 대기해야 한다는 점에서는 신뢰할 수 없습니다.

새로 관련된 액터(생성)

액터가 클라이언트와 연관되게 되면 연결에 대해 새로운 UActorChannel 이 생성되므로 액터의 기본 등록 정보를 통해 적합한 클래스의 액터가 클라이언트에 생성됩니다. 액터 생성 직후 서버가 변수를 설정한 경우(관련도 확인 이전에) 이러한 변수가 복제 구문에 부합한다고 가정하고 액터 데이터에 대한 첨부 형태로 클라이언트에 보내집니다. 현재 틱이 끝날 때까지 복제가 수행되지 않으므로 액터 자체를 통해 초기 복제에 대한 변수를 설정할 수 있는 시간은 충분합니다. 상대방측에서 액터에서 설정된 이러한 변수를 수신하고 이후 PostNetBeginPlay 를 호출합니다( PostBeginPlay 는 네트워크에서 메모리로 액터의 변수를 읽어오기 전에 호출되므로).

처음에 복제할 변수를 결정할 때 서버는 기본 등록 정보와 다른 모든 변수를 보냅니다. 서버가 액터의 기본 등록 정보와 관련하여 클라이언트와 다른 생각을 갖고 있으면 서버는 서버의 버전과 다른 변수만 복제합니다. (동일한 토큰으로 클라이언트측 코드에서 변수가 변경된 경우 서버가 이를 알지 못하며 변경된 데이터를 복제하지 않습니다.)

변수 복제 시간

복제된 변수는 각 틱의 끝에서만 복제됩니다. 즉 한 루프 또는 어떤 다른 코드 블록 동안 변수를 반복적으로 변경하거나 마지막 변수만 복제됩니다. 한 틱 중 여러 번 변수를 변경하고 틱 완료 시 다시 원래 값으로 되돌린 경우 변수 변경이 확인되지 않으며 네트워크 대역폭도 사용하지 않습니다.

물론 여기에도 예외는 있습니다. 서버에서 새로운 액터를 생성할 경우 틱이 종료되기 전까지는 변수가 복제되지 않습니다. 그러나 복제 함수를 호출하면 복제된 함수가 즉시 전송되며 상대측이 이를 수신하기 위해서는 그에 따라 작동하는 액터가 있어야 합니다. 따라서 액터를 아직 복제하지 않았다면 Unreal에서 첫 번째 복제 함수 호출 시 액터와 그 변수를 강제로 복제합니다. 틱이 끝나면 보낸 변수에 대해 액터를 다시 확인하게 됩니다.

클라이언트에서 서버에 복제

또 하나의 단점은 클라이언트-서버 복제는 자체 PlayerController에서만 가능하다는 점입니다. 즉 클라이언트에서 서버로 변수를 복제하려 할 때 현재 PlayerController(또는 상위 클래스 정의 중 하나)에서 수행되어야 합니다. 클라이언트에서 서버로 복제하는 함수는 PlayerController와, 모든 소유 항목에 대해 실행되므로 복제 함수를 사용하여 이러한 제약을 해결할 수 있습니다. 위치와 속도를 복제할 때는 클라이언트에 여러 Pawn이 있을 수 있지만 현재 Pawn만이 자신의 것이며 서버에 변수를 복제할 수 있습니다. 다른 액터 관련 정보를 서버에 복제할 수 있다면 서버에 문제가 발생할 것입니다. 클라이언트 A의 값을 액터 Z 또는 클라이언트 B에 적용할 수 있기 때문입니다. 이들을 구분할 방법이 없으므로 서버는 Pawn 변수만 서버에 복제하도록 허용합니다.

Native 복제

자주 복제되는 데이터가 있는 일부 기본 클래스는 스크립트 복제가 아닌 Native(C++) 복제를 사용합니다. 이러한 클래스는 클래스 정의에 nativereplication 키워드가 있습니다. 이러한 클래스는 스크립트 조건을 무시하고 대신 C++ GetOptimizedRepList() 함수를 기준으로 등록 정보를 복제합니다. 그러나 UnrealScript? 복제 블록에서는 이 경우에도 복제할 수 있는 모든 등록 정보를 나열하므로 컴파일러가 이와 같이 표시할 수 있습니다. 등록 정보를 올바르게 전송하기 위해서는 이 같은 작업이 필요합니다.

필요 시만 복제

변수 복제를 좀 더 최적화할 수도 있습니다. 서버에서 서버의 최신 버전과 비교하여 부정확한 버전이 클라이언트에 있다고 판단할 경우에만 클라이언트에 변수를 복제할 수 있습니다. 서버가 데이터를 복제할 때는 최근 전송되어 Recent 배열에 저장된 값에 현재 값을 비교합니다. 최초 복제인 경우 코드는 액터의 기본 등록 정보에 있는 값과 현재 값에 대해 현재 값을 확인합니다. 따라서 서버는 클라이언트에 있는 항목에 관계없이 변경된 변수를 보냅니다. 클라이언트와 서버 모두에서 실행되는 시뮬레이션 함수에서 값이 변경된 경우 서버는 클라이언트의 값을 인지하지 못하며 자체 값을 복제합니다. 모든 경우 이 변수가 클라이언트와 서버 모두에서 확실하게 변경될 수 있다면 이 값에 대한 복제 조건을 제거할 수 있습니다. 이 액터의 관련성이 변하는 경우, 관련성이 있게 되었을 때 앞서 언급한 바와 같이 클라이언트에서 기본 등록 정보로 초기화됩니다. 이것이 부정확하며 복제 값이 없는 경우 계속 부정확한 상태를 유지합니다. 액터가 최초로 관련성이 있게 되면 bNetInitial 변수를 사용하여 항목을 복제한 다음 서버와 클라이언트 모두에서 실행되는 시뮬레이션 함수를 통해 값을 변경할 수 있습니다. bNetInitial 변수와 유사 항목에 대해서는 차후 논의하겠습니다.

특수 복제 변수

복제 조건을 작성할 때는 함수/변수의 복제 여부 평가에 몇 가지 유용한 변수를 활용할 수 있습니다. 이러한 변수는 액터에서 정의되며 다음과 같습니다.

bNetInitial
네트워크에서 이 액터가 복제되는 것이 최초이면 True입니다. 기본 등록 정보와 다르나 액터의 수명 동안 변하지 않는 변수에 유용합니다.
bNetOwner
복제하려는 플레이어가 이 액터를 직접 소유하는 경우 True입니다.
bNetRelevant
액터가 현재 관련되었습니다. 서버측에서만 유효합니다.
bDemoRecording bClientDemoRecording bRepClientDemo bClientDemoNetFunc bDemoOwner
데모 녹화용으로 사용합니다.

변수 복제 예

이제 복제의 몇 가지 중요한 예와, 변수와 함께 사용하는 방법을 살펴보기에 적당한 시점이 되었습니다. 이를 통해 작동 방식에 대한 이해를 높이고 자신의 코딩 작업을 위한 예를 확인할 수 있을 것입니다.

몇 가지 예를 통해 우리가 원하는 바를 정확하게 설명해 보겠습니다. 먼저 쉬운 것부터 출발하여 점차 복잡한 것으로 확대해갈 것입니다. 다음 예는 별도로 지정하지 않는 한 모두 액터에서 가져온 것입니다.

참고: 이러한 예는 사실상 Unreal Tournament에서 온 것이며 실제 코드와는 무관합니다. 그러나 아래의 예는 변수 복제를 이해하는 데 유용합니다.

PlayerPawn에서

if ( Role < ROLE_Authority )
    Password, bReadyToPlay;

여기서는 암호(Password)를 보낸 서버, 암호로 보호되는 서버에 연결을 시도하거나 관리자(admin)로 로그인을 시도하는 시점을 보여 줍니다. 서버에서는 유효성 확인을 위해 암호가 필요하며, 이 코드를 서버로 보냅니다. 서버와 클라이언트 모두에서 이러한 확인을 평가해 보겠습니다.

서버
서버에서 Role == ROLE_Authority. 이 조건을 살펴보면 쉽게 False란 사실을 알 수 있으므로 암호가 서버측에 복제되지 않습니다. 즉 서버가 연결 상대방에게 암호를 보내지 않습니다.
클라이언트
클라이언트에서 RoleROLE_Authority 이 아니므로(서버 자체에서만 Authority임) 이 복제 확인 평가는 클라이언트에서 True입니다. 또한 변수 복제는 현재 플레이어 Pawn에서만 적용되므로 사용자 자신에 대해서만 서버로 전송됩니다. (다른 사람의 암호를 보내지 않는 건 너무도 당연합니다.)

또한 게임을 시작하기 위해 모두가 클릭해야 하는 토너먼트 스타일 게임에서 사용되는 bReadyToPlay 방식을 보여 줍니다. 자체 플레이어 Pawn 액터의 일부인 경우에만 클라이언트가 서버에 변수를 복제할 수 있으므로 자신에 대해서만 전송된다는 사실을 기억하십시오.

if( Role == ROLE_Authority ) Owner, Role, RemoteRole;

오브젝트의 소유자가 항상 클라이언트에 복제되게 합니다. 소유자의 변수 자체는 복제되지 않으며(재귀적 복제는 없음) 액터 자체만 복제됩니다. 즉 Owner 에 대한 참조가 클라이언트에 복제되기 때문에 단순 비교 if (PlayerPawn? == Owner) 는 올바르게 작동합니다. UT에서는 확신 있는 복제와 확신 없는 복제가 모두 동일하게 처리되므로 둘 사이에 별 차이가 없습니다. Unreal 1에서는 특정 지점에서 영향이 있었으나 개발에서는 더 이상 역할이 없으므로 혼동하지 마십시오.

처음에는 RoleRemoteRole 복제가 생소하게 느껴질 수 있습니다. 이들은 클라이언트에서 반전되어야 하기 때문입니다. 그러나 이러한 전환을 유발하는 Native 코드가 있습니다. 이 코드가 클라이언트에서 두 값을 반전되게 하므로 RemoteRoleROLE_Authority 가 됩니다. 왜 이들이 처음 복제되는지 궁금할 수 있습니다. 클라이언트는 코드에 이러한 변수에 대한 직접 사용을 포함하지 않으나 복제 구문이라고 하는 매우 중요하고 쉽게 살펴볼 수 있는 지점이 있습니다. 클라이언트가 특정 함수 호출을 서버에 복제해야 할지 또는 플레이어 Pawn의 변수를 서버에 복제할지 여부를 평가할 때 클라이언트는 RoleRemoteRole=이 무엇인지에 대한 정확한 사본이 필요합니다. 이런 사본이 없으면 서버에 무엇을 보낼 것인지에 대한 필요한 의사 결정이 불가능합니다. 서버는 =Role/RemoteRole 을 통해 서버로 보내진 복제 데이터의 유효성을 확인하므로 클라이언트가 성공적으로 데이터를 복제하려면 최신 사본이 필요합니다.

if( bNetOwner && Role == ROLE_Authority )
    bNetOwner, Inventory;

인벤토리는 액터의 전체 인벤토리를 나열하는 연결된 목록의 앞부분입니다(보통 Pawn 을 통해서만 활용함). 인벤토리를 클라이언트에 복제하여 클라이언트가 인벤토리에 대해 알고 자신의 HUD 및 항목을 표시하게 하려 합니다. 한 대상이 목록의 다음 대상을 가리키는 연결 목록의 맨 처음만 복제하는 것은 충분하지 못합니다. 각 액터에는 자체적으로 관련성이 있어야 하고(현재 플레이어 Pawn이 소유한 기준에 부합) 각 연결 자체를 복제해야 합니다. 또한 인벤토리 서브클래스 액터가 체인 내 어딘가에 있으므로 인벤토리에도 복제된 인벤토리 변수가 있습니다. 이것이 사용자가 인벤토리 클라이언트측을 확인할 수 있는 방법이기도 합니다. 게임 내 모든 다른 플레이어가 소유하는 항목(현재 무기, 보호 벨트 효과 등은 다른 방법으로 전송됨) 전체 인벤토리의 목록을 알 필요는 없으므로 인벤토리는 클라이언트가 소유자인 경우에만 복제됩니다.

위 복제 구문에서 또 다른 변수는 bNetOwner입니다. 기본적으로 이 변수는 클라이언트와 서버 모두에서 설정되므로 복제가 필요하지 않습니다. 대역폭을 다소라도 소모하면서도 효과가 없다는 우려가 있을 수도 있습니다. 향후의 패치에서는 제거될 것으로 보입니다.

if( DrawType == DT_Mesh && Role == ROLE_Authority )
    Mesh, PrePivot, bMeshEnviroMap, Skin, MultiSkins, Fatness, AmbientGlow,
    ScaleGlow, bUnlit;

위의 모든 메쉬 특정 변수는 액터가 현재 메쉬에 표시되는 경우에만 복제된다는 사실을 알 수 있습니다. 스프라이트든, 브러쉬든 또는 그리기 유형이 전혀 없는 경우에도 이러한 변수를 보낼 이유가 없습니다.

이제 좀 더 복잡한 변수 복제 구문을 살펴보겠습니다.

if( RemoteRole == ROLE_SimulatedProxy ) Base;

여기서는 Role == ROLE_Authority가 없는 약간 더 복잡한 복제 구문을 볼 수 있습니다. 여기서는 현재 베이스=( SetBase 를 통해)는 액터가 시뮬레이션 프록시로 설정된 경우에만 복제됩니다. 액터가 =덤프 프록시 가 되게 설정한 경우 클라이언트측에서는 베이스 변경이 없으며 그 대신 서버가 사용자에게 보내는 움직이는 위치 업데이트가 발생합니다(향후에 자세히 다룸). 따라서 SetBase 를 사용할 경우 시뮬레이션 프록시임을 확인하고 시뮬레이션 프록시가 아니면 SetBase 가 아닌 다른 변수를 사용하십시오. SetBase 와 시뮬레이션 프록시가 모두 필요할 경우 베이스가 복제되지 않습니다. 베이스를 설정하는 시뮬레이션 함수와 같은 다른 메커니즘을 동원하여 복제해야 합니다. 이것은 클라이언트에서 실행되며 이에 대해서도 향후에 자세히 다루겠습니다. 액터가 무관한 상태에서 자신의 호출된 시뮬레이션 함수를 가져올 때는 여전히 문제가 발생할 수 있습니다. 이로 인해 클라이언트측에서 시뮬레이션 함수가 전혀 호출되지 않습니다. 액터가 나중에 관련이 있게 되었을 때는 함수가 전혀 호출되지 않고 베이스가 설정되지 않은 것과 같은 상황이 되므로 버그로 간주될 수도 있습니다. 시뮬레이션 함수가 항상 호출되도록 액터와 연결 대상을 =bAlwaysRelevant=에 연결할 수도 있지만 네트워크 대역폭에 다소 부담이 될 수 있습니다.

if( RemoteRole == ROLE_SimulatedProxy && Physics == PHYS_Rotating
    && bNetInitial )
    bFixedRotationDir, bRotateToDesired, RotationRate, DesiredRotation;

위의 구문은 상대적으로 쉬운 것이지만 점점 더 복잡해집니다. 여기서는 위와 유사한 로직을 볼 수 있습니다. 서버는 클라이언트가 시뮬레이션된 프록시인 경우에만 클라이언트에 이러한 변수를 보냅니다. 그러나 이 모든 변수는 PHYS_Rotating 에만 적용되므로 클라이언트가 PHYS_Rotating 을 사용하지 않으면 클라이언트에 변수를 복제할 필요가 없습니다. 또한 마지막에 있는 bNetInitial 절도 매우 중요합니다. 이 절은 해당 변수가 최초로 복제되는 클라이언트에만 복제된다고 선언합니다.

if( bSimFall || (RemoteRole == ROLE_SimulatedProxy && bNetInitial
    && !bSimulatedPawn) )
    Physics, Acceleration, bBounce;

여기서는 몇 가지 흥미로운 변수를 복제합니다. bSimFall 이 true로 설정된 경우 Physics 를 클라이언트에 복제합니다. 이것은 인벤토리에서 무기를 토스할 때 사용됩니다. 토스될 때는 Physics 가 인벤토리에 있는 동안의 PHYS_None 에서 공기 중으로 날아가는 동안의 PHYS_Falling 으로 바뀝니다. 다시 땅에 닿을 때 PHYS_None 으로 됩니다. 이러한 Physics 변경은 토너먼트 무기 수명 중 주요 시점에 bSimFall=을 설정하여 수행할 수 있습니다. 인벤토리로부터 던져지면 변경을 적용하기 위해 True로 설정되고 땅에 닿아 다시 =Physics=로 바뀔 때까지 그대로 유지됩니다. 땅에 닿아 물리를 재설정한 후에는 =bSimFall 이 False로 설정되므로 Physics 가 더 이상 변하지 않습니다. 구문의 두 번째 반을 보면 시뮬레이션된 프록시이고, 이 액터가 인터넷에서 복제되는 것이 처음이며, 시뮬레이션된 Pawn이 아닌 경우에만 Physics 가 필요한 것을 볼 수 있습니다. ROLE_SimulatedProxyRemoteRole 이 있는 Pawn인 경우 bSimulatedPawn 변수는 True로 설정됩니다. :) 즉 복제되기 전의 시뮬레이션 프록시 비 Pawn 액터에 대한 변경 사항이 클라이언트에 복제됩니다. Physics=는 Pawn에 대해 복제되지 않습니다. 오히려 물리에 대한 코드가 코드에 하드 코딩되어 그라운드에 푸쉬되게 됩니다. 기본적으로 Pawn이 점프하면 서버가 수직 속도를 설정하여 위쪽으로 이동하게 합니다. 이 속도가 클라이언트에 복제됩니다(아래에서 설명). 그러면 클라이언트가 이 Pawn이 플레이어인지 여부(예: 봇이나 플레이어 Pawn)와, 현재 날 수 없고(Pawn의 =bCanFly 변수로 설정) 다른 중력과 물리가 적용되는 수중 지역에 없음을 확인합니다. 따라서 실질적으로 Pawn에 대한 Physics 는 절대로 클라이언트에 복제되지 않으며 그렇게 보일 뿐입니다. 플레이어 Pawn으로 대체 물리를 제작하려면 bCanFly 를 True로 설정하여 플레이어가 수중이나 공중에 있을 때 Native 코드가 플레이어 위로 떨어지는 물리를 시행하지 않도록 해야 합니다. 그런 다음 대체 운송 수단을 구현해야 합니다.

if( !bCarriedItem && (bNetInitial || bSimulatedPawn
    || RemoteRole < ROLE_SimulatedProxy) && Role == ROLE_Authority )
    Location;

여기서는 다른 중요한 변수인 Location 이 등장했습니다. 이것은 복제입니다. 지니는 품목에 대해서는 복제되지 않음을 확인했습니다. 이는 인벤토리=의 경우에 유용합니다. 플레이어가 이를 지닐 때는 어디에도 사용되지 않으므로 위치를 복제할 이유가 없습니다. 플레이어의 전체 인벤토리를 복제하면 네트워크 대역폭을 상당 부분 소모할 수 있습니다. 이 구문의 다음 섹션을 살펴보겠습니다. 액터가 최초로 복제되는 경우 위치가 복제됩니다(조건의 다른 부분이 True라고 가정함). 게임을 시작했을 때 모든 Pawn이 다양한 위치에 생성되므로 매우 유용합니다. 예를 들어 로켓 발사대에서 로켓이 나올 때는 그 시작 위치를 설정해야 합니다. =bSimulatedPawn 인 경우 위치도 전송됩니다. 이것은 Pawn 복제에 발생할 수 있는 오류를 해결하는 데 도움이 됩니다. Pawn이 방향을 바꿀 수 있고 클라이언트가 이를 항상 알지 못할 수도 있으므로(지연 등의 이유로) 이러한 위치 재설정은 위치 업데이트를 수정할 수 있는 유일한 방법입니다. 지연 발생 시 자체 위치 수정에는 사용할 수 없습니다. 이 경우 AutonomousProxy 이고 PlayerPawn에 특정한 함수가 이를 처리합니다(즉 ClientAdjustPosition). 예를 들어 PHYS_WalkingPHYS_Projectile 와 같지 않은 경우 매우 정확하게 그 위치를 예측할 수 있습니다. PHYS_Walking 은 "그라운드와의 연결을 계속 유지한다"는 뜻입니다. 기본적으로 클라이언트의 유일한 업데이트(위치 없음)는 속도이므로 오랜 시간 플레이어가 이동하는 상황에서 오류로 이어지기 쉽습니다. 이 위치는 정정 요인으로 활용되어 클라이언트의 보기가 플레이어의 예상 위치에서 지나치게 멀리 벗어나지 않도록 하며 동시에 속도의 복제를 유지하여 중간의 서버 업데이트를 예측할 수 있도록 합니다. 위치 복제를 허용하는 또 다른 옵션은 RemoteRoleROLE_SimulatedProxy, 즉 ROLE_DumbProxy 보다 낮은 경우입니다. ROLE_None 은 최초의 연관성을 방해하기 때문입니다. DumbProxies 는 몇 틱마다 서버로부터 주기적 업데이트를 받으며 넷플레이에서 변화무쌍한 효과를 낼 수 있습니다. 이것이 얼마 전 시도했던 하키 모드에서 이리저리 움직이는 퍽의 원인이었는데 당시에는 충분한 해결책을 찾지 못했습니다. SimulatedProxy위치 업데이트를 전송하지 않습니다. Velocity 와 현재 물리의 사용을 통해 예측되기 때문입니다. DumbProxies 는 원활한 진행을 위해 클라이언트측에서 시뮬레이션되지 않으며 위치 업데이트만 받습니다. 다양한 복제 변수의 예를 통해 이를 분명히 살펴보겠습니다.

if( !bCarriedItem && (DrawType == DT_Mesh || DrawType == DT_Brush)
    && (bNetInitial || bSimulatedPawn || RemoteRole < ROLE_SimulatedProxy)
    && Role == ROLE_Authority )
    Rotation;

여기서 다른 중요한 변수인 Rotation 을 볼 수 있습니다. 또한 위에서 설명한 것과 같은 이유로 동일한 !bCarriedItem 구문이 있습니다. 이 액터가 메쉬이거나 브러쉬일 때 회전도 복제됩니다. 스프라이트는 항상 플레이어를 마주하므로 회전을 복제하는 것은 무의미합니다. bNetInitial 도 최초로 회전이 복제되게 합니다(다시 말하지만 다른 조건이 True라고 가정). 로켓이 공중을 향해 날아갈 때는 올바른 방향을 마주봐야 합니다. 속도는 이동 방향을 결정하지만 마주보고 있는 방향도 마찬가지로 중요하며 이는 회전에 따라 결정됩니다. 시뮬레이션 Pawn인 경우 회전이 복제됩니다(자신을 제외한 레벨의 모든 플레이어 Pawn과 봇). 따라서 다른 사람들이 마주하는 방향을 확인할 수 있습니다. ViewRotation (클라이언트가 보는 위치 결정)은 ServerMove 통해 서버에 전송되며 여기서 회전으로 전환됩니다. 회전은 이후 클라이언트에 복제됩니다. 마지막으로 DumbProxy (SimulatedProxy 보다 작은 값만 유효)인 경우 역시 회전 업데이트를 받습니다. 회전 업데이트에는 위치처럼 업데이트 간 삽입이 필요하지 않습니다. 플레이어가 신속하게 이동할 때는 위치 지연을 확실히 확인할 수 있지만 회전 지연의 경우는 그렇지 않습니다. 또한 회전을 예측하거나 예상할 수 있는 실질적인 방법은 없으며 전적으로 다른 사용자의 마우스 움직임에 달려 있습니다.

if( bSimFall || ((RemoteRole == ROLE_SimulatedProxy
    && (bNetInitial || bSimulatedPawn)) || bIsMover) )
    Velocity;

여기에는 복합 복제 구문과 마지막으로 중요한 변수가 있습니다. bSimFall 이 설정된 경우 속도가 복제됩니다(인벤토리에서 무기를 던질 때 사용). PHYS_Falling 만으로는 충분하지 못합니다. Pawn으로부터 시작될 때 초기 속도를 정확하게 알아야 합니다. 또한 여기서는 최초로 복제되는 시점이 아니므로 bNetInitial 이 적용되지 않습니다. 상당 시간 무기로 존재했습니다. 업데이트하려는 플레이어로부터 던져지는 시점의 초기 속도에 대한 것입니다. 이동하면서 새로 분사된 로켓, 수류탄 또는 충격탄 등에 대해 복제 채널에서 최초로 복제되는 경우 SimulatedProxies 에서 Velocities 가 복제되는 것을 확인할 수 있습니다. SimulatedProxies 에서도 시뮬레이션된 Pawn이면 속도가 복제됩니다. 모든 다양한 Pawn을 로컬에서 예측할 수 있으려면 속도 복제가 필요하기 때문입니다. 마지막으로 무버 속도가 복제되어 클라이언트가 무버의 로컬 이동을 정확하게 예측할 수 있습니다. Unreal 1 초기에는 무버의 속도가 복제되지 않아 클라이언트가 주기적 업데이트 위치를 받았습니다. DumbProxy 였기 때문입니다. 그 결과 넷플레이에서 무버가 매우 갑작스럽게 움직이게 되어 Unreal 224+에서 수정되었습니다.

if( DrawType == DT_Mesh && ((RemoteRole <= ROLE_SimulatedProxy
    && (!bNetOwner || !bClientAnim)) || bDemoRecording) )
    AnimSequence, SimAnim, AnimMinRate, bAnimNotify;

여기에는 몇 가지 추가적인 변수가 있습니다. 모두 애니메이션과 관련한 것입니다. 이러한 변수는 현재 메쉬로 그려지고 있는 경우에만 복제됩니다. 데모를 녹화할 경우 애니메이션을 항상 전송합니다. 그렇지 않으면 다소 복잡한 조건을 확인합니다. 액터가 DumbProxy 이고 플레이어가 이 오브젝트의 소유자가 아니며 bClientAnim 이 설정되지 않은 경우 애니메이션 변수를 복제합니다. 무기의 경우 클라이언트측에서 애니메이션을 확인하므로 애니메이션 변수를 서버로부터 복제할 필요가 없습니다. 이것은 모든 TournamentWeapons에서 bClientAnim 이 true로 설정되어 애니메이션을 클라이언트측에서 다루도록 표시하기 때문입니다. 애니메이션이 클라이언트측에서 처리되지 않으면 액터가 덤프 프록시로, 서버로부터 보냅니다. Pawn, 로켓 및 기타 발사물을 포함하는 시뮬레이션 프록시는 서버로부터 애니메이션을 받지 않습니다. 대신 클라이언트가 클라이언트측 예측에 따라 애니메이션 처리합니다. Pawn의 경우 엔진에서 내부적으로 처리되므로 이에 대해 염려할 것이 없습니다(다른 튜토리얼 참조).