// Copyright (C) 2023 Nicholas Maltbie
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
// associated documentation files (the "Software"), to deal in the Software without restriction,
// including without limitation the rights to use, copy, modify, merge, publish, distribute,
// sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
// BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
namespace nickmaltbie.OpenKCC.Utils.ColliderCast
{
///
/// ColliderCast behaviour intended to work with any sphere collider shape.
///
public class SphereColliderCast : AbstractPrimitiveColliderCast
{
///
/// Radius of the sphere.
///
[SerializeField]
[Tooltip("Radius of the sphere.")]
public float radius = 0.5f;
///
/// Center of the sphere.
///
[SerializeField]
[Tooltip("Center of the sphere.")]
public Vector3 center = Vector3.zero;
///
/// Sphere collider associated with this object.
///
[HideInInspector]
[SerializeField]
private SphereCollider sphereCollider;
///
/// Gets transformed parameters describing this sphere collider for a given position and rotation
///
/// Relative position of sphere center.
/// Radius of the sphere.
/// Position of the object.
/// Rotation of the object.
/// Modifier to add to radius when computing shape of collider.
/// Returns the center of the sphere in world space and the modified radius.
public static (Vector3, float) GetParams(Vector3 sphereCenter, float sphereRadius, Vector3 position, Quaternion rotation, float radiusMod = 0.0f)
{
Vector3 center = rotation * sphereCenter + position;
float radius = sphereRadius + radiusMod;
return (center, radius);
}
///
/// Gets transformed parameters describing this sphere collider for a given position and rotation
///
/// Position of the object.
/// Rotation of the object.
/// Modifier to add to radius when computing shape of collider.
/// Returns the center of the collider in world space
/// and the modified radius.
public (Vector3, float) GetParams(Vector3 position, Quaternion rotation, float radiusMod = 0.0f)
{
return GetParams(center, radius, position, rotation, radiusMod);
}
///
public override IEnumerable GetOverlapping(
Vector3 position,
Quaternion rotation,
int layerMask = RaycastHelperConstants.DefaultLayerMask,
QueryTriggerInteraction queryTriggerInteraction = RaycastHelperConstants.DefaultQueryTriggerInteraction,
float skinWidth = 0.0f)
{
(Vector3 center, float radius) = GetParams(position, rotation, -skinWidth);
int overlap = Physics.OverlapSphereNonAlloc(center, radius, OverlapCache, layerMask, queryTriggerInteraction);
return Enumerable.Range(0, overlap).Select(i => OverlapCache[i])
.Where(c => c.transform != transform);
}
///
public override IEnumerable GetHits(
Vector3 position,
Quaternion rotation,
Vector3 direction,
float distance,
int layerMask = RaycastHelperConstants.DefaultLayerMask,
QueryTriggerInteraction queryTriggerInteraction = RaycastHelperConstants.DefaultQueryTriggerInteraction,
float skinWidth = 0.01f)
{
(Vector3 center, float radius) = GetParams(position, rotation, -skinWidth);
int hits = Physics.SphereCastNonAlloc(center, radius, direction, HitCache, distance + skinWidth, layerMask, queryTriggerInteraction);
return Enumerable.Range(0, hits).Select(i => HitCache[i])
.Where(hit => hit.collider.transform != transform)
.Select(hit =>
{
hit.distance = Mathf.Max(hit.distance - skinWidth, 0);
return hit;
});
}
///
public override Vector3 GetBottom(Vector3 position, Quaternion rotation)
{
(Vector3 center, float radius) = GetParams(position, rotation);
return center - radius * (rotation * transform.up);
}
///
protected override Collider SetupColliderComponent()
{
#if UNITY_EDITOR
// Some magic to auto update the parameters from existing collider
SphereCollider existingCollider = GetComponent();
if (sphereCollider == null && existingCollider != null)
{
sphereCollider = existingCollider;
center = existingCollider.center;
radius = existingCollider.radius;
}
#endif
sphereCollider = gameObject.GetComponent();
if (sphereCollider == null)
{
sphereCollider = gameObject.AddComponent();
}
return sphereCollider;
}
///
public override void UpdateColliderParameters()
{
base.UpdateColliderParameters();
sphereCollider.radius = radius;
sphereCollider.center = center;
}
///
/// Debug function to reset the attached collider attribute.
///
internal void ResetConfigDebug()
{
sphereCollider = null;
}
}
}