MRが楽しい

MRやVRについて学習したことを書き残す

Particleのアタリ判定を設定してヒット位置を取得する

本日は Unity の技術調査枠です。
Particleのアタリ判定を設定してヒット位置を取得する方法を記事にします。

前提条件

以下の前回記事の続きです。
bluebirdofoz.hatenablog.com

こちらで作成した水の流れの Particle にアタリ判定を設定し、ヒット位置を取得してみます。

Particleでアタリ判定を行う

Particle にアタリ判定を付与するには Collision モジュールの設定項目を有効化します。
f:id:bluebirdofoz:20211108211401j:plain

[Particle System]コンポーネントの[Collision]にチェックを入れ、[Type]を[World]に設定します。
これで Particle が Collider にヒットして跳ね返るようになります。
f:id:bluebirdofoz:20211108211415j:plain

また今回はヒット位置を取得したいので、ヒット時の通知を行う[Send Collision Message]をチェックしておきます。
f:id:bluebirdofoz:20211108211425j:plain

その他、[Dampen]でパーティクルの減速、[Lifetime Loss]で跳ね返りパーティクルの削除を設定しました。
今回は利用しませんでしたが[Collider Force]に 0 より大きい値を設定すると、Particle で衝突対象に力を加えることもできます。
docs.unity3d.com

なお、これらのアタリ判定は[Sub Emitters]で生成される子要素の Particle には反映されません。

ヒット位置を取得する

Collision モジュールの[Send Collision Message]をチェックすると、衝突した Particle のオブジェクトと Collider オブジェクトの両方で OnParticleCollision 関数が呼び出されるようになります。
しかし、OnParticleCollision 関数はヒットしたオブジェクトの情報のみを通知します。
docs.unity3d.com

正確なヒット位置を取得するには ParticleSystem から ParticleCollisionEvent のリストを取得する必要があります。
docs.unity3d.com

サンプルコード

サンプルコードとして以下のスクリプトを作成しました。
OnParticleCollision 関数を受け取ると、Particle が対象オブジェクトに衝突した位置を UnityEvent で通知します。
・ParticleCollisionNotice.cs

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Events;
using System;

public class ParticleCollisionNotice : MonoBehaviour
{
	[Serializable]
	private class VectorUnityEvent : UnityEvent<Vector3> { }

	private ParticleSystem p_RefParticle;
	private List<ParticleCollisionEvent> p_CollisionEventList;

	[SerializeField, Tooltip("ヒット位置の通知")]
	private VectorUnityEvent p_HitPositionNotice;

    private void Start()
    {
		p_RefParticle = this.GetComponent<ParticleSystem>();
		p_CollisionEventList = new List<ParticleCollisionEvent>();
	}

    private void OnParticleCollision(GameObject hitObject)
	{
		// パーティクルのヒット位置を取得するため、イベントリストを取得する
		p_RefParticle.GetCollisionEvents(hitObject, p_CollisionEventList);

		foreach (ParticleCollisionEvent collisionEvent in p_CollisionEventList)
		{
			Vector3 pos = collisionEvent.intersection;
			Debug.Log("Particle Hit : object name = " + hitObject.name + ", position = " + pos.ToString());

			// イベントが登録されていればヒット位置を通知する
			p_HitPositionNotice?.Invoke(pos);

			// 今回は1つ目のヒット情報のみ処理する
			break;
		}
	}
}

f:id:bluebirdofoz:20211108211503j:plain

今回のスクリプトは Particle System コンポーネントのオブジェクトにスクリプトを設定しました。
f:id:bluebirdofoz:20211108211511j:plain

ヒット対象のオブジェクトで OnParticleCollision 関数を受け取る場合は hitObject に ParticleSystem のオブジェクトが返ります。
このため、上記のスクリプトはヒット対象のオブジェクトに設定しても正常に動作しないことに注意が必要です。

ヒット位置の追跡

最後にヒット位置が視覚的にわかるように Collider を外した Sphere オブジェクトをシーンに追加し、スクリプトから座標を設定するようにしました。
f:id:bluebirdofoz:20211108211522j:plain
f:id:bluebirdofoz:20211108211532j:plain

動作確認

シーンを再生して動作を確認します。
f:id:bluebirdofoz:20211108211541j:plain

Particle が Cube にヒットしたとき、ヒット位置に Sphere オブジェクトが追従すれば成功です。
f:id:bluebirdofoz:20211108211550j:plain