// 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 box collider shape.
///
public class BoxColliderCast : AbstractPrimitiveColliderCast
{
///
/// Center of the box collider.
///
[SerializeField]
[Tooltip("Center of the box collider.")]
public Vector3 center = Vector3.zero;
///
/// Size of the box collider in 3D space.
///
[SerializeField]
[Tooltip("Size of the box collider in 3D space.")]
public Vector3 size = Vector3.one;
///
/// Box collider associated with this object.
///
[HideInInspector]
[SerializeField]
private BoxCollider boxCollider;
///
/// Gets transformed parameters describing this sphere collider for a given position and rotation
///
/// Center of box relative to position.
/// Size of the box collider.
/// Position of the object.
/// Rotation of the object.
/// Buffer space around the object.
/// Returns the center of the collider in world space
/// and the size along each axis.
public static (Vector3, Vector3) GetParams(Vector3 boxCenter, Vector3 boxSize, Vector3 position, Quaternion rotation, float buffer = 0)
{
Vector3 center = rotation * boxCenter + position;
Vector3 size = boxSize + Vector3.one * buffer * 2;
return (center, size);
}
///
/// Gets transformed parameters describing this sphere collider for a given position and rotation
///
/// Position of the object.
/// Rotation of the object.
/// Buffer space around the object.
/// Returns the center of the collider in world space
/// and the size along each axis.
public (Vector3, Vector3) GetParams(Vector3 position, Quaternion rotation, float buffer = 0)
{
return GetParams(center, size, position, rotation, buffer);
}
///
public override IEnumerable GetOverlapping(
Vector3 position,
Quaternion rotation,
int layerMask = RaycastHelperConstants.DefaultLayerMask,
QueryTriggerInteraction queryTriggerInteraction = RaycastHelperConstants.DefaultQueryTriggerInteraction,
float skinWidth = 0.0f)
{
(Vector3 center, Vector3 size) = GetParams(position, rotation, -skinWidth);
int overlap = Physics.OverlapBoxNonAlloc(center, size / 2, OverlapCache, rotation, 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, Vector3 size) = GetParams(position, rotation, -skinWidth);
int hits = Physics.BoxCastNonAlloc(center, size / 2, direction, HitCache, rotation, 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, Vector3 size) = GetParams(position, rotation);
return center + rotation * Vector3.down * size.y / 2;
}
///
protected override Collider SetupColliderComponent()
{
#if UNITY_EDITOR
// Some magic to auto update the parameters from existing collider
BoxCollider existingCollider = GetComponent();
if (boxCollider == null && existingCollider != null)
{
boxCollider = existingCollider;
center = existingCollider.center;
size = existingCollider.size;
}
#endif
boxCollider = gameObject.GetComponent();
if (boxCollider == null)
{
boxCollider = gameObject.AddComponent();
}
return boxCollider;
}
///
public override void UpdateColliderParameters()
{
base.UpdateColliderParameters();
boxCollider.size = size;
boxCollider.center = center;
}
///
/// Debug function to reset the attached collider attribute.
///
internal void ResetConfigDebug()
{
boxCollider = null;
}
}
}