Raycasting

업데이트: 2020/01/30

개요

Raycasting은 사용자가 지정한 Cubism의 메쉬와 임의의 좌표가 교차할지 어떨지를 판정하는 기능입니다.
클릭/탭된 좌표나 장면 중의 임의의 오브젝트와 지정된 메쉬와의 충돌 감지를 취득하는 것이 가능합니다.
Raycasting 사용 방법은 여기를 참조하십시오. 

Cubism SDK for Unity의 Raycasting은 크게 두 가지 요소로 구성됩니다.

  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

충돌 감지를 취득한 아트메쉬의 월드 좌표입니다.

이 기사가 도움이 되었나요?
아니요
이 기사에 관한 의견 및 요청사항을 보내 주시기 바랍니다.