移除ECM2
This commit is contained in:
@@ -1,8 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 602ac0bae88fa1545bdf9f8e0ae8b15e
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,11 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f4cf32970eac4c9498a96ba8d86aad38
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -1,8 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b5de5e9cf3765bc478e60446e12f8604
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -1,263 +0,0 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace ECM2
|
||||
{
|
||||
public static class Extensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Return the square of the given value.
|
||||
/// </summary>
|
||||
|
||||
public static int square(this int value)
|
||||
{
|
||||
return value * value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Return the square of the given value.
|
||||
/// </summary>
|
||||
|
||||
public static float square(this float value)
|
||||
{
|
||||
return value * value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks whether value is near to zero within a tolerance.
|
||||
/// </summary>
|
||||
|
||||
public static bool isZero(this float value)
|
||||
{
|
||||
const float kTolerance = 0.0000000001f;
|
||||
|
||||
return Mathf.Abs(value) < kTolerance;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a copy of given vector with only X component of the vector.
|
||||
/// </summary>
|
||||
|
||||
public static Vector3 onlyX(this Vector3 vector3)
|
||||
{
|
||||
vector3.y = 0.0f;
|
||||
vector3.z = 0.0f;
|
||||
|
||||
return vector3;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a copy of given vector with only Y component of the vector.
|
||||
/// </summary>
|
||||
|
||||
public static Vector3 onlyY(this Vector3 vector3)
|
||||
{
|
||||
vector3.x = 0.0f;
|
||||
vector3.z = 0.0f;
|
||||
|
||||
return vector3;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a copy of given vector with only Z component of the vector.
|
||||
/// </summary>
|
||||
|
||||
public static Vector3 onlyZ(this Vector3 vector3)
|
||||
{
|
||||
vector3.x = 0.0f;
|
||||
vector3.y = 0.0f;
|
||||
|
||||
return vector3;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a copy of given vector with only X and Y components of the vector.
|
||||
/// </summary>
|
||||
|
||||
public static Vector3 onlyXY(this Vector3 vector3)
|
||||
{
|
||||
vector3.z = 0.0f;
|
||||
|
||||
return vector3;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a copy of given vector with only X and Z components of the vector.
|
||||
/// </summary>
|
||||
|
||||
public static Vector3 onlyXZ(this Vector3 vector3)
|
||||
{
|
||||
vector3.y = 0.0f;
|
||||
|
||||
return vector3;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks whether vector is near to zero within a tolerance.
|
||||
/// </summary>
|
||||
|
||||
public static bool isZero(this Vector2 vector2)
|
||||
{
|
||||
return vector2.sqrMagnitude < 9.99999943962493E-11;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks whether vector is near to zero within a tolerance.
|
||||
/// </summary>
|
||||
|
||||
public static bool isZero(this Vector3 vector3)
|
||||
{
|
||||
return vector3.sqrMagnitude < 9.99999943962493E-11;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks whether vector is exceeding the magnitude within a small error tolerance.
|
||||
/// </summary>
|
||||
|
||||
public static bool isExceeding(this Vector3 vector3, float magnitude)
|
||||
{
|
||||
// Allow 1% error tolerance, to account for numeric imprecision.
|
||||
|
||||
const float kErrorTolerance = 1.01f;
|
||||
|
||||
return vector3.sqrMagnitude > magnitude * magnitude * kErrorTolerance;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a copy of given vector with a magnitude of 1,
|
||||
/// and outs its magnitude before normalization.
|
||||
///
|
||||
/// If the vector is too small to be normalized a zero vector will be returned.
|
||||
/// </summary>
|
||||
|
||||
public static Vector3 normalized(this Vector3 vector3, out float magnitude)
|
||||
{
|
||||
magnitude = vector3.magnitude;
|
||||
if (magnitude > 9.99999974737875E-06)
|
||||
return vector3 / magnitude;
|
||||
|
||||
magnitude = 0.0f;
|
||||
|
||||
return Vector3.zero;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Dot product of two vectors.
|
||||
/// </summary>
|
||||
|
||||
public static float dot(this Vector3 vector3, Vector3 otherVector3)
|
||||
{
|
||||
return Vector3.Dot(vector3, otherVector3);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a copy of given vector projected onto normal vector.
|
||||
/// </summary>
|
||||
|
||||
public static Vector3 projectedOn(this Vector3 thisVector, Vector3 normal)
|
||||
{
|
||||
return Vector3.Project(thisVector, normal);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a copy of given vector projected onto a plane defined by a normal orthogonal to the plane.
|
||||
/// </summary>
|
||||
|
||||
public static Vector3 projectedOnPlane(this Vector3 thisVector, Vector3 planeNormal)
|
||||
{
|
||||
return Vector3.ProjectOnPlane(thisVector, planeNormal);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a copy of given vector with its magnitude clamped to maxLength.
|
||||
/// </summary>
|
||||
|
||||
public static Vector3 clampedTo(this Vector3 vector3, float maxLength)
|
||||
{
|
||||
return Vector3.ClampMagnitude(vector3, maxLength);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a copy of given vector perpendicular to other vector.
|
||||
/// </summary>
|
||||
|
||||
public static Vector3 perpendicularTo(this Vector3 thisVector, Vector3 otherVector)
|
||||
{
|
||||
return Vector3.Cross(thisVector, otherVector).normalized;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a copy of given vector adjusted to be tangent to a specified surface normal relatively to given up axis.
|
||||
/// </summary>
|
||||
|
||||
public static Vector3 tangentTo(this Vector3 thisVector, Vector3 normal, Vector3 up)
|
||||
{
|
||||
Vector3 r = thisVector.perpendicularTo(up);
|
||||
Vector3 t = normal.perpendicularTo(r);
|
||||
|
||||
return t * thisVector.magnitude;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Transforms a vector to be relative to given transform.
|
||||
/// If isPlanar == true, the transform will be applied on the plane defined by world up axis.
|
||||
/// </summary>
|
||||
|
||||
public static Vector3 relativeTo(this Vector3 vector3, Transform relativeToThis, bool isPlanar = true)
|
||||
{
|
||||
Vector3 forward = relativeToThis.forward;
|
||||
|
||||
if (isPlanar)
|
||||
{
|
||||
Vector3 upAxis = Vector3.up;
|
||||
forward = forward.projectedOnPlane(upAxis);
|
||||
|
||||
if (forward.isZero())
|
||||
forward = Vector3.ProjectOnPlane(relativeToThis.up, upAxis);
|
||||
}
|
||||
|
||||
Quaternion q = Quaternion.LookRotation(forward);
|
||||
|
||||
return q * vector3;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Transforms a vector to be relative to given transform.
|
||||
/// If isPlanar == true, the transform will be applied on the plane defined by upAxis.
|
||||
/// </summary>
|
||||
|
||||
public static Vector3 relativeTo(this Vector3 vector3, Transform relativeToThis, Vector3 upAxis, bool isPlanar = true)
|
||||
{
|
||||
Vector3 forward = relativeToThis.forward;
|
||||
|
||||
if (isPlanar)
|
||||
{
|
||||
forward = Vector3.ProjectOnPlane(forward, upAxis);
|
||||
|
||||
if (forward.isZero())
|
||||
forward = Vector3.ProjectOnPlane(relativeToThis.up, upAxis);
|
||||
}
|
||||
|
||||
Quaternion q = Quaternion.LookRotation(forward, upAxis);
|
||||
|
||||
return q * vector3;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clamps the given quaternion pitch rotation between the given minPitchAngle and maxPitchAngle.
|
||||
/// </summary>
|
||||
|
||||
public static Quaternion clampPitch(this Quaternion quaternion, float minPitchAngle, float maxPitchAngle)
|
||||
{
|
||||
quaternion.x /= quaternion.w;
|
||||
quaternion.y /= quaternion.w;
|
||||
quaternion.z /= quaternion.w;
|
||||
quaternion.w = 1.0f;
|
||||
|
||||
float pitch = Mathf.Clamp(2.0f * Mathf.Rad2Deg * Mathf.Atan(quaternion.x), minPitchAngle, maxPitchAngle);
|
||||
|
||||
quaternion.x = Mathf.Tan(pitch * 0.5f * Mathf.Deg2Rad);
|
||||
|
||||
return quaternion;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 32f22b14e3a428e47a9449588ec9d78b
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -1,174 +0,0 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace ECM2
|
||||
{
|
||||
public static class MathLib
|
||||
{
|
||||
/// <summary>
|
||||
/// Returns Value mapped from one range into another.
|
||||
/// </summary>
|
||||
|
||||
public static float Remap(float inA, float inB, float outA, float outB, float value)
|
||||
{
|
||||
float t = Mathf.InverseLerp(inA, inB, value);
|
||||
|
||||
return Mathf.Lerp(outA, outB, t);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Return the square of the given value.
|
||||
/// </summary>
|
||||
|
||||
public static float Square(float value)
|
||||
{
|
||||
return value * value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the direction adjusted to be tangent to a specified surface normal relatively to given up axis.
|
||||
/// </summary>
|
||||
|
||||
public static Vector3 GetTangent(Vector3 direction, Vector3 normal, Vector3 up)
|
||||
{
|
||||
Vector3 right = direction.perpendicularTo(up);
|
||||
|
||||
return normal.perpendicularTo(right);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Projects a given point onto the plane defined by plane origin and plane normal.
|
||||
/// </summary>
|
||||
|
||||
public static Vector3 ProjectPointOnPlane(Vector3 point, Vector3 planeOrigin, Vector3 planeNormal)
|
||||
{
|
||||
Vector3 toPoint = point - planeOrigin;
|
||||
Vector3 toPointProjected = Vector3.Project(toPoint, planeNormal);
|
||||
|
||||
return point - toPointProjected;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clamps given angle within min - max range.
|
||||
/// </summary>
|
||||
|
||||
public static float ClampAngle(float a, float min, float max)
|
||||
{
|
||||
while (max < min)
|
||||
max += 360.0f;
|
||||
|
||||
while (a > max)
|
||||
a -= 360.0f;
|
||||
|
||||
while (a < min)
|
||||
a += 360.0f;
|
||||
|
||||
return a > max ? a - (max + min) * 0.5f < 180.0f ? max : min : a;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns Angle in the range (0, 360)
|
||||
/// </summary>
|
||||
|
||||
public static float ClampAngle(float angle)
|
||||
{
|
||||
// returns angle in the range (-360, 360)
|
||||
|
||||
angle = angle % 360.0f;
|
||||
|
||||
if (angle < 0.0f)
|
||||
{
|
||||
// shift to (0, 360) range
|
||||
|
||||
angle += 360.0f;
|
||||
}
|
||||
|
||||
return angle;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Return angle in range -180 to 180
|
||||
/// </summary>
|
||||
|
||||
public static float NormalizeAngle(float angle)
|
||||
{
|
||||
// returns angle in the range (0, 360)
|
||||
|
||||
angle = ClampAngle(angle);
|
||||
|
||||
if (angle > 180.0f)
|
||||
{
|
||||
// shift to (-180,180)
|
||||
|
||||
angle -= 360.0f;
|
||||
}
|
||||
|
||||
return angle;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clamps the given angle into 0 - 360 degrees range.
|
||||
/// </summary>
|
||||
|
||||
private static float Clamp0360(float eulerAngles)
|
||||
{
|
||||
float result = eulerAngles - Mathf.CeilToInt(eulerAngles / 360f) * 360f;
|
||||
if (result < 0) result += 360f;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a new rotation angle (interpolated) clamped in the range (0.0f , 360.0f)
|
||||
/// </summary>
|
||||
|
||||
public static float FixedTurn(float current, float target, float maxDegreesDelta)
|
||||
{
|
||||
if (maxDegreesDelta == 0.0f)
|
||||
return Clamp0360(current);
|
||||
|
||||
if (maxDegreesDelta >= 360.0f)
|
||||
return Clamp0360(target);
|
||||
|
||||
float result = Clamp0360(current);
|
||||
current = result;
|
||||
target = Clamp0360(target);
|
||||
|
||||
if (current > target)
|
||||
{
|
||||
if (current - target < 180.0f)
|
||||
result -= Mathf.Min(current - target, Mathf.Abs(maxDegreesDelta));
|
||||
else
|
||||
result += Mathf.Min(target + 360.0f - current, Mathf.Abs(maxDegreesDelta));
|
||||
}
|
||||
else
|
||||
{
|
||||
if (target - current < 180.0f)
|
||||
result += Mathf.Min(target - current, Mathf.Abs(maxDegreesDelta));
|
||||
else
|
||||
result -= Mathf.Min(current + 360.0f - target, Mathf.Abs(maxDegreesDelta));
|
||||
}
|
||||
|
||||
return Clamp0360(result);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Frame Rate Independent Damping.
|
||||
/// Source: https://www.rorydriscoll.com/2016/03/07/frame-rate-independent-damping-using-lerp/
|
||||
/// </summary>
|
||||
|
||||
public static float Damp(float a, float b, float lambda, float dt)
|
||||
{
|
||||
return Mathf.Lerp(a, b, 1.0f - Mathf.Exp(-lambda * dt));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Frame Rate Independent Damping.
|
||||
/// Source: https://www.rorydriscoll.com/2016/03/07/frame-rate-independent-damping-using-lerp/
|
||||
/// </summary>
|
||||
|
||||
public static Vector3 Damp(Vector3 a, Vector3 b, float lambda, float dt)
|
||||
{
|
||||
return Vector3.Lerp(a, b, 1.0f - Mathf.Exp(-lambda * dt));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ff183616129658748b90dd05d0186c8d
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -1,8 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a2eecdf9af0466e479f87cb2d561ce94
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,11 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8a5e05c5b0509214d911dd005a610d9a
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -1,401 +0,0 @@
|
||||
using UnityEngine;
|
||||
using UnityEngine.AI;
|
||||
|
||||
namespace ECM2
|
||||
{
|
||||
/// <summary>
|
||||
/// This component extends a Character (through composition) adding navigation capabilities using a NavMeshAgent.
|
||||
/// This replaces the previous AgentCharacter class.
|
||||
/// </summary>
|
||||
|
||||
[RequireComponent(typeof(Character)), RequireComponent(typeof(NavMeshAgent))]
|
||||
public class NavMeshCharacter : MonoBehaviour
|
||||
{
|
||||
#region EDITOR EXPOSED FIELDS
|
||||
|
||||
[Space(15f)]
|
||||
[Tooltip("Should the agent brake automatically to avoid overshooting the destination point? \n" +
|
||||
"If true, the agent will brake automatically as it nears the destination.")]
|
||||
[SerializeField]
|
||||
private bool _autoBraking;
|
||||
|
||||
[Tooltip("Distance from target position to start braking.")]
|
||||
[SerializeField]
|
||||
private float _brakingDistance;
|
||||
|
||||
[Tooltip("Stop within this distance from the target position.")]
|
||||
[SerializeField]
|
||||
private float _stoppingDistance;
|
||||
|
||||
#endregion
|
||||
|
||||
#region FIELDS
|
||||
|
||||
private NavMeshAgent _agent;
|
||||
private Character _character;
|
||||
|
||||
#endregion
|
||||
|
||||
#region PROPERTIES
|
||||
|
||||
/// <summary>
|
||||
/// Cached NavMeshAgent component.
|
||||
/// </summary>
|
||||
|
||||
public NavMeshAgent agent => _agent;
|
||||
|
||||
/// <summary>
|
||||
/// Cached Character component.
|
||||
/// </summary>
|
||||
|
||||
public Character character => _character;
|
||||
|
||||
/// <summary>
|
||||
/// Should the agent brake automatically to avoid overshooting the destination point?
|
||||
/// If this property is set to true, the agent will brake automatically as it nears the destination.
|
||||
/// </summary>
|
||||
|
||||
public bool autoBraking
|
||||
{
|
||||
get => _autoBraking;
|
||||
set
|
||||
{
|
||||
_autoBraking = value;
|
||||
agent.autoBraking = _autoBraking;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Distance from target position to start braking.
|
||||
/// </summary>
|
||||
|
||||
public float brakingDistance
|
||||
{
|
||||
get => _brakingDistance;
|
||||
set => _brakingDistance = Mathf.Max(0.0001f, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The ratio (0 - 1 range) of the agent's remaining distance and the braking distance.
|
||||
/// 1 If no auto braking or if agent's remaining distance is greater than brakingDistance.
|
||||
/// less than 1, if agent's remaining distance is less than brakingDistance.
|
||||
/// </summary>
|
||||
|
||||
public float brakingRatio
|
||||
{
|
||||
get
|
||||
{
|
||||
if (!autoBraking)
|
||||
return 1f;
|
||||
|
||||
return agent.hasPath ? Mathf.InverseLerp(0.0f, brakingDistance, agent.remainingDistance) : 1.0f;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Stop within this distance from the target position.
|
||||
/// </summary>
|
||||
|
||||
public float stoppingDistance
|
||||
{
|
||||
get => _stoppingDistance;
|
||||
set
|
||||
{
|
||||
_stoppingDistance = Mathf.Max(0.0f, value);
|
||||
agent.stoppingDistance = _stoppingDistance;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region EVENTS
|
||||
|
||||
public delegate void DestinationReachedEventHandler();
|
||||
|
||||
/// <summary>
|
||||
/// Event triggered when agent reaches its destination.
|
||||
/// </summary>
|
||||
|
||||
public event DestinationReachedEventHandler DestinationReached;
|
||||
|
||||
/// <summary>
|
||||
/// Trigger DestinationReached event.
|
||||
/// Called when agent reaches its destination.
|
||||
/// </summary>
|
||||
|
||||
public virtual void OnDestinationReached()
|
||||
{
|
||||
DestinationReached?.Invoke();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region METHODS
|
||||
|
||||
/// <summary>
|
||||
/// Cache used components.
|
||||
/// </summary>
|
||||
|
||||
protected virtual void CacheComponents()
|
||||
{
|
||||
_agent = GetComponent<NavMeshAgent>();
|
||||
_character = GetComponent<Character>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Does the Agent currently has a path?
|
||||
/// </summary>
|
||||
|
||||
public virtual bool HasPath()
|
||||
{
|
||||
return agent.hasPath;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// True if Agent is following a path, false otherwise.
|
||||
/// </summary>
|
||||
|
||||
public virtual bool IsPathFollowing()
|
||||
{
|
||||
return agent.hasPath && !agent.isStopped;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the destination set for this agent.
|
||||
/// If a destination is set but the path is not yet processed,
|
||||
/// the returned position will be valid navmesh position that's closest to the previously set position.
|
||||
/// If the agent has no path or requested path - returns the agents position on the navmesh.
|
||||
/// If the agent is not mapped to the navmesh (e.g. Scene has no navmesh) - returns a position at infinity.
|
||||
/// </summary>
|
||||
|
||||
public virtual Vector3 GetDestination()
|
||||
{
|
||||
return agent.destination;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Requests the character to move to the valid navmesh position that's closest to the requested destination.
|
||||
/// </summary>
|
||||
|
||||
public virtual void MoveToDestination(Vector3 destination)
|
||||
{
|
||||
Vector3 worldUp = -character.GetGravityDirection();
|
||||
Vector3 toDestination2D = Vector3.ProjectOnPlane(destination - character.position, worldUp);
|
||||
|
||||
if (toDestination2D.sqrMagnitude >= MathLib.Square(stoppingDistance))
|
||||
agent.SetDestination(destination);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Pause / Resume Character path following movement.
|
||||
/// If set to True, the character's movement will be stopped along its current path.
|
||||
/// If set to False after the character has stopped, it will resume moving along its current path.
|
||||
/// </summary>
|
||||
|
||||
public virtual void PauseMovement(bool pause)
|
||||
{
|
||||
agent.isStopped = pause;
|
||||
character.SetMovementDirection(Vector3.zero);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Halts Character's current path following movement.
|
||||
/// This will clear agent's current path.
|
||||
/// </summary>
|
||||
|
||||
public virtual void StopMovement()
|
||||
{
|
||||
agent.ResetPath();
|
||||
character.SetMovementDirection(Vector3.zero);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Computes the analog input modifier (0.0f to 1.0f) based on Character's max speed and given desired velocity.
|
||||
/// </summary>
|
||||
|
||||
protected virtual float ComputeAnalogInputModifier(Vector3 desiredVelocity)
|
||||
{
|
||||
float maxSpeed = _character.GetMaxSpeed();
|
||||
if (desiredVelocity.sqrMagnitude > 0.0f && maxSpeed > 0.00000001f)
|
||||
return Mathf.Clamp01(desiredVelocity.magnitude / maxSpeed);
|
||||
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Calculates Character movement direction from a given desired velocity factoring (if enabled) auto braking.
|
||||
/// </summary>
|
||||
|
||||
protected virtual Vector3 CalcMovementDirection(Vector3 desiredVelocity)
|
||||
{
|
||||
Vector3 worldUp = -character.GetGravityDirection();
|
||||
Vector3 desiredVelocity2D = Vector3.ProjectOnPlane(desiredVelocity, worldUp);
|
||||
|
||||
Vector3 scaledDesiredVelocity2D = desiredVelocity2D * brakingRatio;
|
||||
|
||||
float minAnalogSpeed = _character.GetMinAnalogSpeed();
|
||||
if (scaledDesiredVelocity2D.sqrMagnitude < MathLib.Square(minAnalogSpeed))
|
||||
scaledDesiredVelocity2D = scaledDesiredVelocity2D.normalized * minAnalogSpeed;
|
||||
|
||||
return Vector3.ClampMagnitude(scaledDesiredVelocity2D, ComputeAnalogInputModifier(scaledDesiredVelocity2D));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Makes the character's follow Agent's path (if any).
|
||||
/// Eg: Keep updating Character's movement direction vector to steer towards Agent's destination until reached.
|
||||
/// </summary>
|
||||
|
||||
protected virtual void DoPathFollowing()
|
||||
{
|
||||
if (!IsPathFollowing())
|
||||
return;
|
||||
|
||||
// Is destination reached ?
|
||||
|
||||
if (agent.remainingDistance <= stoppingDistance)
|
||||
{
|
||||
// Destination is reached, stop movement
|
||||
|
||||
StopMovement();
|
||||
|
||||
// Trigger event
|
||||
|
||||
OnDestinationReached();
|
||||
}
|
||||
else
|
||||
{
|
||||
// If destination not reached, request a Character to move towards agent's desired velocity direction
|
||||
|
||||
Vector3 movementDirection = CalcMovementDirection(agent.desiredVelocity);
|
||||
character.SetMovementDirection(movementDirection);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Synchronize the NavMeshAgent with Character (eg: speed, acceleration, velocity, etc) as we moves the Agent.
|
||||
/// </summary>
|
||||
|
||||
protected virtual void SyncNavMeshAgent()
|
||||
{
|
||||
agent.angularSpeed = _character.rotationRate;
|
||||
|
||||
agent.speed = _character.GetMaxSpeed();
|
||||
agent.acceleration = _character.GetMaxAcceleration();
|
||||
|
||||
agent.velocity = _character.GetVelocity();
|
||||
agent.nextPosition = _character.GetPosition();
|
||||
|
||||
agent.radius = _character.radius;
|
||||
agent.height = _character.height;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// On MovementMode change, stop agent movement if character is not walking or falling.
|
||||
/// </summary>
|
||||
|
||||
protected virtual void OnMovementModeChanged(Character.MovementMode prevMovementMode, int prevCustomMovementMode)
|
||||
{
|
||||
if (!character.IsWalking() || !character.IsFalling())
|
||||
{
|
||||
StopMovement();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// While Character has a valid path, do path following.
|
||||
/// </summary>
|
||||
|
||||
protected virtual void OnBeforeSimulationUpdated(float deltaTime)
|
||||
{
|
||||
DoPathFollowing();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region MONOBEHAVIOUR
|
||||
|
||||
/// <summary>
|
||||
/// If overriden, base method MUST be called.
|
||||
/// </summary>
|
||||
|
||||
private void Reset()
|
||||
{
|
||||
_autoBraking = true;
|
||||
|
||||
_brakingDistance = 2.0f;
|
||||
_stoppingDistance = 1.0f;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// If overriden, base method MUST be called.
|
||||
/// </summary>
|
||||
|
||||
private void OnValidate()
|
||||
{
|
||||
if (_agent == null)
|
||||
_agent = GetComponent<NavMeshAgent>();
|
||||
|
||||
brakingDistance = _brakingDistance;
|
||||
stoppingDistance = _stoppingDistance;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// If overriden, base method MUST be called.
|
||||
/// </summary>
|
||||
|
||||
protected virtual void Awake()
|
||||
{
|
||||
// Cache used components
|
||||
|
||||
CacheComponents();
|
||||
|
||||
// Initialize NavMeshAgent
|
||||
|
||||
agent.autoBraking = autoBraking;
|
||||
agent.stoppingDistance = stoppingDistance;
|
||||
|
||||
// Turn-off NavMeshAgent auto-control,
|
||||
// we control it (see SyncNavMeshAgent method)
|
||||
|
||||
agent.updatePosition = false;
|
||||
agent.updateRotation = false;
|
||||
|
||||
agent.updateUpAxis = false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// If overriden, base method MUST be called.
|
||||
/// </summary>
|
||||
|
||||
protected virtual void OnEnable()
|
||||
{
|
||||
// Subscribe to Character events
|
||||
|
||||
character.MovementModeChanged += OnMovementModeChanged;
|
||||
character.BeforeSimulationUpdated += OnBeforeSimulationUpdated;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// If overriden, base method MUST be called.
|
||||
/// </summary>
|
||||
|
||||
protected virtual void OnDisable()
|
||||
{
|
||||
// Un-Subscribe to Character events
|
||||
|
||||
character.MovementModeChanged -= OnMovementModeChanged;
|
||||
character.BeforeSimulationUpdated -= OnBeforeSimulationUpdated;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// If overriden, base method MUST be called.
|
||||
/// </summary>
|
||||
|
||||
protected virtual void LateUpdate()
|
||||
{
|
||||
SyncNavMeshAgent();
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0b8353732406ae7418b47f5928e333c6
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -1,142 +0,0 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace ECM2
|
||||
{
|
||||
/// <summary>
|
||||
/// Helper component used to define physics volumes like water, air, oil, etc.
|
||||
/// Characters will react according to this settings when inside this volume.
|
||||
/// </summary>
|
||||
|
||||
[RequireComponent(typeof(BoxCollider))]
|
||||
public class PhysicsVolume : MonoBehaviour
|
||||
{
|
||||
#region EDITOR EXPOSED FIELDS
|
||||
|
||||
[Tooltip("Determines which PhysicsVolume takes precedence if they overlap (higher value == higher priority).")]
|
||||
[SerializeField]
|
||||
private int _priority;
|
||||
|
||||
[Tooltip("Determines the amount of friction applied by the volume as Character using CharacterMovement moves through it.\n" +
|
||||
"The higher this value, the harder it will feel to move through the volume.")]
|
||||
[SerializeField]
|
||||
private float _friction;
|
||||
|
||||
[Tooltip("Determines the terminal velocity of Characters using CharacterMovement when falling.")]
|
||||
[SerializeField]
|
||||
private float _maxFallSpeed;
|
||||
|
||||
[Tooltip("Determines if the volume contains a fluid, like water.")]
|
||||
[SerializeField]
|
||||
private bool _waterVolume;
|
||||
|
||||
#endregion
|
||||
|
||||
#region FIELDS
|
||||
|
||||
private BoxCollider _collider;
|
||||
|
||||
#endregion
|
||||
|
||||
#region PROPERTIES
|
||||
|
||||
/// <summary>
|
||||
/// This volume collider (trigger).
|
||||
/// </summary>
|
||||
|
||||
public BoxCollider boxCollider
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_collider == null)
|
||||
_collider = GetComponent<BoxCollider>();
|
||||
|
||||
return _collider;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines which PhysicsVolume takes precedence if they overlap (higher value == higher priority).
|
||||
/// </summary>
|
||||
|
||||
public int priority
|
||||
{
|
||||
get => _priority;
|
||||
set => _priority = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines the amount of friction applied by the volume as Character's using CharacterMovement move through it.
|
||||
/// The higher this value, the harder it will feel to move through the volume.
|
||||
/// </summary>
|
||||
|
||||
public float friction
|
||||
{
|
||||
get => _friction;
|
||||
set => _friction = Mathf.Max(0.0f, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines the terminal velocity of Character's using CharacterMovement when falling.
|
||||
/// </summary>
|
||||
|
||||
public float maxFallSpeed
|
||||
{
|
||||
get => _maxFallSpeed;
|
||||
set => _maxFallSpeed = Mathf.Max(0.0f, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines if the volume contains a fluid, like water.
|
||||
/// </summary>
|
||||
|
||||
public bool waterVolume
|
||||
{
|
||||
get => _waterVolume;
|
||||
set => _waterVolume = value;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region METHODS
|
||||
|
||||
protected virtual void OnReset()
|
||||
{
|
||||
priority = 0;
|
||||
friction = 0.5f;
|
||||
maxFallSpeed = 40.0f;
|
||||
waterVolume = true;
|
||||
}
|
||||
|
||||
protected virtual void OnOnValidate()
|
||||
{
|
||||
friction = _friction;
|
||||
maxFallSpeed = _maxFallSpeed;
|
||||
}
|
||||
|
||||
protected virtual void OnAwake()
|
||||
{
|
||||
boxCollider.isTrigger = true;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region MONOBEHAVIOUR
|
||||
|
||||
private void Reset()
|
||||
{
|
||||
OnReset();
|
||||
}
|
||||
|
||||
private void OnValidate()
|
||||
{
|
||||
OnOnValidate();
|
||||
}
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
OnAwake();
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4a7a95050e56c7e4f976c906ecd1a941
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -1,113 +0,0 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace ECM2
|
||||
{
|
||||
/// <summary>
|
||||
/// The slope behaviour for attached collider.
|
||||
/// </summary>
|
||||
|
||||
public enum SlopeBehaviour
|
||||
{
|
||||
Default,
|
||||
|
||||
/// <summary>
|
||||
/// Sets the collider as walkable.
|
||||
/// </summary>
|
||||
|
||||
Walkable,
|
||||
|
||||
/// <summary>
|
||||
/// Sets the collider as not walkable.
|
||||
/// </summary>
|
||||
|
||||
NotWalkable,
|
||||
|
||||
/// <summary>
|
||||
/// Let you specify a custom slope limit value for collider.
|
||||
/// </summary>
|
||||
|
||||
Override
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Overrides a CharacterMovement SlopeLimit property allowing to define per-object behaviour instead of per face.
|
||||
/// This enable you to tweak what surfaces Characters can walk up. Perhaps a stair case is too steep or
|
||||
/// maybe you want to enforce the "no walking on the grass" signs, these settings will enable you to do so.
|
||||
/// </summary>
|
||||
|
||||
public sealed class SlopeLimitBehaviour : MonoBehaviour
|
||||
{
|
||||
#region EDITOR EXPOSED FIELDS
|
||||
|
||||
[Tooltip("The desired behaviour.")]
|
||||
[SerializeField]
|
||||
private SlopeBehaviour _slopeBehaviour = SlopeBehaviour.Default;
|
||||
|
||||
[SerializeField]
|
||||
private float _slopeLimit;
|
||||
|
||||
#endregion
|
||||
|
||||
#region FIELDS
|
||||
|
||||
[SerializeField, HideInInspector]
|
||||
private float _slopeLimitCos;
|
||||
|
||||
#endregion
|
||||
|
||||
#region PROPERTIES
|
||||
|
||||
/// <summary>
|
||||
/// The current behaviour.
|
||||
/// </summary>
|
||||
|
||||
public SlopeBehaviour walkableSlopeBehaviour
|
||||
{
|
||||
get => _slopeBehaviour;
|
||||
set => _slopeBehaviour = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The slope limit angle in degrees.
|
||||
/// </summary>
|
||||
|
||||
public float slopeLimit
|
||||
{
|
||||
get => _slopeLimit;
|
||||
|
||||
set
|
||||
{
|
||||
_slopeLimit = Mathf.Clamp(value, 0.0f, 89.0f);
|
||||
|
||||
_slopeLimitCos = Mathf.Cos(_slopeLimit * Mathf.Deg2Rad);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The cosine of slope angle (in radians), this is used to faster angle tests (e.g. dotProduct > slopeLimitCos)
|
||||
/// </summary>
|
||||
|
||||
public float slopeLimitCos
|
||||
{
|
||||
get => _slopeLimitCos;
|
||||
|
||||
set
|
||||
{
|
||||
_slopeLimitCos = Mathf.Clamp01(value);
|
||||
|
||||
_slopeLimit = Mathf.Clamp(Mathf.Acos(_slopeLimitCos) * Mathf.Rad2Deg, 0.0f, 89.0f);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region MONOBEHAVIOUR
|
||||
|
||||
private void OnValidate()
|
||||
{
|
||||
slopeLimit = _slopeLimit;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2843c82a9ca85424b925fc897391f62d
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -1,14 +0,0 @@
|
||||
{
|
||||
"name": "ECM2",
|
||||
"rootNamespace": "ECM2",
|
||||
"references": [],
|
||||
"includePlatforms": [],
|
||||
"excludePlatforms": [],
|
||||
"allowUnsafeCode": false,
|
||||
"overrideReferences": false,
|
||||
"precompiledReferences": [],
|
||||
"autoReferenced": true,
|
||||
"defineConstraints": [],
|
||||
"versionDefines": [],
|
||||
"noEngineReferences": false
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 659d7efc2734dfe4babc4144eff2294d
|
||||
AssemblyDefinitionImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -1,8 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8be59010d2e089f4ab0b4354b30ee000
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -1,118 +0,0 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace ECM2
|
||||
{
|
||||
/// <summary>
|
||||
///
|
||||
/// RootMotionController.
|
||||
///
|
||||
/// Helper component to get Animator root motion velocity vector (animRootMotionVelocity).
|
||||
///
|
||||
/// This must be attached to a game object with an Animator component.
|
||||
/// </summary>
|
||||
|
||||
[RequireComponent(typeof(Animator))]
|
||||
public class RootMotionController : MonoBehaviour
|
||||
{
|
||||
#region FIELDS
|
||||
|
||||
protected Animator _animator;
|
||||
|
||||
protected Vector3 _rootMotionDeltaPosition;
|
||||
protected Quaternion _rootMotionDeltaRotation;
|
||||
|
||||
#endregion
|
||||
|
||||
#region METHOD
|
||||
|
||||
/// <summary>
|
||||
/// Flush any accumulated deltas. This prevents accumulation while character is toggling root motion.
|
||||
/// </summary>
|
||||
|
||||
public virtual void FlushAccumulatedDeltas()
|
||||
{
|
||||
_rootMotionDeltaPosition = Vector3.zero;
|
||||
_rootMotionDeltaRotation = Quaternion.identity;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Return current root motion rotation and clears accumulated delta rotation.
|
||||
/// </summary>
|
||||
|
||||
public virtual Quaternion ConsumeRootMotionRotation()
|
||||
{
|
||||
Quaternion rootMotionRotation = _rootMotionDeltaRotation;
|
||||
_rootMotionDeltaRotation = Quaternion.identity;
|
||||
|
||||
return rootMotionRotation;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the calculated root motion velocity.
|
||||
/// </summary>
|
||||
|
||||
public virtual Vector3 GetRootMotionVelocity(float deltaTime)
|
||||
{
|
||||
if (deltaTime == 0.0f)
|
||||
return Vector3.zero;
|
||||
|
||||
Vector3 rootMotionVelocity = _rootMotionDeltaPosition / deltaTime;
|
||||
return rootMotionVelocity;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Return current root motion velocity and clears accumulated delta positions.
|
||||
/// </summary>
|
||||
|
||||
public virtual Vector3 ConsumeRootMotionVelocity(float deltaTime)
|
||||
{
|
||||
Vector3 rootMotionVelocity = GetRootMotionVelocity(deltaTime);
|
||||
_rootMotionDeltaPosition = Vector3.zero;
|
||||
|
||||
return rootMotionVelocity;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region MONOBEHAVIOUR
|
||||
|
||||
/// <summary>
|
||||
/// If overriden, base method MUST be called.
|
||||
/// </summary>
|
||||
|
||||
public virtual void Awake()
|
||||
{
|
||||
_animator = GetComponent<Animator>();
|
||||
|
||||
if (_animator == null)
|
||||
{
|
||||
Debug.LogError($"RootMotionController: There is no 'Animator' attached to the '{name}' game object.\n" +
|
||||
$"Please attach a 'Animator' to the '{name}' game object");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// If overriden, base method MUST be called.
|
||||
/// </summary>
|
||||
|
||||
public virtual void Start()
|
||||
{
|
||||
_rootMotionDeltaPosition = Vector3.zero;
|
||||
_rootMotionDeltaRotation = Quaternion.identity;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// If overriden, base method MUST be called.
|
||||
/// </summary>
|
||||
|
||||
public virtual void OnAnimatorMove()
|
||||
{
|
||||
// Accumulate root motion deltas between character updates
|
||||
|
||||
_rootMotionDeltaPosition += _animator.deltaPosition;
|
||||
_rootMotionDeltaRotation = _animator.deltaRotation * _rootMotionDeltaRotation;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -1,3 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 838519e5d02343249e0d4ddd7578cba4
|
||||
timeCreated: 1603243744
|
||||
@@ -1,3 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8990a943a41a4051a9e2b542066a7bd5
|
||||
timeCreated: 1700619077
|
||||
@@ -1,14 +0,0 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace ECM2
|
||||
{
|
||||
public interface IColliderFilter
|
||||
{
|
||||
/// <summary>
|
||||
/// Determines if the given collider should be filtered (eg: ignored).
|
||||
/// Return true if should be filtered, false otherwise.
|
||||
/// </summary>
|
||||
|
||||
bool Filter(Collider otherCollider);
|
||||
}
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ab476561401c4b644965e59c1c307e27
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -1,8 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2140f7c214e8462448458ec61e2435dd
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -1,171 +0,0 @@
|
||||
using System;
|
||||
using UnityEngine;
|
||||
|
||||
namespace ECM2
|
||||
{
|
||||
/// <summary>
|
||||
/// General purpose collision detection functions.
|
||||
/// Lets you filter results implementing the IColliderFilter interface.
|
||||
/// </summary>
|
||||
|
||||
public static class CollisionDetection
|
||||
{
|
||||
#region CONSTANTS
|
||||
|
||||
private const int kMaxHits = 8;
|
||||
|
||||
#endregion
|
||||
|
||||
#region FIELDS
|
||||
|
||||
private static readonly RaycastHit[] HitsBuffer = new RaycastHit[kMaxHits];
|
||||
|
||||
#endregion
|
||||
|
||||
#region METHODS
|
||||
|
||||
public static int Raycast(Vector3 origin, Vector3 direction, float distance, int layerMask,
|
||||
QueryTriggerInteraction queryTriggerInteraction, out RaycastHit closestHit, RaycastHit[] hits,
|
||||
IColliderFilter colliderFilter)
|
||||
{
|
||||
closestHit = default;
|
||||
|
||||
int rawHitCount = Physics.RaycastNonAlloc(origin, direction, HitsBuffer, distance, layerMask,
|
||||
queryTriggerInteraction);
|
||||
|
||||
if (rawHitCount == 0)
|
||||
return 0;
|
||||
|
||||
int filteredHitCount = 0;
|
||||
float closestDistance = Mathf.Infinity;
|
||||
|
||||
Array.Clear(hits, 0, hits.Length);
|
||||
|
||||
for (int i = 0; i < rawHitCount; i++)
|
||||
{
|
||||
if (HitsBuffer[i].distance <= 0.0f ||
|
||||
colliderFilter != null && colliderFilter.Filter(HitsBuffer[i].collider))
|
||||
continue;
|
||||
|
||||
if (HitsBuffer[i].distance < closestDistance)
|
||||
{
|
||||
closestHit = HitsBuffer[i];
|
||||
closestDistance = closestHit.distance;
|
||||
}
|
||||
|
||||
hits[filteredHitCount++] = HitsBuffer[i];
|
||||
}
|
||||
|
||||
return filteredHitCount;
|
||||
}
|
||||
|
||||
public static int SphereCast(Vector3 origin, float radius, Vector3 direction, float distance, int layerMask,
|
||||
QueryTriggerInteraction queryTriggerInteraction, out RaycastHit closestHit, RaycastHit[] hits,
|
||||
IColliderFilter colliderFilter, float backStepDistance)
|
||||
{
|
||||
closestHit = default;
|
||||
|
||||
Vector3 optOrigin = origin - direction * backStepDistance;
|
||||
float optDistance = distance + backStepDistance;
|
||||
|
||||
int rawHitCount = Physics.SphereCastNonAlloc(optOrigin, radius, direction, HitsBuffer, optDistance,
|
||||
layerMask, queryTriggerInteraction);
|
||||
|
||||
if (rawHitCount == 0)
|
||||
return 0;
|
||||
|
||||
int filteredHitCount = 0;
|
||||
float closestDistance = Mathf.Infinity;
|
||||
|
||||
Array.Clear(hits, 0, hits.Length);
|
||||
|
||||
for (int i = 0; i < rawHitCount; i++)
|
||||
{
|
||||
if (HitsBuffer[i].distance <= 0.0f ||
|
||||
colliderFilter != null && colliderFilter.Filter(HitsBuffer[i].collider))
|
||||
continue;
|
||||
|
||||
HitsBuffer[i].distance -= backStepDistance;
|
||||
|
||||
if (HitsBuffer[i].distance < closestDistance)
|
||||
{
|
||||
closestHit = HitsBuffer[i];
|
||||
closestDistance = closestHit.distance;
|
||||
}
|
||||
|
||||
hits[filteredHitCount++] = HitsBuffer[i];
|
||||
}
|
||||
|
||||
return filteredHitCount;
|
||||
}
|
||||
|
||||
public static int CapsuleCast(Vector3 point1, Vector3 point2, float radius, Vector3 direction, float distance,
|
||||
int layerMask, QueryTriggerInteraction queryTriggerInteraction, out RaycastHit closestHit,
|
||||
RaycastHit[] hits, IColliderFilter colliderFilter, float backStepDistance)
|
||||
{
|
||||
closestHit = default;
|
||||
|
||||
Vector3 optPoint1 = point1 - direction * backStepDistance;
|
||||
Vector3 optPoint2 = point2 - direction * backStepDistance;
|
||||
|
||||
float optDistance = distance + backStepDistance;
|
||||
|
||||
int rawHitCount = Physics.CapsuleCastNonAlloc(optPoint1, optPoint2, radius, direction, HitsBuffer,
|
||||
optDistance, layerMask, queryTriggerInteraction);
|
||||
|
||||
if (rawHitCount == 0)
|
||||
return 0;
|
||||
|
||||
int filteredHitCount = 0;
|
||||
float closestDistance = Mathf.Infinity;
|
||||
|
||||
Array.Clear(hits, 0, hits.Length);
|
||||
|
||||
for (int i = 0; i < rawHitCount; i++)
|
||||
{
|
||||
if (HitsBuffer[i].distance <= 0.0f ||
|
||||
colliderFilter != null && colliderFilter.Filter(HitsBuffer[i].collider))
|
||||
continue;
|
||||
|
||||
HitsBuffer[i].distance -= backStepDistance;
|
||||
|
||||
if (HitsBuffer[i].distance < closestDistance)
|
||||
{
|
||||
closestHit = HitsBuffer[i];
|
||||
closestDistance = closestHit.distance;
|
||||
}
|
||||
|
||||
hits[filteredHitCount++] = HitsBuffer[i];
|
||||
}
|
||||
|
||||
return filteredHitCount;
|
||||
}
|
||||
|
||||
public static int OverlapCapsule(Vector3 point1, Vector3 point2, float radius, int layerMask,
|
||||
QueryTriggerInteraction queryTriggerInteraction, Collider[] results, IColliderFilter colliderFilter)
|
||||
{
|
||||
int rawOverlapCount =
|
||||
Physics.OverlapCapsuleNonAlloc(point1, point2, radius, results, layerMask, queryTriggerInteraction);
|
||||
|
||||
if (rawOverlapCount == 0)
|
||||
return 0;
|
||||
|
||||
int filteredOverlapCount = rawOverlapCount;
|
||||
|
||||
for (int i = 0; i < rawOverlapCount; i++)
|
||||
{
|
||||
Collider overlappedCollider = results[i];
|
||||
|
||||
if (colliderFilter != null && !colliderFilter.Filter(overlappedCollider))
|
||||
continue;
|
||||
|
||||
if (i < --filteredOverlapCount)
|
||||
results[i] = results[filteredOverlapCount];
|
||||
}
|
||||
|
||||
return filteredOverlapCount;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -1,3 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3ea13ecaa42445519734922e43f938f2
|
||||
timeCreated: 1700619165
|
||||
@@ -1,99 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Rendering;
|
||||
|
||||
namespace ECM2
|
||||
{
|
||||
public static class MeshUtility
|
||||
{
|
||||
private const int kMaxVertices = 1024;
|
||||
private const int kMaxTriangles = kMaxVertices * 3;
|
||||
|
||||
private static readonly List<Vector3> _vertices = new List<Vector3>(kMaxVertices);
|
||||
|
||||
private static readonly List<ushort> _triangles16 = new List<ushort>(kMaxTriangles);
|
||||
private static readonly List<int> _triangles32 = new List<int>();
|
||||
|
||||
private static readonly List<ushort> _scratchBuffer16 = new List<ushort>(kMaxTriangles);
|
||||
private static readonly List<int> _scratchBuffer32 = new List<int>();
|
||||
|
||||
public static Vector3 FindMeshOpposingNormal(Mesh sharedMesh, ref RaycastHit inHit)
|
||||
{
|
||||
Vector3 v0, v1, v2;
|
||||
|
||||
if (sharedMesh.indexFormat == IndexFormat.UInt16)
|
||||
{
|
||||
_triangles16.Clear();
|
||||
|
||||
int subMeshCount = sharedMesh.subMeshCount;
|
||||
if (subMeshCount == 1)
|
||||
sharedMesh.GetTriangles(_triangles16, 0);
|
||||
else
|
||||
{
|
||||
for (int i = 0; i < subMeshCount; i++)
|
||||
{
|
||||
sharedMesh.GetTriangles(_scratchBuffer16, i);
|
||||
|
||||
_triangles16.AddRange(_scratchBuffer16);
|
||||
}
|
||||
}
|
||||
|
||||
sharedMesh.GetVertices(_vertices);
|
||||
|
||||
v0 = _vertices[_triangles16[inHit.triangleIndex * 3 + 0]];
|
||||
v1 = _vertices[_triangles16[inHit.triangleIndex * 3 + 1]];
|
||||
v2 = _vertices[_triangles16[inHit.triangleIndex * 3 + 2]];
|
||||
}
|
||||
else
|
||||
{
|
||||
_triangles32.Clear();
|
||||
|
||||
int subMeshCount = sharedMesh.subMeshCount;
|
||||
if (subMeshCount == 1)
|
||||
sharedMesh.GetTriangles(_triangles32, 0);
|
||||
else
|
||||
{
|
||||
for (int i = 0; i < subMeshCount; i++)
|
||||
{
|
||||
sharedMesh.GetTriangles(_scratchBuffer32, i);
|
||||
|
||||
_triangles32.AddRange(_scratchBuffer32);
|
||||
}
|
||||
}
|
||||
|
||||
sharedMesh.GetVertices(_vertices);
|
||||
|
||||
v0 = _vertices[_triangles32[inHit.triangleIndex * 3 + 0]];
|
||||
v1 = _vertices[_triangles32[inHit.triangleIndex * 3 + 1]];
|
||||
v2 = _vertices[_triangles32[inHit.triangleIndex * 3 + 2]];
|
||||
}
|
||||
|
||||
Matrix4x4 mtx = inHit.transform.localToWorldMatrix;
|
||||
|
||||
Vector3 p0 = mtx.MultiplyPoint3x4(v0);
|
||||
Vector3 p1 = mtx.MultiplyPoint3x4(v1);
|
||||
Vector3 p2 = mtx.MultiplyPoint3x4(v2);
|
||||
|
||||
Vector3 u = p1 - p0;
|
||||
Vector3 v = p2 - p0;
|
||||
|
||||
Vector3 worldNormal = Vector3.Cross(u, v).normalized;
|
||||
|
||||
if (Vector3.Dot(worldNormal, inHit.normal) < 0.0f)
|
||||
worldNormal = Vector3.Cross(v, u).normalized;
|
||||
|
||||
return worldNormal;
|
||||
}
|
||||
|
||||
public static void FlushBuffers()
|
||||
{
|
||||
_vertices.Clear();
|
||||
|
||||
_scratchBuffer16.Clear();
|
||||
_scratchBuffer32.Clear();
|
||||
|
||||
_triangles16.Clear();
|
||||
_triangles32.Clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 740062629b0d7924ca2d9b9f8ff3318c
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Reference in New Issue
Block a user