UDN
Search public documentation:

VariableReplicationJP
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

変数のレプリケーション

ドキュメント概要: 1 つのゲーム インスタンスから他へ、どのようにして変数がレプリケーションされるかの説明。

ドキュメントの変更ログ: 最後のアップデートは Michiel Hendriks より、Two.VariableReplication から移植。原作者は Mike Lambert (UdnStaff?)。

概観

変数は、レプリケーションステートメントを通して送ることのできる 2 つのタイプのデータのうちの 1 つ (もう 1 つは関数) です。ここでは、1 つの Unreal インスタンスから他へどのようにしてデータがレプリケーションされるかを解説していきます。細かい部分が多いので、最初は思っているよりもプロセスが複雑かも知れません。

変数は、ローカル マシンで行った変更に関して、他のマシンを常にアップデートされた状態にするためレプリケーションされます。多くの場合、これは Unreal サーバーがクライアントに変数をレプリケーションし、変更された情報を与えることを意味します。プレイヤーの速度が変われば、2 つのネットワークがアップアデートする間に、このプレイヤーの挙動を適切にシミュレートできるように、クライアントにレプリケーションされる必要があります。

このドキュメントは「Unreal のネットワーキング総括」の一部です。

変数データの信頼性

単純な答えは、変数は常に信頼性があると言えます。アクタの変数がレプリケーションされる際、そのオブジェクトの変数に関連付けられたリタイアメントが、出て行くパケット ID のコピーを取得します。パケットに否定応答がきた場合は、そのパケット ID に関連付けられていた変数がチャンネルの Dirty 配列に入れられます。次回アクタがレプリケーションされた際、そうしなければレプリケーションされないという場合、アクタはこれらすべての変数を追加します。従って、変数データはもう一方に必ず届きますが、アップデート 2 を処理する前にアップデート 1 を待たなければいけないという意味では、信頼性がありません。

関連性のある新しいアクタ (スポーン)

アクタがクライアントに対して関連性が出てくると、新しい UActorChannel が接続に作成されます。これは、クライアントで適切なクラスのアクタが defaultproperties と共にスポーンするようにします。アクタのスポーン後すぐに (関連性チェックの前に) サーバーが変数を設定し、変数がレプリケーションステートメントに合うと仮定した場合、それらはアクタのデータへの追加物としてクライアントに送られます。その時点の tick の最後までレプリケーションは終わらないので、アクタ自身での最初のレプリケーションの変数を設定する時間は十分あります。これらのアクタに設定された変数は、もう一方により受信され、そこでは PostNetBeginPlay (アクタの変数がネットワークからメモリへ読まれる前に PostBeginPlay が呼び出されるので) が呼び出されます。

最初にどの変数がレプリケーションされるかを決定するのに、サーバーが defaultproperties とは異なる変数を送ります。クライアントに対して、もしサーバーがアクタの defaultproperties に関して異なるアイデアを持っている場合、変数のみ (サーバーのバージョンと異なる場合) をレプリケーションします。(同様に、もし変数がクライアント側のコードで変更された場合、サーバーはそのことを知らないので、変更されたデータをレプリケーションしません。)

変数のレプリケーションのタイミング

レプリケーションされた変数は、各 tick の最後にのみレプリケーションされます。これは、ループ中に繰り返し変数を変更した場合、または他のコードのブロックを変更した場合、最終値のみがレプリケーションされるということです。tick 中に何度も変数を変更して、tick が完了した時に元の値に戻した場合、変数の変更は全く見られず、ネットワークのバンド幅は全く使用されません。

もちろん、これには例外があります。サーバーで新しいアクタをスポーンさせたら、変数がレプリケーションされるまで tick の最後までの時間があります。しかしながら、いかなるレプリケーションされた関数を呼び出した場合、それらのレプリケーションされた関数は即座に送られます。そして、もう一方がそれを受け取るには、それを動作させるためのアクタを持っていなければいけません。従って、もしアクタがまだレプリケーションされていない場合、最初のレプリケーションされた関数が呼び出されたときに、Unreal は強制的にアクタとその変数をレプリケーションします。そして tick の最後で、どの変数を送るかを決定するために、アクタを再チェックします。

