// 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
{
///
/// Collider cast for selecting between different primitive collider
/// /// shapes.
///
public class PrimitiveColliderCast : AbstractPrimitiveColliderCast
{
///
/// Selected collider configuration.
///
[Tooltip("Collider type selected for casting.")]
[SerializeField]
public ColliderConfiguration config = new ColliderConfiguration();
///
/// Generated collider for this primitive collider cast.
///
[HideInInspector]
[SerializeField]
private Collider generatedCollider;
///
/// Create the collider for this primitive collider cast.
///
public override void UpdateColliderParameters()
{
base.UpdateColliderParameters();
generatedCollider = config.AttachCollider(gameObject, true);
base._collider = generatedCollider;
if (config.type == ColliderType.Point)
{
SphereCollider sphere = gameObject.AddComponent();
sphere.radius = KCCUtils.Epsilon / 2;
sphere.center = config.pointCenter;
generatedCollider = sphere;
}
}
///
public override Vector3 GetBottom(Vector3 position, Quaternion rotation)
{
switch (config.type)
{
case ColliderType.Box:
(Vector3 boxCenter, Vector3 boxSize) = BoxColliderCast.GetParams(config.Center, config.Size, position, rotation);
return boxCenter + rotation * Vector3.down * boxSize.y / 2;
case ColliderType.Sphere:
(Vector3 sphereCenter, float sphereRadius) = SphereColliderCast.GetParams(config.Center, config.Radius, position, rotation);
return sphereCenter - sphereRadius * (rotation * transform.up);
case ColliderType.Capsule:
(_, Vector3 capsuleBottom, float capsuleRadius, _) = CapsuleColliderCast.GetParams(config.Center, config.Radius, config.Height, config.CapsuleDirection, position, rotation);
return capsuleBottom + capsuleRadius * (rotation * Vector3.down);
case ColliderType.Point:
default:
return position + rotation * config.Center;
}
}
///
public override IEnumerable GetHits(Vector3 position, Quaternion rotation, Vector3 direction, float distance, int layerMask = -1, QueryTriggerInteraction queryTriggerInteraction = QueryTriggerInteraction.Ignore, float skinWidth = 0.01f)
{
IEnumerable hits;
switch (config.type)
{
case ColliderType.Box:
(Vector3 boxCenter, Vector3 boxSize) = BoxColliderCast.GetParams(config.Center, config.Size, position, rotation, -skinWidth);
hits = Physics.BoxCastAll(boxCenter, boxSize / 2, direction, rotation, distance + skinWidth, layerMask, queryTriggerInteraction);
break;
case ColliderType.Sphere:
(Vector3 sphereCenter, float sphereRadius) = SphereColliderCast.GetParams(config.Center, config.Radius, position, rotation, -skinWidth);
hits = Physics.SphereCastAll(sphereCenter, sphereRadius, direction, distance + skinWidth, layerMask, queryTriggerInteraction);
break;
case ColliderType.Capsule:
(Vector3 capsuleTop, Vector3 capsuleBottom, float capsuleRadius, float capsuleHeight) = CapsuleColliderCast.GetParams(config.Center, config.Radius, config.Height, config.CapsuleDirection, position, rotation, -skinWidth);
hits = Physics.CapsuleCastAll(capsuleTop, capsuleBottom, capsuleRadius, direction, distance + skinWidth, layerMask, queryTriggerInteraction);
break;
case ColliderType.Point:
default:
Vector3 origin = position + rotation * config.Center;
hits = Physics.RaycastAll(origin, direction, distance, layerMask, queryTriggerInteraction);
skinWidth = 0.0f;
break;
}
return hits.Where(hit => hit.collider.transform != transform)
.Select(hit =>
{
hit.distance = Mathf.Max(hit.distance - skinWidth, 0);
return hit;
});
}
///
public override IEnumerable GetOverlapping(Vector3 position, Quaternion rotation, int layerMask = -1, QueryTriggerInteraction queryTriggerInteraction = QueryTriggerInteraction.Ignore, float skinWidth = 0.0f)
{
IEnumerable overlap;
switch (config.type)
{
case ColliderType.Box:
(Vector3 boxCenter, Vector3 boxSize) = BoxColliderCast.GetParams(config.Center, config.Size, position, rotation, -skinWidth);
overlap = Physics.OverlapBox(boxCenter, boxSize / 2, rotation, layerMask, queryTriggerInteraction);
break;
case ColliderType.Sphere:
(Vector3 sphereCenter, float sphereRadius) = SphereColliderCast.GetParams(config.Center, config.Radius, position, rotation, -skinWidth);
overlap = Physics.OverlapSphere(sphereCenter, sphereRadius, layerMask, queryTriggerInteraction);
break;
case ColliderType.Capsule:
(Vector3 capsuleTop, Vector3 capsuleBottom, float capsuleRadius, float capsuleHeight) = CapsuleColliderCast.GetParams(config.Center, config.Radius, config.Height, config.CapsuleDirection, position, rotation, -skinWidth);
overlap = Physics.OverlapCapsule(capsuleTop, capsuleBottom, capsuleRadius, layerMask, queryTriggerInteraction);
break;
case ColliderType.Point:
default:
Vector3 origin = position + rotation * config.Center;
overlap = Physics.OverlapSphere(origin, KCCUtils.Epsilon / 2, layerMask, queryTriggerInteraction);
break;
}
return overlap.Where(collider => collider.transform != transform);
}
///
protected override Collider SetupColliderComponent()
{
UpdateColliderParameters();
return generatedCollider;
}
}
}