// 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
{
///
/// Collection of functions for abstracting more complex physics operations.
///
public static class PhysicsUtils
{
///
/// Filters a set of raycast hits for the first hit that is of a collider attached ot a specific object
///
/// Only game object that will be searched for
/// All of the hits to process
/// Closest hit that is attached to the ignored object
/// True if a hit was detected, false otherwise
public static bool FilterForFirstHitAllow(GameObject allow, IEnumerable hits, out RaycastHit closest)
{
bool hitSomething = false;
closest = new RaycastHit { distance = Mathf.Infinity };
foreach (RaycastHit hit in hits)
{
if (hit.collider.gameObject == allow && hit.distance < closest.distance)
{
hitSomething = true;
closest = hit;
}
}
return hitSomething;
}
///
/// Filters a set of raycast hits for the first hit that is of a collider attached ot a specific object
///
/// Only game object that will be searched for
/// All of the hits to process
/// Closest hit that is attached to the ignored object
/// True if a hit was detected, false otherwise
public static bool FilterForFirstHitAllow(ICollection allowList, IEnumerable hits, out RaycastHit closest)
{
bool hitSomething = false;
closest = new RaycastHit { distance = Mathf.Infinity };
foreach (RaycastHit hit in hits)
{
if (allowList.Contains(hit.collider.gameObject) && hit.distance < closest.distance)
{
hitSomething = true;
closest = hit;
}
}
return hitSomething;
}
///
/// Filters a set of raycast hits for the first hit that is not
/// of a collider attached to a given game object
///
/// Game objects to ignore when checking for collider hit
/// All of the hits to process
/// Closest hit that is attached to the ignored object
/// True if a hit was detected, false otherwise
public static bool FilterForFirstHitIgnore(ICollection ignoreList, IEnumerable hits, out RaycastHit closest)
{
bool hitSomething = false;
closest = new RaycastHit { distance = Mathf.Infinity };
foreach (RaycastHit hit in hits)
{
if (!ignoreList.Contains(hit.collider.gameObject) && hit.distance < closest.distance)
{
hitSomething = true;
closest = hit;
}
}
return hitSomething;
}
///
/// Filters a set of raycast hits for the first hit that is not
/// of a collider attached to a given game object
///
/// Game object to ignore when checking for collider hit
/// All of the hits to process
/// Closest hit that is attached to the ignored object
/// True if a hit was detected, false otherwise
public static bool FilterForFirstHitIgnore(GameObject ignore, IEnumerable hits, out RaycastHit closest)
{
bool hitSomething = false;
closest = new RaycastHit { distance = Mathf.Infinity };
foreach (RaycastHit hit in hits)
{
if (hit.collider.gameObject != ignore && hit.distance < closest.distance)
{
hitSomething = true;
closest = hit;
}
}
return hitSomething;
}
///
/// Compute the first object hit while only looking for colliders attached to a specific object
///
/// Object search for given raycast
/// Source position of raycast
/// Direction of raycast
/// Distance of raycast
/// Laymask for raycast
/// Query trigger interaction for raycast
/// The closest raycast hit event
/// True if a hit was detected, false otherwise.
public static bool RaycastHitAllow(ICollection allowList, Vector3 source, Vector3 direction, float distance,
LayerMask layerMask, QueryTriggerInteraction queryTriggerInteraction, out RaycastHit closest)
{
return FilterForFirstHitAllow(allowList, Physics.RaycastAll(source, direction, distance, layerMask, queryTriggerInteraction), out closest);
}
///
/// Compute the first object hit while only looking for colliders attached to a specific object
///
/// Object search for given raycast
/// Source position of raycast
/// Direction of raycast
/// Distance of raycast
/// Laymask for raycast
/// Query trigger interaction for raycast
/// The closest raycast hit event
/// True if a hit was detected, false otherwise.
public static bool RaycastHitAllow(GameObject target, Vector3 source, Vector3 direction, float distance,
LayerMask layerMask, QueryTriggerInteraction queryTriggerInteraction, out RaycastHit closest)
{
return FilterForFirstHitAllow(target, Physics.RaycastAll(source, direction, distance, layerMask, queryTriggerInteraction), out closest);
}
///
/// Compute the first object hit while only looking for colliders attached to a specific object
///
/// Object search for given raycast
/// Source position of raycast
/// Radius spherecast
/// Direction of raycast
/// Distance of raycast
/// Layer mask for raycast
/// Query trigger interaction for raycast
/// The closest raycast hit event
/// True if a hit was detected, false otherwise.
public static bool SphereCastAllow(GameObject target, Vector3 source, float radius, Vector3 direction, float distance,
LayerMask layerMask, QueryTriggerInteraction queryTriggerInteraction, out RaycastHit closest)
{
return FilterForFirstHitAllow(target, Physics.SphereCastAll(source, radius, direction, distance, layerMask, queryTriggerInteraction), out closest);
}
///
/// Compute the first object hit while ignoring a given object for a raycast.
/// Will include overlapping objects if objects overlap.
///
/// Object to ignore from raycast
/// Source position of raycast
/// Direction of raycast
/// Distance of raycast
/// Laymask for raycast
/// Query trigger interaction for raycast
/// The closest raycast hit event
/// True if a hit was detected, false otherwise.
public static bool RaycastFirstHitIgnore(ICollection ignoreList, Vector3 source, Vector3 direction, float distance,
LayerMask layerMask, QueryTriggerInteraction queryTriggerInteraction, out RaycastHit closest)
{
return FilterForFirstHitIgnore(ignoreList, Physics.RaycastAll(source, direction, distance, layerMask, queryTriggerInteraction), out closest);
}
///
/// Compute the first object hit while ignoring a given object for a raycast.
/// Will include overlapping objects if objects overlap.
///
/// Object to ignore from raycast
/// Source position of raycast
/// Direction of raycast
/// Distance of raycast
/// Laymask for raycast
/// Query trigger interaction for raycast
/// The closest raycast hit event
/// True if a hit was detected, false otherwise.
public static bool RaycastFirstHitIgnore(GameObject ignore, Vector3 source, Vector3 direction, float distance,
LayerMask layerMask, QueryTriggerInteraction queryTriggerInteraction, out RaycastHit closest)
{
return FilterForFirstHitIgnore(ignore, Physics.RaycastAll(source, direction, distance, layerMask, queryTriggerInteraction), out closest);
}
///
/// Compute the first object hit while ignoring a given object for a spherecast.
/// Will include overlapping objects if objects overlap.
///
/// Objects to ignore from spherecast
/// Source position of spherecast
/// Radius spherecast
/// Direction of spherecast
/// Distance of spherecast
/// Laymask for spherecast
/// Query trigger interaction for spherecast
/// The closest raycast hit event
/// True if a hit was detected, false otherwise.
public static bool SphereCastFirstHitIgnore(ICollection ignoreList, Vector3 source, float radius, Vector3 direction, float distance,
LayerMask layerMask, QueryTriggerInteraction queryTriggerInteraction, out RaycastHit closest)
{
return FilterForFirstHitIgnore(ignoreList, Physics.SphereCastAll(source, radius, direction, distance, layerMask, queryTriggerInteraction), out closest);
}
///
/// Compute the first object hit while ignoring a given object for a spherecast.
/// Will include overlapping objects if objects overlap.
///
/// Object to ignore from spherecast
/// Source position of spherecast
/// Radius spherecast
/// Direction of spherecast
/// Distance of spherecast
/// Laymask for spherecast
/// Query trigger interaction for spherecast
/// The closest raycast hit event
/// True if a hit was detected, false otherwise.
public static bool SphereCastFirstHitIgnore(GameObject ignore, Vector3 source, float radius, Vector3 direction, float distance,
LayerMask layerMask, QueryTriggerInteraction queryTriggerInteraction, out RaycastHit closest)
{
return FilterForFirstHitIgnore(ignore, Physics.SphereCastAll(source, radius, direction, distance, layerMask, queryTriggerInteraction), out closest);
}
}
}