クライアント -> サーバーレプリケーション

もう 1 つ気を付けていただきたいことは、クライアント サーバーレプリケーションは、自分の PlayerController のみに存在するということです。これは、クライアントからサーバーへ変数をレプリケーションさせたい場合は、その時点での PlayerController (またはペアレント クラス定義のうちの 1 つ) で行われなければいけないということです。クライアント -> サーバー関数は PlayerController のために実行され、すべてがそれに所有されているので、レプリケーションされた関数はこの制限を避けるのに使用することができます。多くのポーンがその場所としてクライアント上に存在し、速度がレプリケーションされる一方、所有されている現在のポーンのみが変数をサーバーにレプリケーションすることができます。1 つが他のアクタに関する情報をサーバーにレプリケーションしたとしたら、クライアント A の値がアクタ Z やクライアント B のものになるので、サーバーには問題が起こります。分別する方法がないので、サーバーはサーバーへのポーン変数レプリケーションのみを許可します。

Native レプリケーション

よくレプリケーションされるデータを持ついくつかのベース クラスは、スクリプトレプリケーションの代わりに Native (C++) レプリケーションを使用します。これらのクラスは、クラス定義内にある nativereplication キーワードによって見分けることができます。これらのクラスはスクリプト条件を無視し、代わりに C++ GetOptimizedRepList() 関数に基づいてプロパティをレプリケーションします。しかしながら、UnrealScript レプリケーションブロックは、すべてのレプリケーション可能なプロパティをリストする必要があります。というのは、コンパイラがそれらを「プロパティが正しく送られなければいけない」とマークすることができるからです。

必要な場合のみレプリケーションする

変数レプリケーションにも最適化は存在します。サーバーがその最新のバージョンと比べて、クライアントの持っているバージョンが正しくないと判断した時のみに、変数はクライアントにレプリケーションされます。サーバーがデータをレプリケーションする際、現在の値と Recent 配列に格納された最後に送られた値を比べます。もしこれが初めてのレプリケーションとなるならば、コードが、現在の値とアクタのデフォルト プロパティ内の値をチェックします。従って、クライアントに何があるかに関わらず、サーバーは変更された変数を送ります。クライアントとサーバー両方で実行されるシミュレートされた関数で値が変更された場合でも、サーバーはクライアントの値を知らないので、自身の値をレプリケーションします。この変数がすべての場合において、クライアントとサーバーの両方で必ず変更されるのならば、この値のレプリケーション条件を削除することができます。このアクタが関連性を持ったり失くしたりする場合、関連性を持ったときに上記のように、クライアントで defaultproperties に初期化されます。これが不正確でレプリケーション値を持たない場合、不正確なままになります。アクタが初めて関連性を持つ場合、何かをレプリケーションする際に bNetInitial 変数を使用して、その後、サーバーとクライアント両方で実行されるシミュレートされた関数が、そこから値を変更してくれるのを頼ることもできます。変数 bNetInitial とその仲間に関しては、次で解説していきます。

特別なレプリケーション変数

レプリケーション条件を書く際には、関数/変数がレプリケーションされるべきかどうかを評価するのに使用できるような、役に立つ変数がいくつかあります。これらの変数はアクタで定義されており、以下の通りです。

bNetInitial: このアクタがネットワークで初めてレプリケーションされる場合は TRUE です。defaultproperties から異なる変数の場合に有用ですが、アクタのライフに渡って変化しません。 bNetOwner:レプリケーションしている先のプレイヤーが、このアクタを直接所有する場合は TRUE です。 bNetRelevant:アクタは現在、関連性があります。サーバー側のみで有効です。 bDemoRecording bClientDemoRecording bRepClientDemo bClientDemoNetFunc bDemoOwner:デモ レコーディングの目的で使用されます。

変数レプリケーションの例

今度は、これらがどのように動作するのか、そしてコーディングを自分でする際の手助けとなる例を挙げるため、いくつかの重要なレプリケーションの例や変数とどのように使用されるかを説明していきます。

