Raycasting

最終更新: 2020年1月30日

概述

Raycasting是用于判定用户指定的Cubism网格是否与任意座标相交的功能。
可以获得单击/点击座标、场景中任意物体与指定网格之间的重叠检测。
参考 此处 了解如何使用Raycasting。

Cubism SDK for Unity中的Raycasting包括两个主要元素。

  1. 用于指定要判断的图形网格的组件
  2. 进行指定输入座标的图形网格重叠检测的组件。

1. 用于指定要判断的图形网格的组件

使用CubismRaycastable指定用于重叠检测的网格。

CubismRaycastable通过附加到置入[Prefab根]/Drawables/下的GameObject来使用。
与附加的GameObject具有相同ID的图形网格将用于重叠检测。

CubismRaycastable具有设置该网格的重叠检测精度的参数。

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
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;
}
}
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; } }
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有两个可设置的值,每个值如下。

  • BoundingBox

包围网格并具有水平或垂直边的矩形用作重叠检测。
根据网格的形状,即使在网格外的座标处也会执行重叠检测,但它比Triangles具有更好的性能。

  • Triangles

网格的形状被用作重叠检测的范围。
虽然性能不如BoundingBox,但可以在准确的范围内做出判断。

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
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
}
}
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 } }
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的所有CubismRaycastables的参考。
在执行过程中追加/删除重叠检测的网格时,会调用CubismRaycaster.Refresh()再次获取参考。

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
/// <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();
}
/// <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(); }
        /// <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()从座标中获取重叠检测。

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
/// <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;
}
/// <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; }
        /// <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[]类型的副本,设置获得重叠检测的网格信息。

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
var raycastResults = new CubismRaycastHit[4];
var hitCount = _raycaster.Raycast(Camera.main.ScreenPointToRay(Input.mousePosition), raycastResults);
var raycastResults = new CubismRaycastHit[4]; var hitCount = _raycaster.Raycast(Camera.main.ScreenPointToRay(Input.mousePosition), raycastResults);
            var raycastResults = new CubismRaycastHit[4];
            var hitCount = _raycaster.Raycast(Camera.main.ScreenPointToRay(Input.mousePosition), raycastResults);

当多个网格在同一座标上重叠时,CubismRaycaster.Raycast()会获得CubismRaycastHit[]类型副本的元素数量。
如果超过元素数量的网格重叠,将不会获得超出部分网格的结果。

CubismRaycastHit是包含已获得重叠检测的网格信息的结构。

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
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;
}
}
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; } }
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

获得重叠检测的图形网格的世界座标。

请问这篇文章对您有帮助吗?
关于本报道,敬请提出您的意见及要求。