Raycasting

最終更新: 2020年1月30日

概要

Raycastingは、ユーザが指定したCubismのメッシュと任意の座標とが交差するかどうかを判定する機能です。
クリック/タップされた座標や、シーン中の任意のオブジェクトと指定されたメッシュとの当たり判定を取得することが可能です。
Raycastingの使用方法については こちら をご覧ください。

Cubism SDK for Unity におけるRaycastingは大きく2種類の要素によって構成されています。

  1. 判定させるアートメッシュ指定用のコンポーネント
  2. 入力された座標と指定されたアートメッシュの当たり判定を行うコンポーネント

1. 判定させるアートメッシュ指定用のコンポーネント

当たり判定に使用するメッシュを指定するには、CubismRaycastableを使用します。

CubismRaycastableは[Prefabのルート]/Drawables/ 以下に配置されたGameObjectにアタッチして使用します。
これがアタッチされたGameObjectと同じIDのアートメッシュを当たり判定に使用します。

CubismRaycastableは、そのメッシュの当たり判定の精度を設定するパラメータを持っています。

namespace Live2D.Cubism.Framework.Raycasting
{
    /// <summary>
    /// Allows raycasting against <see cref="Core.CubismDrawable"/>s.
    /// </summary>
    public sealed class CubismRaycastable : MonoBehaviour
    {
        /// <summary>
        /// The precision.
        /// </summary>
        public CubismRaycastablePrecision Precision;
    }
}

CubismRaycastable.Precisionには設定可能な値が2つあり、それぞれ以下のようになっております。

  • BoundingBox

そのメッシュを囲う、四辺が水平または垂直の矩形を当たり判定として使用します。
メッシュの形状によってはメッシュ外の座標でも当たり判定を取りますが、Trianglesと比較してパフォーマンスに優れます。

  • Triangles

メッシュの形状を当たり判定の範囲として使用します。
BoundingBoxと比較するとパフォーマンスに劣りますが、正確な範囲で判定を行うことができます。

namespace Live2D.Cubism.Framework.Raycasting
{
    /// <summary>
    /// Precision for casting rays against a <see cref="CubismRaycastable"/>.
    /// </summary>
    public enum CubismRaycastablePrecision
    {
        /// <summary>
        /// Cast against bounding box.
        /// </summary>
        BoundingBox,

        /// <summary>
        /// Cast against triangles.
        /// </summary>
        Triangles
    }
}

2. 入力された座標と指定されたアートメッシュの当たり判定を行うコンポーネント

実際の当たり判定の取得はCubismRaycasterを使用します。
CubismRaycasterを使用する際はCubismのPrefabのルートにアタッチします。

CubismRaycasterの初期化時に、PrefabにアタッチされたすべてのCubismRaycastableの参照を取得します。
実行中に当たり判定用のメッシュを追加/削除した際には、CubismRaycaster.Refresh()を呼んで参照を取得し直します。

        /// <summary>
        /// Refreshes the controller. Call this method after adding and/or removing <see cref="CubismRaycastable"/>.
        /// </summary>
        private void Refresh()
        {
            var candidates = this
                .FindCubismModel()
                .Drawables;


            // Find raycastable drawables.
            var raycastables = new List<CubismRenderer>();
            var raycastablePrecisions = new List<CubismRaycastablePrecision>();


            for (var i = 0; i < candidates.Length; i++)
            {
                // Skip non-raycastables.
                if (candidates[i].GetComponent<CubismRaycastable>() == null)
                {
                    continue;
                }


                raycastables.Add(candidates[i].GetComponent<CubismRenderer>());
                raycastablePrecisions.Add(candidates[i].GetComponent<CubismRaycastable>().Precision);
            }


            // Cache raycastables.
            Raycastables = raycastables.ToArray();
            RaycastablePrecisions = raycastablePrecisions.ToArray();
        }

        ...

        /// <summary>
        /// Called by Unity. Makes sure cache is initialized.
        /// </summary>
        private void Start()
        {
            // Initialize cache.
            Refresh();
        }