例はいくつか挙げて、それぞれがどのように働くかを解説します。簡単なものから始め、複雑なものへと移っていきます。以下の例は、特に記述がない限り、アクタから取ったものです。

注意: これらの例は、Unreal Tournament からのものであり、実際のコードは既に関連性がありません。しかしながら、以下の例は変数レプリケーションを理解するのに有用です。

PlayerPawn にて

if ( Role < ROLE_Authority )
    Password, bReadyToPlay;

ここでは、パスワードのかかっているサーバーに参加しようとする際に、または管理者としてそのサーバーにログインしようとする際に、パスワードがサーバーに送られる状態を示しています。サーバーは、認証するためにパスワードを必要とし、このコードはそれをサーバーに送ります。このチェックをサーバーとクライアントの両方で見てみましょう。

サーバー:サーバーでは Role == ROLE_Authority です。条件を見ると、FALSE になることが明らかです。従って、パスワードはサーバー側でレプリケーションされません。これは、サーバーが接続先にパスワードを送らないということです。 クライアント:クライアントでは RoleROLE_Authority (サーバー自身のみで Authority であることを思い出してください) ではないので、このレプリケーションはクライアント上でう TRUE とチェックされます。また、変数レプリケーションは現在の PlayerPawn のみで動作するので、ユーザーのためだけにサーバーに送られます。(他の人にパスワードを送ってしまっては、意味がないですよね?)

またこれは、ゲームを始めるのに全員がクリックしなければならないような Tournament スタイルのゲームで使用されている bReadyToPlay がどのように動作するかも示しています。変数が自身の playerpwn アクタの一部である場合のみに、クライアントは変数をサーバーにレプリケーションできるので、これは、このユーザーのためのみに送られるものであることに留意してください。

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

これは、オブジェクトのオーナーは常にクライアントにレプリケーションされることを保証します。オーナーの変数自身がレプリケーションされるのではなく (再帰レプリケーションはないので) アクタ自身がレプリケーションされるのを覚えておいてください。これは、 Owner への参照がクライアントへレプリケーションされるので、単純な比較 if (PlayerPawn? == Owner) がきちんと動作するということです。両方とも同等に処理されるので、信頼性があるかどうかということは、UT では何の差違もないことを覚えておいてください。Unreal 1 の時代には何らかの影響があった可能性はありますが、もはや開発において要因となりません。混乱しないようにしてください。

RoleRemoteRole はクライアントでリバースされるべきなので、これらをレプリケーションするのは、最初は奇妙に思えるかも知れません。しかしながら、切り替えを起こすような Native コードがあります。クライアント上では、これら 2 つの変数は確実にリバースになるので、=RemoteRole= は ROLE_Authority になります。そもそもなぜレプリケーションされるのか不思議に思われるかも知れません。クライアントはコード内でこれらの変数の使用法を直接持ちませんが、非常に大事で見過ごしてしまう場所が 1 つあります。それは、レプリケーションステートメントです。クライアントが、特定の関数呼び出し、または playerpawn の変数をサーバーにレプリケーションする必要があるかをチェックする際、クライアントは RoleRemoteRole が何であるかという正確なコピーを必要とします。それらなしでは、何をサーバーに送るかに関する必要な決定を下せなくなります。サーバーは、サーバーに送られるレプリケーションデータの認証を Role/RemoteRole でチェックするので、そのデータをきちんとレプリケーションするには、クライアントは最新のコピーを必要とするのです。

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

Inventory は Inventory for the Actor (アクタのインベントリ、通常 Pawn とのみ使用される) 全体をリストする、リンクされたリストの上の部分です。ユーザーがどのようなインベントリを持っているかを分かるために、また HUD に表示するために、Inventory をクライアントにレプリケーションします (意味をなしていますよね?)。しかしながら、単純にリンクされたリストの上部分 (そこでは各オブジェクトがリスト内の「次」を提示しています) をレプリケーションするだけでは十分ではありません。各アクタは関連性がある必要があり (現在の playerpawn に所有されているという基準を満たした状態で)、各リンクがレプリケーションされる必要があります。チェーン内の Inventory サブクラス アクタもレプリケーションされた Inventory 変数を持ちます。このようにして、クライアント側で Inventory を見ることができるのです。ユーザーは、ゲーム内の他のプレイヤー全員の完全な Inventory リストを知る必要はないので (現在の武器やシールドベルとは他の方法で転送されます)、クライアントがオーナーである場合のみ、Inventory はレプリケーションされます。

