CinemachineImpulseをScriptableObjectにした話

はじめに

Cinemachineには、衝撃などの発生時にカメラを振動させるための「CinemachineImpulse」という機能があります。

実際にCinemachineImpulseを使おうとすると、大体このような感じで「Cinemachine Impulse Source」系のコンポーネントをアタッチして設定することになります。

ただ、僕はこのワークフローに欠点を感じています。

  • ゲームオブジェクトのインスペクターを圧迫する
  • 複数のコンポーネント間で、同じImpulseを共有できない

今回紹介する方法は、この2つを解決できます。

コード

以下のコードは、CinemachineImpulseをScriptableObjectとして実装するコードです。


using UnityEngine;
using Cinemachine;

[CreateAssetMenu]
public class CinemachineImpulseData : ScriptableObject {

	static readonly Vector3 k_DefaultVelocity = Vector3.down;

	[SerializeField, CinemachineImpulseDefinitionProperty]
	CinemachineImpulseDefinition m_Definition = new CinemachineImpulseDefinition();

	public CinemachineImpulseDefinition Definition => m_Definition;

	public void GenerateImpulse (Vector3 position) {
		m_Definition.CreateEvent(position,k_DefaultVelocity);
	}

	public void GenerateImpulse (Vector3 position,Vector3 velocity) {
		m_Definition.CreateEvent(position,velocity);
	}

#if UNITY_EDITOR
	void OnValidate () {
		m_Definition.OnValidate();
	}
#endif

}

実際にCinemachineImpulseDataのアセットを作成するとこのような感じになります。

実際に使ってみる

以下のコードは、実際にゲームで使っている「何かしらの効果(攻撃など)が発生したときにImpulseを発生させる」コンポーネントの例です。


using UnityEngine;
using UniRx;

public class EffectImpulse : MonoBehaviour {

	[SerializeField]
	Vector3 m_Velocity = Vector3.down;

	[SerializeField]
	CinemachineImpulseData m_Impulse;

	Transform m_Transform;
	GridEffectorBase m_GridEffector;

	readonly SerialDisposable m_Disposable = new SerialDisposable();

	void Awake () {
		m_Transform = transform;
		m_GridEffector = GetComponent();
	}

	void OnEnable () {
		m_Disposable.Disposable = m_GridEffector.OnAffected.Subscribe(_ => {
			m_Impulse.GenerateImpulse(m_Transform.position,m_Velocity);
		});
	}

	void OnDisable () {
		m_Disposable.Disposable = null;
	}

}

このコンポーネントを実際にインスペクターで表示すると以下のような感じになります。

とても簡素でインスペクターを圧迫せず、複数のコンポーネント間でImpulseを共有することができます。

おわりに

ワークフローは大事

記事をシェアしてもらえると嬉しいです!