[Unity]球体の内側に衝突判定Colliderを付与する
360球体画像の内側からアイトラッキングで視線情報を追従してどの部分を見ていたかログを取得する処理をつくっていましたが、ビルド後に視線のRayと360球体画像の内側の衝突判定がとれなくなりログに出力できないという問題が発生しました。
今回はこの問題の解決方法をシェアしたいと思います。
球体の内側に衝突判定Colliderを付与する
Unityでは球体などのオブジェクトの衝突判定はオブジェクト外側に設定されています。
なのでまずはオブジェクトの衝突判定をオブジェクト内側に変更する処理が必要です。
以下のUnityフォーラムのスレッドで方法が紹介されていました。
https://forum.unity.com/threads/can-you-invert-a-sphere-or-box-collider.118733/
using UnityEngine;
using System.Linq;
using System.Collections;
public class AddInvertedMeshCollider : MonoBehaviour
{
public bool removeExistingColliders = true;
public void CreateInvertedMeshCollider()
{
if (removeExistingColliders)
RemoveExistingColliders();
InvertMesh();
gameObject.AddComponent<MeshCollider>();
}
private void RemoveExistingColliders()
{
Collider[] colliders = GetComponents<Collider>();
for (int i = 0; i < colliders.Length; i++)
DestroyImmediate(colliders[i]);
}
private void InvertMesh()
{
Mesh mesh = GetComponent<MeshFilter>().mesh;
mesh.triangles = mesh.triangles.Reverse().ToArray();
}
}
この方法では、Unityプロジェクト上でデバッグモードとして実行した場合は動作するのですが、exeでビルドした後は正しく動作しない(内側に衝突判定Colliderが付与されない)問題があることがわかりました。
問題切り分けとプロジェクトの作成
当初はexeでビルドした後に正しく動作しない問題の原因はアイトラッキングにあると思い調査をおこなってましたが、問題の切り分け用にアイトラッキングを使用しないシンプルなプロジェクトを作成して確認をおこなったところ、原因は上記のAddInvertedMeshColliderスクリプトがビルド後に動作しなくなることがわかりました。
以下のようなシンプルなテスト用プロジェクトの作成をおこない動作確認をおこないました。
- 1. 外周用の球体を用意します。
- 2. 外周用の球体の内側に動作確認用にUnity物理演算の重力設定をおこなった小さな球体をいくつか配置します。
- 3. 外周用の球体の衝突判定が外側にあるか内側にあるかで内側に配置した小さな球体の挙動が変わるので、スクリプトが動作しているか確認します。
外周用の球体の衝突判定が「外側」にある場合、
球体の内側にある小さな球体が外に押し出されそのまま下に落下していきます。

外周用の球体の衝突判定が「内側」にある場合、
球体の内側にある小さな球体が外周用の球体の内側の衝突判定まで落下して跳ねかえります。

Mesh Colliderコンポーネントを追加して対処する
上記の問題を改善するために別の方法を調べていたところ、以下のQiitaで紹介されていた方法がありました。
https://qiita.com/mechamogera/items/166f7486323e171356b4
この方法では対象の球体にMesh Colliderコンポーネントを追加した上でスクリプトで処理をする手法です。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class ReverseNormals2 : MonoBehaviour {
// Use this for initialization
void Start()
{
MeshFilter filter = GetComponent(typeof(MeshFilter)) as MeshFilter;
if (filter != null)
{
Mesh mesh = CopyMesh(filter.mesh);
Vector3[] normals = mesh.normals;
for (int i = 0; i < normals.Length; i++)
normals[i] = -normals[i];
mesh.normals = normals;
for (int m = 0; m < mesh.subMeshCount; m++)
{
int[] triangles = mesh.GetTriangles(m);
for (int i = 0; i < triangles.Length; i += 3)
{
int temp = triangles[i + 0];
triangles[i + 0] = triangles[i + 1];
triangles[i + 1] = temp;
}
mesh.SetTriangles(triangles, m);
}
}
this.GetComponent<MeshCollider>().sharedMesh = filter.mesh;
}
// Update is called once per frame
void Update () {
}
public Mesh CopyMesh(Mesh mesh)
{
var copy = new Mesh();
foreach (var property in typeof(Mesh).GetProperties())
{
if (property.GetSetMethod() != null && property.GetGetMethod() != null)
{
property.SetValue(copy, property.GetValue(mesh, null), null);
}
}
return copy;
}
}
こちらの方法ではビルド後も問題なく動作することが確認できました。
木曜日担当:nishida
nishida at 2024年02月22日 10:00:00