同期した位置の線形補間

ネットワークマルチなどで表示物の位置を同期する際、クライアントがサーバーから受信した位置をそのまま使用すると、ラグや低同期レートなどが原因でその表示物の動作がカックカクになるが、これを軽減するために同期フレームとその前のフレームの位置を線形補間しようよってやつ。ベタ中のベタ。予測同期と併せてUnityがまだメジャーになる10数年前に初めて出会い使用しそれっきりだったが、最近ネットワーク同期について調べる機会がありそこで目にしたがやっぱり今でも活用されていて、超簡単、手軽だし書いてみた。回転補間付。

コード

public class TransformInterpolator : MonoBehaviour
{
	private Vector3 m_fromPos;
	private Vector3 m_toPos;
	private Quaternion m_fromRotation;
	private Quaternion m_toRotation;
	private float m_diffTime;
	private float m_lastTime;
	private float m_elapsedTime;

	public void SetTransform(Vector3 newPos,Quaternion newRot)
	{
		m_fromPos = transform.position;
		m_toPos = newPos;
		m_fromRotation = transform.rotation;
		m_toRotation = newRot;
		m_diffTime = Time.time - m_lastTime;
		m_lastTime = Time.time;
		m_elapsedTime = 0.0f;
	}

	void LateUpdate()
	{
		if (m_diffTime <= 0)
			return;

		m_elapsedTime += Time.deltaTime;
		float ratio = m_elapsedTime / m_diffTime;
		if (ratio >= 1.0f)
			ratio = 1.0f;

		transform.position = Vector3.Lerp(m_fromPos, m_toPos, ratio);
		transform.rotation = Quaternion.Lerp(m_fromRotation, m_toRotation, ratio);
	}

}

使用例

※疑似コード

public class SyncObject : MonoBehaviour
{
	private TransformInterpolator m_transformInterpolator;

	void Awake()
	{
		// クライアント側のみに追加
		m_transformInterpolator = gameObject.AddComponent<TransformInterpolator>();
	}

	// サーバーからスナップショットを受信したら呼ばれる
	void OnReceiveSnapshot(Snapshot snapshot)
	{
		m_transformInterpolator.SetTransform(snapshot.newPos,snapshot.newRot);
	}
}


他にも補間手法はありますが、開発中のゲームに組み込んでみると相性が悪くて思ったような挙動にならずそのプロジェクト独自の補間手法(や、サーバー側でのクラ位置管理手段)を模索することになることが多かったが、線形補間はとりあえず実装出来て効果もそこそこ、プロトレベルでは十分なのでコスパよい印象。