座標から当たり判定を取得するには、CubismRaycaster.Raycast()を利用します。

        /// <summary>
        /// Casts a ray.
        /// </summary>
        /// <param name="origin">The origin of the ray.</param>
        /// <param name="direction">The direction of the ray.</param>
        /// <param name="result">The result of the cast.</param>
        /// <param name="maximumDistance">[Optional] The maximum distance of the ray.</param>
        /// <returns><see langword="true"/> in case of a hit; <see langword="false"/> otherwise.</returns>
        /// <returns>The numbers of drawables had hit</returns>
        public int Raycast(Vector3 origin, Vector3 direction, CubismRaycastHit[] result, float maximumDistance = Mathf.Infinity)
        {
            return Raycast(new Ray(origin, direction), result, maximumDistance);
        }

        /// <summary>
        /// Casts a ray.
        /// </summary>
        /// <param name="ray"></param>
        /// <param name="result">The result of the cast.</param>
        /// <param name="maximumDistance">[Optional] The maximum distance of the ray.</param>
        /// <returns><see langword="true"/> in case of a hit; <see langword="false"/> otherwise.</returns>
        /// <returns>The numbers of drawables had hit</returns>
        public int Raycast(Ray ray, CubismRaycastHit[] result, float maximumDistance = Mathf.Infinity)
        {
            // Cast ray against model plane.
            var intersectionInWorldSpace = ray.origin + ray.direction * (ray.direction.z / ray.origin.z);
            var intersectionInLocalSpace = transform.InverseTransformPoint(intersectionInWorldSpace);


            intersectionInLocalSpace.z = 0;


            var distance = intersectionInWorldSpace.magnitude;


            // Return non-hits.
            if (distance > maximumDistance)
            {
                return 0;
            }

            // Cast against each raycastable.
            var hitCount = 0;


            for (var i = 0; i < Raycastables.Length; i++)
            {
                var raycastable = Raycastables[i];
                var raycastablePrecision = RaycastablePrecisions[i];


                // Skip inactive raycastables.
                if (!raycastable.MeshRenderer.enabled)
                {
                    continue;
                }



                if (raycastablePrecision == CubismRaycastablePrecision.BoundingBox)
                {
                    var bounds = raycastable.Mesh.bounds;

                    // Skip non hits
                    if (!bounds.Contains(intersectionInLocalSpace))
                    {
                        continue;
                    }
                }
                else
                {
                    // Skip non hits
                    if (!ContainsInTriangles(raycastable.Mesh, intersectionInLocalSpace))
                    {
                        continue;
                    }
                }


                result[hitCount].Drawable = raycastable.GetComponent<CubismDrawable>();
                result[hitCount].Distance = distance;
                result[hitCount].LocalPosition = intersectionInLocalSpace;
                result[hitCount].WorldPosition = intersectionInWorldSpace;


                ++hitCount;


                // Exit if result buffer is full.
                if (hitCount == result.Length)
                {
                    break;
                }
            }


            return hitCount;
        }

CubismRaycaster.Raycast()は返り値に当たり判定を取得したメッシュの数を返します。
また、引数に渡したCubismRaycastHit[]型のインスタンスに当たり判定を取得したメッシュの情報が設定されます。

            var raycastResults = new CubismRaycastHit[4];
            var hitCount = _raycaster.Raycast(Camera.main.ScreenPointToRay(Input.mousePosition), raycastResults);

CubismRaycaster.Raycast()は、同座標上に複数のメッシュが重なっていた場合、最大でCubismRaycastHit[]型のインスタンスの要素の数まで取得します。
要素数以上のメッシュが重なっていた場合、超えた分のメッシュは結果が取得されません。

CubismRaycastHitは当たり判定を取得したメッシュの情報を持つ構造体です。

using Live2D.Cubism.Core;
using UnityEngine;


namespace Live2D.Cubism.Framework.Raycasting
{
    /// <summary>
    /// Contains raycast information.
    /// </summary>
    public struct CubismRaycastHit
    {
        /// <summary>
        /// The hit <see cref="CubismDrawable"/>.
        /// </summary>
        public CubismDrawable Drawable;

        /// <summary>
        /// The distance the ray traveled until it hit the <see cref="CubismDrawable"/>.
        /// </summary>
        public float Distance;

        /// <summary>
        /// The hit position local to the <see cref="CubismDrawable"/>.
        /// </summary>
        public Vector3 LocalPosition;

        /// <summary>
        /// The hit position in world coordinates.
        /// </summary>
        public Vector3 WorldPosition;
    }
}
  • Drawable

当たり判定を取得したアートメッシュの参照です。

  • Distance

指定した座標からの距離です。
CubismRaycaster.Raycast()の引数に渡したoriginまたはray.originとDrawableのTransform.positionの直線距離です。

  • LocalPosition

当たり判定を取得したアートメッシュのローカル座標です。

  • WorldPosition

当たり判定を取得したアートメッシュのワールド座標です。

この記事はお役に立ちましたか?
はいいいえ
この記事に関するご意見・
ご要望をお聞かせください。