物理演算のレプリケーション
物理オブジェクトは、力・衝突・重力の影響下で継続して動き続けるため、複製するのが困難です。リモートオブジェクトを受信した位置に単純にスナップさせても、特に一般的なネットワーク更新レート(10~30回毎秒)では、同期ズレが目立ってしまいます。
Fusionは速度ベースの予測によってこの問題を解決します。リモートクライアントは、最新の既知の速度を使用して物体の位置を予測し、次のサーバー更新が到着した際に、spring-damper系によってズレをスムーズに補正します。これによって、ある程度のパケットロスや遅延が発生している状況でも、リモートクライアント上でスムーズな動きが実現できます。
概要
RigidBody2D/3Dをルートに持つFusionSharedReplicatorのroot_replication_modeをAutoに設定します。FusionSharedReplicatorは物理オブジェクトを自動的に検知し、速度ベースの予測スムージングを使用します。位置・回転・速度・角速度は自動的に同期されるため、手動でプロパティを設定する必要はありません。
Forecastの動作について
各ネットワーク更新において、以下の式から位置が予測されます:
predicted_pos = received_pos + velocity × time + 0.5 × gravity × time²
spring-damper系によってローカル状態から予測位置に向かってスムーズに補正することで、高速で動作するオブジェクトの知覚的な遅延を軽減します。
主要プロパティ
FusionSharedReplicatorのプロパティによって、リモートクライアント上で物理補正をどのように適用するかを制御できます。
root_teleport_threshold(float,100.0) - 位置ズレがこの値を超えると、即座にスナップが行われますroot_rotation_teleport_threshold(float,15.0) - 回転ズレがこの値を超えると、即座にスナップが行われます(度単位、0で無効)root_spring_stiffness(float,50.0) - 値が大きいほど補正挙動が硬くなるroot_spring_damping(float,10.0) - 値が大きいほど跳ね返りが少なくなるroot_max_forecast_time(float,0.25) - 予測する最大秒数root_correction_mode- VELOCITY(軽いオブジェクト用), FORCE(重いオブジェクト用、デフォルト), BLEND(非物理的スナップ)root_blend_factor(float,0.1) - BLENDモードの重み付けroot_forecast_gravity(bool,true) - 予測に重力を適用するかsync_sleeping(bool,true) - 物理のスリープ状態を複製するか
調整のコツ
以下のガイドラインを参考にして、物理演算のレプリケーションでよくみられる不具合を診断・修正してください。
- 頻繁なテレポート:
root_teleport_thresholdを増やす - オブジェクトの動作が鈍い:
root_spring_stiffnessを増やし、root_spring_dampingを減らす - 振動/跳ね返り:
root_spring_dampingを増やす(damping >= 2 × sqrt(stiffness)になるように調整する) - 予測のオーバーシュート:
root_max_forecast_timeを減らすか、重力のないオブジェクトはroot_forecast_gravityを無効にする
実装例(2D)
Rigidbody2Dに権限を持つクライアントのみが入力を適用し、リモートクライアントはFusionSharedReplicatorによって物理状態を自動的に受信します。
GDScript
extends RigidBody2D
const MOVE_FORCE := 600.0
@onready var sync: FusionSharedReplicator = $FusionSharedReplicator
func _physics_process(delta):
if sync.has_authority():
var dir = Input.get_axis("ui_left", "ui_right")
if dir != 0:
apply_central_force(Vector2(dir * MOVE_FORCE, 0))
if Input.is_action_just_pressed("ui_accept"):
apply_central_impulse(Vector2(0, -500))
シーン構造:

テレポート
新しい位置に(スムージングを省略して)即座にスナップさせます:
GDScript
sync.teleport_2d(Vector2(400, 300), 0.0) # 2D (position, rotation)
sync.teleport_3d(Vector3(10, 0, 5), Vector3.ZERO) # 3D (position, rotation)
sync.teleport_2d(Vector2(400, 300), deg_to_rad(90)) # 2Dでrotationを指定
Back to top