ue-mover について
UE 5.8 の実験的な Mover 移動システムを扱う開発スキルです。モジュール式のアクター移動を構築する開発者向けに、セットアップ、構成、移動モード、シミュレーションの流れを説明します。
主な機能
- 01Mover プラグインの設定とモジュール依存関係
- 02コンポーネント、バックエンド、状態の構成
- 03カスタム移動モードと切り替え
- 04レイヤード移動と瞬間効果
- 05シミュレーションフローとネットワークバックエンド
活用シーン
- 01非人型 Pawn のモジュール式移動を設計
- 02クライミングなどの移動モードを追加
- 03ダッシュ、ノックバック、瞬間移動を実装
- 04Mover のシミュレーションと状態遷移を調査
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ quodsoler/ unreal-engine-skills --skill ue-moverインストール後、Claude Code で/ue-moverを入力して読み込まれたか確認します。
同梱ファイルreferences/mover-recipes.mdreferences/mover-reference.md
詳細
UE Mover
対象エンジン:UE 5.8。以下の API は 5.8 のヘッダーで検証済みです。古い形式は「非推奨 — 使用しないでください」に記載されています。
Mover は Epic のモジュール式移動システムで、Mover プラグインとして提供されています(5.8 では実験的機能のため、API とデータ形式は変更される可能性があります)。移動は、UBaseMovementMode、FLayeredMoveBase、FMovementModifierBase、FInstantMovementEffect、UBaseMovementModeTransition などのデータ駆動型オブジェクトで構成され、UMoverComponent がこれらを保持します。また、TickComponent ではなく、外部の バックエンドリエゾン によって駆動されます。Build.cs のモジュール名は "Mover"(物理駆動の移動では "ChaosMover"、ロールバックバックエンドでは "NetworkPrediction"、Mass では "MoverMassIntegration" も追加)です。公開ヘッダーは Plugins/Experimental/Mover/Source/Mover/Public 以下にあります。ログカテゴリは LogMover、コンソールコマンドのプレフィックスは Mover. と mover. です。
コンテキスト
.agents/ue-project-context.md が存在する場合は読み込んでください(モジュール名、規約、有効なプラグイン、GAS/ネットワーク設定)。ファイルがなくても作業を中止しないでください。
依頼とコードベースから対象領域を特定してください。妥当な解釈が 2 つあり、それぞれで異なるコードになる場合に限り、確認してください。
| 依頼の内容… | 参照先 |
|---|---|
| CMC と Mover の選択 | CMC と Mover |
| プラグインの有効化、Build.cs、プロジェクト設定 | セットアップ |
| コンポーネント、モードマップ、共有設定、各要素の所有関係 | アーキテクチャ |
| シミュレーションティックの処理先、サブステップ、混合 | ティックフロー |
| 歩行/登攀/滑空/ジップラインなど、新しい移動タイプ | 移動モード |
| ダッシュ、発射、ノックバック、ホーミング、ルートモーション | レイヤードムーブ |
| しゃがみ、スタンス、設定の切り替え | 移動モディファイア |
| テレポート、単発インパルス、強制モード変更 | 即時移動エフェクト |
| 自動モード切り替えルール | トランジション |
| Enhanced Input を Mover の入力コマンドに変換 | 入力の生成 |
| カスタムのフレーム単位データ、位置、速度、永続性 | 入力と同期状態 |
| バックエンド、ロールバック、再シミュレーション、レプリケーションが必要なもの | バックエンドとネットワーク |
| 速度/モードの読み取り、デリゲートのバインド、ゲームプレイタグ | 状態とイベントの読み取り |
| モーションマッチング、軌跡、AI パスフォローイング、Mass | 統合ポイント |
完全なテンプレートは mover-recipes.md にあります。デフォルトの移動セットのカタログ、データモデルの詳細、ユーティリティライブラリ、デバッグについては mover-reference.md を参照してください。
CMC と Mover
Mover は UCharacterMovementComponent の単純な置き換えではなく、UMoverComponent は ACharacter を必要としません。必須なのは USceneComponent のルートコンポーネントだけです。この表は ue-character-movement のものと同じです。
| 状況 | 使用するもの |
|---|---|
| あらゆるエンジン対応プラットフォーム向けの出荷タイトル | CMC |
既存の ACharacter 階層で、アニメーションと AI がすでに CMC に接続されている | CMC |
| 現時点で安定性が必須のネットワークゲームプレイ | CMC |
| ロールバックネットワークを利用したい、カプセル形状でも人型でもない移動体(乗り物、ドローン) | Mover |
Phys* サブクラスではなく、データ駆動型のモード/モディファイアで構成する移動 | Mover |
| Chaos による物理スレッド上でシミュレーションする移動 | Mover(ChaosMover) |
| 実験的 API の変更を受け入れられるプロトタイプ | Mover |
概念の対応関係:CMC の移動モード → UBaseMovementMode オブジェクト、ルートモーションソース → FLayeredMoveBase、LaunchCharacter/TeleportTo → FInstantMovementEffect、しゃがみ → FMovementModifierBase、Phys* に分散しているモード変更条件 → UBaseMovementModeTransition。
同じポーンに両方のコンポーネントを配置しないでください。 CMC とは異なり、Mover の状態は外部から書き換えられません。代入可能な Velocity プロパティはありません。動作を変更する場合は、モード、レイヤードムーブ、モディファイア、即時エフェクトのみを使用してください。
セットアップ
Mover を有効にします(5.8 では実験的機能)。これにより、NetworkPrediction(5.8 ではベータ版)、MotionWarping、PoseSearch、Water、ChaosVD が取り込まれます。オプションの関連プラグイン:MoverExamples(実験的機能 — サンプルマップとポーン)、ChaosMover(実験的機能 — 物理バックエンド)、MoverAnimNext(実験的機能 — UAF ブリッジ)、MoverIntegrations(実験的機能 — Mass)。
// MyGame.Build.cs
PublicDependencyModuleNames.AddRange(new string[] { "Core", "CoreUObject", "Engine", "Mover", "GameplayTags" });
// Add "EnhancedInput" when the pawn binds input actions (the pawn recipe includes EnhancedInputComponent.h).
// Add "ChaosMover" only for physics-driven Mover actors.
// Add "MoverMassIntegration" only when driving Mover pawns from Mass.
プロジェクト設定 → プラグイン → Network Prediction(UNetworkPredictionSettingsObject、config=NetworkPrediction)。プラグインの README で推奨されている開始設定は次のとおりです。
| 設定 | 値 | 理由 |
|---|---|---|
PreferredTickingPolicy | ENetworkPredictionTickingPolicy::Fixed | 共有タイムライン、グループロールバック |
SimulatedProxyNetworkLOD | ENetworkLOD::Interpolated | 他のプレイヤーのポーンはフォワード予測の精度が低いため |
bEnableFixedTickSmoothing | true | 固定シミュレーションレートと可変レンダリングレート |
スムージングを有効にした場合は、コンポーネントの SmoothingMode を EMoverSmoothingMode::VisualComponentOffset(デフォルト)に設定します。シングルプレイヤーでは、ENetworkPredictionTickingPolicy::Independent を使用するか、BackendClass を UMoverStandaloneLiaisonComponent に設定して Network Prediction を完全に無効にします。
アーキテクチャ
AActor (any APawn or AActor with a USceneComponent root)
└─ UMoverComponent (UActorComponent, IMovementInterface)
├─ BackendClass -> a UActorComponent implementing IMoverBackendLiaisonInterface
├─ MovementModes : TMap<FName, TObjectPtr<UBaseMovementMode>> (Instanced)
├─ StartingMovementMode : FName
├─ Transitions : TArray<TObjectPtr<UBaseMovementModeTransition>> (global)
├─ PersistentSyncStateDataTypes : TArray<FMoverDataPersistence>
├─ InputProducer / InputProducers : objects implementing IMoverInputProducerInterface
├─ MovementMixer : UMovementMixer
└─ SharedSettings : auto-built from each mode's SharedSettingsClasses
UCharacterMoverComponent は UMoverComponent のサブクラスで、キャラクター向けの動作を追加します:Jump()、CanActorJump()、Crouch()、UnCrouch()、CanCrouch()、GetCrouchIntent()、IsCrouching()、IsFlying()、IsFalling()、IsAirborne()、IsOnGround()、IsSwimming()、IsSlopeSliding()、および OnStanceChanged デリゲートです。人型のポーンにはこれを使用します。
モードがゲームプレイ状態を所有することはありません。モードは共有される Instanced な UObject で、調整値は UPROPERTY フィールドと共有設定オブジェクト(IMovementSettingsInterface、例:UCommonLegacyMovementSettings)に格納されます。フレームごとに変化するものはすべて FMoverSyncState に格納します。
// Inside UMyClimbingMode::OnRegistered, after calling Super. Full class in mover-recipes.md.
const UCommonLegacyMovementSettings* Settings = GetMoverComponent()->FindSharedSettings<UCommonLegacyMovementSettings>();
Tick の流れ
バックエンドのリエゾンが、次の 3 つのフェーズ(EMoverTickPhase::ProduceInput、SimulateMovement、ApplyState)を制御します。
-
ProduceInput —
UMoverComponent::ProduceInput(const int32 DeltaTimeMS, FMoverInputCmdContext* Cmd)は、InputProducers内のすべてのエントリに対してIMoverInputProducerInterface::ProduceInputを呼び出します。InputProducersはBeginPlayでInputProducer(デフォルトでは、オーナーがインターフェースを実装している場合はオーナー)と、それを実装するオーナーの全コンポーネントで構成されます。5.8 の.cppはbGatherInputFromAllInputProducerComponentsを読み取りません。制御インスタンスでのみ実行され、再シミュレーション中に再実行されることはありません。 -
SimulateMovement —
OnPreSimulate(TimeStep, StartingData)がOnPreSimulationTickを呼び出した後、次のサブステップループでシミュレーションが実行されます。- キューに入れられた即時エフェクトを適用します。
- アクティブな各
FLayeredMoveBase::GenerateMoveがFProposedMoveを生成します。 - レイヤー化された移動が移動を完全にオーバーライドしない限り(
mover.perf.SkipGenerateMoveIfOverridden)、アクティブなモードのGenerateMoveが実行されます。 UMovementMixer::MixProposedMovesがEMoveMixModeに従ってそれらを組み合わせます。ProcessGeneratedMovement(BindProcessGeneratedMovement経由でバインド)が最後の決定権を持ちます。- モード所有の
Transitions、続いてグローバルなTransitionsが評価されます。最初に成立したものがTriggerを呼び出し、FMovementModeTickEndState::NextModeNameを設定します。成立するものがなければ、UBaseMovementMode::SimulationTickが移動を実行します。 FMovementModeTickEndState::RemainingMsが時間を返し、同じフレーム内で別のサブステップが実行されます。
その後、
OnPostSimulate(TimeStep, StartingData, EndingData)がOnPostMovementとOnPostSimulationTickを呼び出します。 -
ApplyState — リエゾンが
FinalizeFrame(SyncState, AuxState)(またはFinalizeUnchangedFrame())を呼び出して、アクターを移動させ、OnPostFinalizeを呼び出します。
移動モード
UBaseMovementMode は UCLASS(MinimalAPI, Abstract, Blueprintable, BlueprintType, EditInlineNew, DefaultToInstanced) です。以下をそのままオーバーライドします。
virtual void OnRegistered(const FName ModeName, const FMoverSimContext& SimContext) override;
virtual void OnUnregistered(const FMoverSimContext& SimContext) override;
virtual void Activate(const FMoverEventContext& Context, FName PrevModeName, const FMoverSimContext& SimContext, const FMoverTickStartData& StartState, FMoverSyncState* OutSyncState, FMoverAuxStateContext* OutAuxState) override;
virtual void Deactivate(const FMoverEventContext& Context, FName NextModeName, const FMoverSimContext& SimContext) override;
virtual void GenerateMove_Implementation(const FMoverSimContext& SimContext, const FMoverTickStartData& StartState, const FMoverTimeStep& TimeStep, FProposedMove& OutProposedMove) const override;
virtual void SimulationTick_Implementation(const FSimulationTickParams& Params, FMoverTickEndData& OutputState) override;
GenerateMove は const で、求める動作を決定します。SimulationTick はそれを実行し(スイープ、床のチェック)、終了時の FMoverDefaultSyncState を書き込みます。OnRegistered、OnUnregistered、Activate、Deactivate では Super:: を呼び出してください。基本実装は、所有するトランジションを管理し、Blueprint イベントを発火させます。GenerateMove/SimulationTick がシーンコンポーネントやその他のゲームスレッド状態に触れない場合に限り、bSupportsAsync = true を設定してください。
デフォルトのセット:UWalkingMode、UFallingMode、UFlyingMode、USwimmingMode、UNavWalkingMode、さらに非同期版の UAsyncWalkingMode、UAsyncFallingMode、UAsyncFlyingMode、UAsyncNavWalkingMode と、簡易版の USimpleWalkingMode、USimpleFlyingMode、USimpleSpringWalkingMode、USmoothWalkingMode です。デフォルトのモード名は DefaultModeNames::Walking、::Falling、::Flying、::Swimming です。全カタログは mover-reference.md を参照してください。
実行時に登録して切り替えます。
static void MyEnableClimbing(UMoverComponent& MoverComp)
{
MoverComp.AddMovementModeFromClass(TEXT("Climbing"), UMyClimbingMode::StaticClass());
MoverComp.QueueNextMode(TEXT("Climbing"), /*bShouldReenter=*/false); // takes effect next sim frame
}
static void MyDisableClimbing(UMoverComponent& MoverComp)
{
MoverComp.RemoveMovementMode(TEXT("Climbing"));
}
レイヤードムーブ
FLayeredMoveBase は一時的な加算または上書き移動です。移動を提案するだけで、実行するのはアクティブなモードです。共通フィールド:MixMode(EMoveMixMode)、Priority、DurationMs(>0 は時間指定、0 は 1 ティック、<0 は手動)、StartSimTimeMs、FinishVelocitySettings(FLayeredMoveFinishVelocitySettings)です。以下をそのままオーバーライドします。
virtual bool GenerateMove(const FMoverTickStartData& StartState, const FMoverTimeStep& TimeStep, const UMoverComponent* MoverComp, UMoverBlackboard* SimBlackboard, FProposedMove& OutProposedMove) override;
virtual bool IsFinished(double CurrentSimTimeMs) const override;
virtual FLayeredMoveBase* Clone() const override;
virtual void NetSerialize(FArchive& Ar) override;
virtual UScriptStruct* GetScriptStruct() const override;
virtual FString ToSimpleString() const override;
virtual void AddReferencedObjects(class FReferenceCollector& Collector) override;
NetSerialize は最初に Super::NetSerialize(Ar) を呼び出してから、独自のフィールドをシリアライズする必要があります。シリアライズから漏れたものは補正後に維持されません。キューに追加すると、後のシミュレーションフレームでフラッシュされるまで TSharedPtr がそのまま保持されます(同期状態のコピー時にのみ複製されるため、追加する前に完全に設定してください)。
static void MyQueueDash(UMoverComponent& MoverComp, const FVector& DashDirection)
{
TSharedPtr<FLayeredMove_LinearVelocity> Dash = MakeShared<FLayeredMove_LinearVelocity>();
Dash->Velocity = DashDirection.GetSafeNormal() * 2000.f;
Dash->DurationMs = 200.f;
Dash->MixMode = EMoveMixMode::OverrideVelocity;
MoverComp.QueueLayeredMove(Dash);
}
組み込みのムーブ:FLayeredMove_LinearVelocity、FLayeredMove_JumpImpulseOverDuration、FLayeredMove_JumpTo、FLayeredMove_MoveTo、FLayeredMove_MoveToDynamic、FLayeredMove_RadialImpulse、FLayeredMove_MultiJump、FLayeredMove_Launch、FLayeredMove_AnimRootMotion、FLayeredMove_AnimRootMotion_SimDriven です。
2 つ目のインスタンス化形式が開発中です。ULayeredMoveLogic にステートレスなロジックを、FLayeredMoveInstancedData に複製されるデータを格納し、FLayeredMoveActivationParams で有効化します。RegisterMove<T>()/K2_RegisterMove でロジッククラスを登録し、QueueLayeredMoveActivation(UMyMoveLogic::StaticClass()) または QueueLayeredMoveActivationWithContext(Params) を呼び出します。インスタンス化パスが整うまでは、新しい C++ の実装には FLayeredMoveBase を優先してください。どちらも FMoverSyncState 内で併存します。
移動モディファイア
FMovementModifierBase は移動を提案せずに、シミュレーションの動作を変更します。姿勢、設定の切り替え、モードの再マッピングなどが該当します。以下をオーバーライドします。
virtual void OnStart(UMoverComponent* MoverComp, const FMoverTimeStep& TimeStep, const FMoverSyncState& SyncState, const FMoverAuxStateContext& AuxState) override;
virtual void OnEnd(UMoverComponent* MoverComp, const FMoverTimeStep& TimeStep, const FMoverSyncState& SyncState, const FMoverAuxStateContext& AuxState) override;
virtual void OnPreMovement(UMoverComponent* MoverComp, const FMoverTimeStep& TimeStep) override;
virtual void OnPostMovement(UMoverComponent* MoverComp, const FMoverTimeStep& TimeStep, const FMoverSyncState& SyncState, const FMoverAuxStateContext& AuxState) override;
virtual FMovementModifierBase* Clone() const override;
virtual void NetSerialize(FArchive& Ar) override;
virtual UScriptStruct* GetScriptStruct() const override;
virtual bool Matches(const FMovementModifierBase* Other) const override;
static FMovementModifierHandle MyQueueStance(UMoverComponent& MoverComp)
{
return MoverComp.QueueMovementModifier(MakeShared<FStanceModifier>());
}
static void MyCancelStance(UMoverComponent& MoverComp, const FMovementModifierHandle& Handle)
{
if (MoverComp.IsModifierActiveOrQueued(Handle))
{
MoverComp.CancelModifierFromHandle(Handle);
}
}
Mover では、同じ型のモディファイアは同時に 1 つだけと想定しています。デフォルトの Matches は型のみを比較します。複数を許可する場合は、NetSerialize でも書き込むフィールドを使って Matches をオーバーライドしてください。EStanceMode と UStanceSettings を使用する FStanceModifier は、UCharacterMoverComponent::Crouch() で駆動される、組み込みのしゃがみ実装です。
即時移動エフェクト
FInstantMovementEffect は、時間を消費せず、サブステップの開始時または終了時に 1 回適用されます。virtual bool ApplyMovementEffect(FApplyMovementEffectParams& ApplyEffectParams, FMoverSyncState& OutputState) override; をオーバーライドし、何かを変更した場合は true を返します。組み込み:FTeleportEffect、FAsyncTeleportEffect、FJumpImpulseEffect、FApplyVelocityEffect です。
static void MyQueueTeleport(UMoverComponent& MoverComp, const FVector& Destination)
{
TSharedPtr<FTeleportEffect> Teleport = MakeShared<FTeleportEffect>();
Teleport->TargetLocation = Destination;
MoverComp.QueueInstantMovementEffect(Teleport);
}
ScheduleInstantMovementEffect/ScheduleLayeredMove は、すべてのエンドポイントで同じフレームに実行されるよう、実行を遅延させます。これらが意味を持つのは、ChaosMover のネットワーク物理パスのみです。通常は Queue* 形式を使用してください。
トランジション
UBaseMovementModeTransition は状態に基づいてモード変更を判断します。virtual FTransitionEvalResult Evaluate_Implementation(const FSimulationTickParams& Params) const override;(FTransitionEvalResult(NextModeName) または FTransitionEvalResult::NoTransition を返します)と、副作用を実行する virtual void Trigger_Implementation(const FSimulationTickParams& Params) override; をオーバーライドしてください。モードごとのチェックにはモードの Transitions 配列に、グローバルなチェックにはコンポーネントの配列に追加します。bAllowModeReentry と bFirstSubStepOnly は、再入とサブステップの適用範囲を制御します。トランジションは必須ではありません。QueueNextMode、FProposedMove::PreferredMode、FMovementModeTickEndState::NextModeName でもモードを変更できます。
入力の生成
Mover は Enhanced Input を読み取りません。ゲームスレッドで入力をキャッシュし、ProduceInput 内で変換してください。
// AMyMoverPawn : public APawn, public IMoverInputProducerInterface.
// CachedMoveInputIntent, bJumpHeld and bJumpPressedThisFrame are members filled by Enhanced Input handlers.
void AMyMoverPawn::ProduceInput_Implementation(int32 SimTimeMs, FMoverInputCmdContext& InputCmdResult)
{
FCharacterDefaultInputs& CharacterInputs = InputCmdResult.InputCollection.FindOrAddMutableDataByType<FCharacterDefaultInputs>();
CharacterInputs.ControlRotation = GetControlRotation();
CharacterInputs.SetMoveInput(EMoveInputType::DirectionalIntent, CharacterInputs.ControlRotation.RotateVector(CachedMoveInputIntent));
CharacterInputs.OrientationIntent = CharacterInputs.GetMoveInput().GetSafeNormal();
CharacterInputs.bIsJumpPressed = bJumpHeld;
CharacterInputs.bIsJumpJustPressed = bJumpPressedThisFrame;
bJumpPressedThisFrame = false;
}
固定ティックでは、複数のレンダーフレームから 1 つの入力コマンドが生成されることもあれば、1 つのフレームから複数のティックが生成されることもあるため、サンプリングするのではなく蓄積してください。FCharacterDefaultInputs には SuggestedMovementMode、MovementBase/MovementBaseBoneName/bUsingMovementBase も含まれます。FMoverAIInputs には AI 用の RVOVelocityDelta が含まれます。ポーン全体については、mover-recipes.md を参照してください。
入力と同期状態
入力コマンドと同期状態はどちらも、FMoverDataStructBase 派生構造体を格納する動的コレクション(FMoverDataCollection)なので、カスタムデータを追加するためにコンポーネントをサブクラス化する必要はありません。
| 型 | 内容 |
|---|---|
FMoverInputCmdContext | InputCollection — 毎フレーム、オーナーが作成します |
FMoverSyncState | MovementMode、LayeredMoves、LayeredMoveInstances、MovementModifiers、SyncStateCollection |
FMoverAuxStateContext | AuxStateCollection — ほとんど変化しないシミュレーション入力 |
FMoverDefaultSyncState | 位置、向き、速度、AngularVelocityDegrees、MoveDirectionIntent、移動ベース |
FMoverTickStartData | ティック用の InputCmd、SyncState、AuxState |
FMoverTickEndData | SyncState、AuxState、MovementEndState、MoveRecord |
FProposedMove | PreferredMode、DirectionIntent、LinearVelocity、AngularVelocityDegrees、bHasDirIntent、MixMode |
FMoverTimeStep | ServerFrame、BaseSimTimeMs、StepMs、bIsResimulating、bIsFirstResimFrame |
すべての Mover アクターで FMoverDefaultSyncState が必要です。ワールド/ベース空間のアクセサー(GetLocation_WorldSpace()、GetVelocity_WorldSpace()、GetOrientation_WorldSpace()、GetAngularVelocityDegrees_WorldSpace()、GetTransform_WorldSpace())を使って読み取り、SetTransforms_WorldSpace(Location, Orient, Velocity, AngularVelocityDegrees, Base, BaseBone) で書き込んでください。
void UMyClimbingMode::SimulationTick_Implementation(const FSimulationTickParams& Params, FMoverTickEndData& OutputState)
{
const FMoverDefaultSyncState* StartingSyncState = Params.StartState.SyncState.SyncStateCollection.FindDataByType<FMoverDefaultSyncState>();
check(StartingSyncState);
FMyClimbState& OutClimb = OutputState.SyncState.SyncStateCollection.FindOrAddMutableDataByType<FMyClimbState>();
OutClimb.SurfaceNormal = StartingSyncState->GetOrientation_WorldSpace().Vector();
}
カスタム状態構造体では、Clone、NetSerialize、GetScriptStruct、ToString、ShouldReconcile、Interpolate をオーバーライドする必要があります。フレームをまたいで保持する必要があるデータは、FMoverDataPersistence として PersistentSyncStateDataTypes に追加してください。
UMoverBlackboard(GetSimBlackboard()/GetSimBlackboard_Mutable())は、ネットワーク同期される状態の外部に派生値をキャッシュします。たとえば CommonBlackboard::LastFloorResult、LastWaterResult、LastFoundDynamicMovementBase、TimeSinceSupported です。これはロールバック対応のブラックボード(GetRollbackBlackboardExternal())に置き換えられつつあるため、検索結果が存在しない可能性を常に考慮し、代替値を用意してください。TryGet<T> と Set<T> は型に厳密です。同じ型で保存と読み取りを行ってください(double として書き込んだ値は double として読み取る必要があります)。
バックエンドとネットワーク
IMoverBackendLiaisonInterface(Backends/MoverBackendLiaison.h)が契約です。GetCurrentSimTimeMs()、GetCurrentSimFrame()、IsAsync()、IsFixedDt()、ShouldResim()、保留中およびプレゼンテーション用の同期状態アクセサー、OnSimulationRollback(NewSyncState, NewBaseTimeStep, PreRollbackTimeStep) が含まれます。実装するコンポーネントクラスを UMoverComponent::BackendClass に設定します。
| バックエンド | 用途 | 備考 |
|---|---|---|
UMoverNetworkPredictionLiaisonComponent | ネットワークプレイまたはスタンドアロンプレイ | Network Prediction(5.8 ではベータ版)。固定または独立したティック、ロールバックと再シミュレーションに対応 |
UMoverStandaloneLiaisonComponent | スタンドアロン、物理駆動の移動なし | Network Prediction のオーバーヘッドなし |
UChaosMoverBackendComponent | 物理駆動のポーン | ChaosMover(5.8 では試験的)。非同期、固定 dt、Chaos Networked Physics に対応 |
ネットワークモデル:すべてのエンドポイントが共有タイムラインをシミュレーションし、クライアントはわずかに先行して動作します。クライアントは特定のシミュレーションフレームの入力コマンドのみを送信し、サーバーはそれらをバッファーに格納してシミュレーションを実行し、状態をブロードキャストします。クライアントは状態を比較し、ロールバックして再シミュレーションするかどうかを判断します。FMoverSyncState::ShouldReconcile は MovementMode、SyncStateCollection、MovementModifiers を比較します。
コードへの影響:
- 次のフレームでシミュレーションが読み取るものはすべて、同期状態(または補助状態)に含め、
NetSerializeを実装する必要があります。モードオブジェクトにキャッシュされた状態、NetSerializeでフィールドを省略するレイヤードムーブ、ブラックボードのデータは、ロールバック時に復元されません。 GenerateMove/SimulationTick/ApplyMovementEffectは再シミュレーション中に再実行されます。決定性を保ってください。シード付き状態なしで乱数を使わず、スポーンせず、単発のオーディオや VFX を再生しないでください。コスメティック処理を制御するには、FMoverTimeStep::bIsResimulatingとFMoverEventContext::bIsCausedByRollbackを使います。ProduceInputは再シミュレーション時には再実行されず、操作中のインスタンスでのみ実行されます。エイムアシストとロックオンはここに配置し、移動ロジックは配置しないでください。- シミュレートプロキシ:
ENetworkLOD::Interpolatedを優先してください。他プレイヤーのフォワード予測は、方向転換やジャンプ時に予測を誤ります。 - Mover は GAS と移動の同期を解決しません。GAS は引き続き独立してレプリケートされます。
RPC のルール、ネットロール、DOREPLIFETIME、プッシュモデルについては、ue-networking-replication を参照してください。
状態とイベントの確認
static void MyReadMoverState(const UMoverComponent& MoverComp)
{
const FMoverSyncState& SyncState = MoverComp.GetSyncState();
const FName ModeName = MoverComp.GetMovementModeName();
const FVector Velocity = MoverComp.GetVelocity();
const FVector Intent = MoverComp.GetMovementIntent();
const FRotator TargetOrientation = MoverComp.GetTargetOrientation();
const bool bGrounded = MoverComp.HasGameplayTag(Mover_IsOnGround, /*bExactMatch=*/false);
FHitResult FloorHit;
const bool bHasFloor = MoverComp.TryGetFloorCheckHitResult(FloorHit);
UE_LOG(LogMyGame, Verbose, TEXT("%s: mode %s, speed %.1f, grounded %d, floor %d, intent %s, facing %s"),
*SyncState.MovementMode.ToString(), *ModeName.ToString(), Velocity.Size(),
bGrounded ? 1 : 0, bHasFloor ? 1 : 0, *Intent.ToString(), *TargetOrientation.ToString());
}
UMoverComponent のマルチキャストデリゲート:OnPreSimulationTick(FMover_OnPreSimTick)、OnPostSimulationTick(FMover_OnPostSimTick)、OnPostMovement(FMover_OnPostMovement)、OnPostSimulationRollback(FMover_OnPostSimRollback)、OnMovementModeChanged(FMover_OnMovementModeChanged)、OnTeleportSucceeded、OnTeleportFailed、OnBasedMovementApplied、OnGameplayTagAdded、OnGameplayTagRemoved、OnMovementTransitionTriggered、OnPostFinalize、および C++ 専用の OnPostSimEventReceived。UCharacterMoverComponent には OnStanceChanged が追加されています。
MoverTypes.h で宣言されているネイティブタグ:Mover_IsOnGround、Mover_IsInAir、Mover_IsFalling、Mover_IsFlying、Mover_IsSwimming、Mover_IsCrouching、Mover_IsNavWalking、Mover_AnimRootMotion、Mover_SkipAnimRootMotion、Mover_SkipVerticalAnimRootMotion、Mover_DisableLanding。独自のタグは AddGameplayTag/RemoveGameplayTag で追加・削除し、HasGameplayTag または GetGameplayTags() で確認します。
統合ポイント
- アニメーション —
GetPredictedTrajectory(FMoverPredictTrajectoryParams)はモーションマッチング用にTArray<FTrajectorySampleInfo>を返します。FLayeredMove_AnimRootMotionとFLayeredMove_AnimRootMotion_SimDrivenがルートモーションを駆動します。ConvertLocalRootMotionToWorldとProcessLocalRootMotionDelegate/ProcessWorldRootMotionDelegateを使うと、Motion Warping をフックできます。MoverAnimNext(5.8 では Experimental)は UAF とのブリッジです。モンタージュとアニメーショングラフについてはue-animation-systemを参照してください。 - AI — Mover コンポーネントとともに
UNavMoverComponentを追加します。このコンポーネントはINavMovementInterfaceとIRVOAvoidanceInterfaceを実装しており、ConsumeNavMovementData(OutIntent, OutVelocity)がProduceInputにデータを渡します。UNavWalkingMode/UAsyncNavWalkingModeはナビメッシュ上に投影します。ナビメッシュの生成とクエリについてはue-ai-navigationを参照してください。 - Mass —
MoverIntegrations(5.8 では Experimental)は、UMoverMassAgentTrait、UMoverMassAgentOrientationSyncTrait、および(IMoverInputProducerInterfaceコンポーネントである)UMassMoverInputComponentを提供します。エンティティ/プロセッサーの設計についてはue-mass-entityを参照してください。 - 物理 — キネマティックモードでは
UMovementUtils::TrySafeMoveUpdatedComponentとTryMoveToSlideAlongSurfaceを使ってスイープします。衝突はHandleImpact(FMoverOnImpactParams&)を介して処理されます。一方、ChaosMover は物理スレッド上で Chaos のキャラクター接地拘束を駆動します。チャンネル、スイープ、応答についてはue-physics-collisionを参照してください。
非推奨 — 使用しないでください
| 使用しない | 5.8 で使用 | ソース |
|---|---|---|
UBaseMovementMode::OnActivate() | Activate() | MovementMode.h の UE_DEPRECATED(5.6) |
UBaseMovementMode::OnDeactivate() | Deactivate() | MovementMode.h の UE_DEPRECATED(5.6) |
UBaseMovementMode::OnGenerateMove() | GenerateMove_Implementation() | MovementMode.h の UE_DEPRECATED(5.6) |
UBaseMovementMode::OnSimulationTick() | SimulationTick_Implementation() | MovementMode.h の UE_DEPRECATED(5.6) |
UBaseMovementModeTransition::OnEvaluate() | Evaluate_Implementation() | MovementModeTransition.h の UE_DEPRECATED(5.6) |
UBaseMovementModeTransition::OnTrigger() | Trigger_Implementation() | MovementModeTransition.h の UE_DEPRECATED(5.6) |
UMoverComponent::GetFutureTrajectory() | GetPredictedTrajectory() | MoverComponent.h の UE_DEPRECATED(5.5) |
UMoverComponent::HasValidCachedState() | なし — 状態は現在、常に有効です | MoverComponent.h の UE_DEPRECATED(5.6) |
UMoverComponent::HasValidCachedInputCmd() | なし — 入力コマンドは現在、常に有効です | MoverComponent.h の UE_DEPRECATED(5.6) |
SetTransforms_WorldSpace(Loc, Orient, Velocity, Base, BaseBone) | FVector WorldAngularVelocityDegrees を受け取るオーバーロード | MoverDataModelTypes.h の UE_DEPRECATED(5.7) |
UMovementUtils::ComputeAngularVelocity(FRotator, ...) | ComputeAngularVelocityDegrees() | MoveLibrary/MovementUtils.h の UE_DEPRECATED(5.7) |
UMovementUtils::ApplyAngularVelocity(FRotator, FRotator, float) | ApplyAngularVelocityToRotator() / ApplyAngularVelocityToQuat() | MoveLibrary/MovementUtils.h の UE_DEPRECATED(5.7) |
UMovementUtils::IsAngularVelocityZero(FRotator) | 角速度は FVector です — 直接テストしてください | MoveLibrary/MovementUtils.h の UE_DEPRECATED(5.7) |
UFloorQueryUtils::FindFloor / ComputeFloorDist(個別の float パラメーターを使用) | FFloorCheckSettings を受け取るオーバーロード | MoveLibrary/FloorQueryUtils.h の UE_DEPRECATED(5.8) |
UAirMovementUtils::IsValidLandingSpot / TryMoveToFallAlongSurface / TestFallingMoveAlongHitSurface(個別の float パラメーターを使用) | FFloorCheckSettings のオーバーロード | MoveLibrary/AirMovementUtils.h の UE_DEPRECATED(5.8) |
UGroundMovementUtils::TryMoveToStepUp / TestMoveToStepOver(個別の float パラメーターを使用) | FFloorCheckSettings のオーバーロード | MoveLibrary/GroundMovementUtils.h の UE_DEPRECATED(5.8) |
EBlackboardPersistencePolicy::NextFrameOnly | EBlackboardPersistencePolicy::ThroughNextFrame | MoveLibrary/RollbackBlackboard.h の UE_DEPRECATED(5.8) |
よくある間違い
モードオブジェクトにフレームごとの状態を保存する: モードは共有されるインスタンス化済みの UObject であり、フレームごとの保存領域ではありません。SimulationTick で書き込まれたメンバーはロールバックされないため、レイテンシーがあると気付かないうちに同期がずれていきます。FMoverSyncState 内の FMoverDataStructBase に保存し、その型を PersistentSyncStateDataTypes に追加してください。
バックエンドを設定し忘れる: UMoverComponent は TickComponent による更新を行いません。IMoverBackendLiaisonInterface を実装するコンポーネントを BackendClass に設定していない場合(また、Network Prediction liaison では NetworkPrediction プラグインを有効にしていない場合)、アクターはまったく移動しません。
1つのポーンで CMC と Mover を併用する: 同じルートを2つのコンポーネントが取り合うと、ジッターや補正が発生します。どちらか一方を選んでください。UMoverComponent に ACharacter は必要ありません。
CMC の API が使えると思い込む:
// WRONG — no such members on UMoverComponent
MoverComp.Velocity = FVector(0.0, 0.0, 600.0);
MoverComp.SetMovementMode(MOVE_Flying);
// RIGHT
static void MyStartFlyingJump(UMoverComponent& MoverComp)
{
TSharedPtr<FLayeredMove_JumpImpulseOverDuration> Jump = MakeShared<FLayeredMove_JumpImpulseOverDuration>();
Jump->UpwardsSpeed = 600.f;
MoverComp.QueueLayeredMove(Jump);
MoverComp.QueueNextMode(DefaultModeNames::Flying);
}
NetSerialize からフィールドを省略する: 一部のフィールドしかシリアライズしないレイヤードムーブや状態構造体は、補正後に異なる値で再生され、レイテンシー時にだけ発生するラバーバンドを引き起こします。ムーブが読み取るものをすべてシリアライズし、最初に Super::NetSerialize(Ar) を呼び出してください。
キューに入れたムーブを後から変更する: QueueLayeredMove、QueueMovementModifier、QueueInstantMovementEffect は TSharedPtr をキューに保持します。そのキューはシミュレーション(別スレッドの場合もあります)が後からフラッシュし、その後に同期状態がクローンします。後から加えた変更はシミュレーションと競合し、反映されることもされないこともあります。キューに入れた後は、構造体に触れないでください。
モード変更が即座に反映されると思い込む: QueueNextMode が反映されるのは、呼び出しフレームではなく次のシミュレーションフレームです。GetMovementModeName() ではなく、GetNextMovementModeName() で確認してください。
シミュレーション中にエフェクトを生成または再生する: モードとレイヤードムーブのコードは、再シミュレーション時に再実行されます。コスメティック処理は !TimeStep.bIsResimulating を条件に実行するか、OnPostFinalize/OnMovementModeChanged から実行してください。
同じ型のモディファイアを2つ使う: Mover は、デフォルトの Matches が構造体の型を比較するため、型ごとに1つだけ保持します。複数必要な場合は、NetSerialize でも書き込むフィールドを使って Matches をオーバーライドしてください。
ブラックボードを信頼しすぎる: UMoverBlackboard のエントリーは、ロールバック時とモード変更時に無効化されます。TryGet が false を返す場合に必ず対処し、保存時とは異なる型として値を読み取らないでください。
関連スキル
ue-character-movement—UCharacterMovementComponent、ACharacter、FSavedMove_Character。システム Mover が置き換えることを想定しているものですue-networking-replication— ネットワークロール、RPC、DOREPLIFETIME、プッシュモデル、レレバンシー。Mover バックエンドが利用するレプリケーション層ですue-animation-system— Mover の軌道を利用するモンタージュ、アニメーショングラフ、ルートモーション、モーションマッチングue-ai-navigation—UNavMoverComponentとUNavWalkingModeを支えるナビメッシュ生成、パスフォロー、回避ue-physics-collision— Mover のスイープと ChaosMover バックエンドで使われるコリジョンチャンネル、スイープ、Chaos ボディue-gameplay-framework— Mover アクターに関連するポーン、コントローラー、所持の設定ue-input-system—ProduceInputに渡す Enhanced Input のアクションとマッピングue-mass-entity—MoverIntegrationsのトレイトを支える Mass エンティティとプロセッサー