// 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 UnityEngine;
namespace nickmaltbie.OpenKCC.Utils.ColliderCast
{
///
/// Abstract class to optimize operations for a primitive shape
/// collider cast.
///
public abstract class AbstractPrimitiveColliderCast : MonoBehaviour, IColliderCast
{
///
/// Cache size for raycast hit.
///
public const int RaycastHitCacheSize = 25;
///
/// Cache size for collider overlap.
///
public const int OverlapCacheSize = 25;
///
/// Overlap hit cache.
///
protected static Collider[] OverlapCache = new Collider[OverlapCacheSize];
///
/// Raycast hit cache.
///
protected static RaycastHit[] HitCache = new RaycastHit[RaycastHitCacheSize];
///
/// Collider associated with this primitive collider cast.
///
protected Collider _collider;
///
/// Hiding flags for the collider component.
///
protected virtual HideFlags ColliderHideFlags => HideFlags.NotEditable;
///
/// Configure collider component.
///
public virtual void Start()
{
SetupCollider();
}
///
public bool CastSelf(
Vector3 position,
Quaternion rotation,
Vector3 direction,
float distance,
out IRaycastHit hit,
int layerMask = RaycastHelperConstants.DefaultLayerMask,
QueryTriggerInteraction queryTriggerInteraction = RaycastHelperConstants.DefaultQueryTriggerInteraction,
float skinWidth = 0.01f)
{
var closest = new RaycastHit() { distance = Mathf.Infinity };
bool hitSomething = false;
foreach (RaycastHit objHit in GetHits(position, rotation, direction, distance, layerMask, queryTriggerInteraction, skinWidth))
{
if (objHit.collider.gameObject.transform != gameObject.transform)
{
if (objHit.distance < closest.distance)
{
closest = objHit;
}
hitSomething = true;
}
}
hit = new RaycastHitWrapper(closest);
return hitSomething;
}
///
public virtual Vector3 PushOutOverlapping(Vector3 position, Quaternion rotation, float maxDistance, int layerMask = RaycastHelperConstants.DefaultLayerMask, QueryTriggerInteraction queryTriggerInteraction = RaycastHelperConstants.DefaultQueryTriggerInteraction, float skinWidth = 0.0f)
{
Vector3 pushed = Vector3.zero;
foreach (Collider overlap in GetOverlapping(position, rotation, layerMask, queryTriggerInteraction, skinWidth))
{
Physics.ComputePenetration(
Collider, position, rotation,
overlap, overlap.gameObject.transform.position, overlap.gameObject.transform.rotation,
out Vector3 direction, out float distance
);
float distPush = distance;
Vector3 push = direction.normalized * distPush;
pushed += push;
position += push;
}
return Vector3.ClampMagnitude(pushed, maxDistance);
}
///
public bool DoRaycastInDirection(Vector3 source, Vector3 direction, float distance, out IRaycastHit stepHit, int layerMask = RaycastHelperConstants.DefaultLayerMask, QueryTriggerInteraction queryTriggerInteraction = RaycastHelperConstants.DefaultQueryTriggerInteraction)
{
bool didHit = Physics.Raycast(new Ray(source, direction), out RaycastHit hit, distance, layerMask, queryTriggerInteraction);
stepHit = new RaycastHitWrapper(hit);
return didHit;
}
///
/// Primitive collider shape associated with this object.
///
public Collider Collider => _collider = _collider ?? SetupColliderComponent();
///
public abstract Vector3 GetBottom(Vector3 position, Quaternion rotation);
///
public abstract IEnumerable GetOverlapping(Vector3 position, Quaternion rotation, int layerMask = RaycastHelperConstants.DefaultLayerMask, QueryTriggerInteraction queryTriggerInteraction = RaycastHelperConstants.DefaultQueryTriggerInteraction, float skinWidth = 0.00f);
///
public abstract IEnumerable GetHits(Vector3 position, Quaternion rotation, Vector3 direction, float distance, int layerMask = RaycastHelperConstants.DefaultLayerMask, QueryTriggerInteraction queryTriggerInteraction = RaycastHelperConstants.DefaultQueryTriggerInteraction, float skinWidth = 0.01f);
///
/// Update the parameters of the collider on a configuration change.
///
public virtual void UpdateColliderParameters()
{
if (_collider != null)
{
_collider.hideFlags = ColliderHideFlags;
}
}
///
/// Setup the collider component associated with this object.
///
/// Collider created (or one that already exists) for this object.
protected abstract Collider SetupColliderComponent();
///
/// Setup the collider associated with this object.
///
public void SetupCollider()
{
_collider = SetupColliderComponent();
UpdateColliderParameters();
}
}
}