mcpField
U
ue-mover

ue-mover

github.com/quodsoler開発419221 回インストール4 週間前に更新
リポジトリ

ue-mover について

UE 5.8 の実験的な Mover 移動システムを扱う開発スキルです。モジュール式のアクター移動を構築する開発者向けに、セットアップ、構成、移動モード、シミュレーションの流れを説明します。

主な機能

  1. 01Mover プラグインの設定とモジュール依存関係
  2. 02コンポーネント、バックエンド、状態の構成
  3. 03カスタム移動モードと切り替え
  4. 04レイヤード移動と瞬間効果
  5. 05シミュレーションフローとネットワークバックエンド

活用シーン

  1. 01非人型 Pawn のモジュール式移動を設計
  2. 02クライミングなどの移動モードを追加
  3. 03ダッシュ、ノックバック、瞬間移動を実装
  4. 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 で推奨されている開始設定は次のとおりです。

設定値理由
PreferredTickingPolicyENetworkPredictionTickingPolicy::Fixed共有タイムライン、グループロールバック
SimulatedProxyNetworkLODENetworkLOD::Interpolated他のプレイヤーのポーンはフォワード予測の精度が低いため
bEnableFixedTickSmoothingtrue固定シミュレーションレートと可変レンダリングレート

スムージングを有効にした場合は、コンポーネントの 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)を制御します。

  1. ProduceInput — UMoverComponent::ProduceInput(const int32 DeltaTimeMS, FMoverInputCmdContext* Cmd) は、InputProducers 内のすべてのエントリに対して IMoverInputProducerInterface::ProduceInput を呼び出します。InputProducers は BeginPlay で InputProducer(デフォルトでは、オーナーがインターフェースを実装している場合はオーナー)と、それを実装するオーナーの全コンポーネントで構成されます。5.8 の .cpp は bGatherInputFromAllInputProducerComponents を読み取りません。制御インスタンスでのみ実行され、再シミュレーション中に再実行されることはありません。

  2. 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 を呼び出します。

  3. 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)なので、カスタムデータを追加するためにコンポーネントをサブクラス化する必要はありません。

型内容
FMoverInputCmdContextInputCollection — 毎フレーム、オーナーが作成します
FMoverSyncStateMovementMode、LayeredMoves、LayeredMoveInstances、MovementModifiers、SyncStateCollection
FMoverAuxStateContextAuxStateCollection — ほとんど変化しないシミュレーション入力
FMoverDefaultSyncState位置、向き、速度、AngularVelocityDegrees、MoveDirectionIntent、移動ベース
FMoverTickStartDataティック用の InputCmd、SyncState、AuxState
FMoverTickEndDataSyncState、AuxState、MovementEndState、MoveRecord
FProposedMovePreferredMode、DirectionIntent、LinearVelocity、AngularVelocityDegrees、bHasDirIntent、MixMode
FMoverTimeStepServerFrame、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::NextFrameOnlyEBlackboardPersistencePolicy::ThroughNextFrameMoveLibrary/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 エンティティとプロセッサー

コメント