上記のレプリケーションステートメントの他の変数は bNetOwner です。これは、クライアントとサーバーの両方に Native に設定されているので、この変数はレプリケーションされる必要がありません。バンド幅に微妙に負荷をかけるという話もありますが、その影響は取るに足りないものでしょう。将来のパッチでは、おそらく削除されるでしょう。

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

これらのメッシュ固有の変数は、このアクタが現在メッシュとして表示されている場合のみにレプリケーションされます。スプライト、ブラシであったり、またはドロータイプでない場合は、これらの変数を送る理由がありません。

ここで、もう少し複雑な変数レプリケーションステートメントを見てみましょう。

if( RemoteRole == ROLE_SimulatedProxy ) Base;

ここでは、Role == ROLE_Authority に関わりのない、もう少し複雑なレプリケーションステートメントを見ていきます。現在の Base (SetBase を介して設定される) が、アクタがシミュレートされたプロキシに設定されている場合のみ、レプリケーションされるのが分かります。アクタを DumbProxy に設定すると、クライアント側では Base に変更がないのが分かります。またその代わりに、サーバーから送られるぎくしゃくした Location アップデートを得ます (これに関しては後ほど説明します)。従って、もし SetBase を使用していたら、シミュレートされたプロキシであることを確認してください。そうでない場合は、_SetBase_ 以外の何かを使用してください。または、_SetBase_ とシミュレートされたプロキシの両方が必要な場合は、Base はレプリケーションされません。それをするには、何か他のメカニズムを考え出さなければいけません。クライアントで実行されるように、おそらく Base を設定するシミュレートされた関数が良いでしょう (これに関しては後ほど説明します)。アクタに関連性がない場合、アクタにシミュレートされた関数が呼び出される際に、まだうまく動作しないことがあります。結果として、シミュレートされた関数がクライアント側で呼び出されず、最終的にアクタが関連性を持つようになると、その関数はまるでまったく呼び出されなかった (そして Base が設定されなかった) かのようになります。すると、「バグ」が生まれてしまうというわけです。シミュレートされた関数が常に呼び出されるように、関連付けされているアクタを (そしてそれにアクタに関連付けられているものを) 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= がクライアントにレプリケーションされるのが分かります。これは、インベントリから武器を放り投げる場合に使用されます。武器が放り投げられると、武器が空中を飛ぶにつれて PHYS_Falling するには、インベントリにある状態で PHYS_None から Physics の変更が必要となります。その後、地面に落ちたら PHYS_None に設定し直します。これらすべての Physics の変更は、=bSimFall= を TournamentWeapon のライフの間のキー ポイントで設定することで可能です。変更をとらえるために、インベントリから放り投げられる際には TRUE に設定されています。そして、地面に落ちるまでオンのままで、地面に落ちると再度 Physics に変更されます。最終的に地面に落ちて当たると、その Physics がリセットされ、それ以降の Physics が起きないように bSimFall は FALSE に設定されます。ステートメントの後半部分を見ると、シミュレートされたプロキシの場合のみに Physics がレプリケーションされているのが分かります。このアクタがインターネット上でレプリケーションされるのは初めてで、これはシミュレートされたポーンではありません。=ROLE_SimulatedProxy= の RemoteRole を持つ Pawn の場合は、=bSimulatedPawn= 変数は TRUE に設定されています。(誰が考えたのでしょうか?) これは、レプリケーションされる前のシミュレートされたプロキシ非ポーン アクタの Physics へのどのような変更も、クライアントにレプリケーションされるということです。=Physics= は、ポーンのために決してレプリケーションされません。それよりも、Physics のコードは、コードにハード ワイヤードされており、ポーンが地面に押される原因となります。つまり、ポーンがジャンプすると、ポーンが上方向に向かうように、サーバーは垂直の速度を設定します。次に、その Velocity はクライアントにレプリケーションされます (以下で説明されています)。そして、クライアントはポーンがプレイヤー (例:bot または playerpawn) であるかどうかをチェックし、飛べない状態 (Pawn の bCanFly 変数で設定) であることを確認し、水辺にいない (この場合、異なる重力や物理が関わってくるので) ことを確認します。従って実際、ポーンの Physics は決してクライアントにレプリケーションされませんが、そのように見えます。playerpawn を用いて代替の Physics を作成しようとしているのならば、プレイヤーが壁の上や空中にいる際に、Native コードが落ちる Physics をプレイヤーに強制しないように、=bCanFly= を TRUE に設定するようにしてください。その後、代替の方法を自分で実装しなければいけません。

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

ここでは、もう 1 つ重要な変数である Location とそのレプリケーションを見ていきます。これが持ち歩いているアイテムにレプリケーションされていないのが分かります。これは、=Inventory= の場合に有用になってきます。プレイヤーがそれを持ち歩いている場合、それは何にも使用されないので、その場所がレプリケーションされなければいけない理由はありません。プレイヤーのインベントリ全体をレプリケーションすることは、ネットワーク バンド幅にとってかなりの負担になります。ステートメントの次のセクションを見てみましょう。これが、アクタがレプリケーションされるのが初めての場合 (条件の他の部分は TRUE であると仮定)、Location はレプリケーションされます。これは、ゲームに入るとすべてのポーンが異なる場所でスポーンする、また、ロケット ランチャーからロケットがスポーンする場合はその場所が設定されていなければいけない、などのさまざまな理由から、非常に有用です。これが bSimulatedPawn の場合にも、Location は送られます。これは、ポーンのレプリケーションにおいて起きるエラーを修正する手助けをします。ポーンは方向を変えることができ、それをクライアントは常に知っているわけではない (ラグによってなどの理由で) ので、Location のリセットがそのアップデートが「修正される」唯一の方法となります。これは、自分がラグを感じている場合は、自分の Location を修正するのに用いられないことに注意してください。その場合 AutonomousProxy なので、PlayerPawn 固有の関数がそれを処理します (すなわち ClientAdjustPosition)。例えば、=PHYS_Walking= は、位置が素晴らしい精度をもって予測される PHYS_Projectile とは違います。=PHYS_Walking= は、単に「地面にくっついた状態にする」というです。つまり、クライアントの唯一のアップデート (Location なしの) は速度であり、これはプレイヤーの動作が行われる長い時間の間にエラーが起こってしまう可能性が高いわけです。この Location は、修正の要因として使用され、クライアントのビューが、プレイヤーがそこにいるであろうという場所からなるべく遠くならないようにします。また同時に、サーバー アップデートの間に予測されるように、速度のレプリケーションも続けます。=Location= をレプリケーションするもう 1 つのオプションは、=RemoteRole= が ROLE_SimulatedProxy よりも少ない Location になります。すなわち、=ROLE_DumbProxy= です。なぜなら、=ROLE_None= は最初から関係性を回避するからです。=DumbProxies= は、サーバーから定時的にアップデートを何 tick かごとに得て、ネットプレイで急変するエフェクトを作成します。以前、試しに作成したホッケーの MOD でのぎくしゃくしたパックの原因がそれでした。その当時には、それを修正するのに十分な知識がありませんでした。=Velocity= と現在の Physics の使用を通して予測されるので、=SimulatedProxy= には Location アップデートが送られません。=DumbProxies= はスムーズさを達成するためにクライアント側でシミュレートされないので、=Location= アップデートのみを得ます。これに関しては、さまざまなレプリケーションされた変数の例を提示した後に説明に入っていきます。

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

これには、他の重要な変数である Rotation が見られます。これも同様な !bCarriedItem 節が、上記と同様の理由からあります。Rotation もアクタがメッシュ、またはブラシの場合以外にレプリケーションされません。スプライトは常にプレイヤーと向かい合うので、その Rotation をレプリケーションすることは無意味になります。また、ロケットは空中を飛ぶ際に、正しい方向を向いている必要があるので、=bNetInitial= は Rotation を初回にレプリケーションさせます (他の条件が TRUE であると仮定した場合)。その速度は移動する方向を決定しますが、向いている方向も同様に重要で、これは Rotation により決定されます。シミュレートされたポーン (自分自身以外のレベルに存在するすべての playerpawn と bot) の場合、Rotation はレプリケーションされます。これは、他の人々がどちらの方向を向いているかを分かるようにするためです。=ViewRotation= (クライアントがどちらを向いているかを決定する) は ServerMove を介してサーバーに送られ、そこで Rotation に変換されます。その後、この Rotation がクライアントにレプリケーションされます。そして最終的に、=DumbProxy= (SimulatedProxy より小さく唯一有効) の場合、Rotation アップデートも取得します。Rotation アップデートは、Location のようにアップデート間の補間を必要としません。Location のラグはプレイヤーがすばやく動いている際には、非常に顕著になりますが、=Rotation= ラグに関してはあまり気付かれることはありません。また、Rotation を予測したり、予想したりする方法はありません。これは、ユーザーのマウスに完全に委ねられています。

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

これは、最後に挙げる、複雑なレプリケーションステートメントをもつ重要な変数の 1 つになります。Velocity (速度) は bSimFall が設定されているとレプリケーションされます (インベントリから武器を放り投げる際に使用されます)。=PHYS_Falling= のみでは十分ではありません。ポーンから打ち上げられたときの、最初の速度が分かっていなければいけません。また、初めてレプリケーションされるので、=bNetInitial= はここでは動作しません。これは、長い間武器として存在してきました。アップデートが欲しい部分は、プレイヤーから投げられたときの最初の速度のみになります。そのまま見ていくと、新しくスポーンしたロケット、手榴弾、投射物などにとって初めてのレプリケーションチャンネルの場合、=SimulatedProxies= の Velocities がレプリケーションされているのが分かります。シミュレートされたポーンの場合、=SimulatedProxies= もまた、その速度がレプリケーションされます。これは、ローカルで予測されるように、すべてのポーンの速度がレプリケーションされる必要があるのです。そして最後に、ムーバーの速度がレプリケーションされます。これは、クライアントが正確に、ムーバーの移動をローカルで予測するためです。初期の Unreal 1 時代には、ムーバーの速度はレプリケーションされず、=DumbProxy= だったので、クライアントは定期的にアップデート ロケーションを取得しました。結果として、ネットプレイで非常にとびとびのムーバーになり、Unreal 224+ では修正されました。

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

ここでは、いくつかの変数があり、そのすべてがアニメーションに関連しています。これらの変数は、メッシュとして描かれている場合のみにレプリケーションされます。デモをレコーディングしている場合、アニメーションは常に送られます。そうでない限り、ややこしい条件をチェックしてしまうからです。アクタが DumbProxy で、プレイヤーがこのオブジェクトのオーナーでなく、=bClientAnim= が設定されていないと、これはアニメーション変数をレプリケーションします。武器の場合は、クライアント側でそれらのアニメーションを見ることができるので、アニメーション変数はサーバーからレプリケーションされる必要はありません。これは、すべての TournamentWeapons の bClientAnim が TRUE に設定されており、アニメーションがクライアント側で処理されることを示しているからです。もしアニメーションがクライアント側で処理されなく、アクタが DumbProxy であれば、サーバーから送られます。ポーン、ロケット、その他の投射物を含むシミュレートされたプロキシは、すべてシミュレートされたプロキシなので、サーバーからアニメーションを取得しません。その代わりに、クライアントはクライアント側の予測を使用してそれらをアニメーション化します。ポーンの場合は、これはエンジンの内部で (他のチュートリアルにあります) 処理されるので、心配する必要はありません。