Files
2024-11-20 15:21:28 +01:00

287 lines
11 KiB
C#

using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.AI;
namespace Invector.vCharacterController.AI
{
[SelectionBase]
[RequireComponent(typeof(BoxCollider))]
[vClassHeader("AI Cover Point", openClose = false)]
public class vAICoverPoint : vMonoBehaviour
{
[System.Serializable]
public class CoverEvent : UnityEngine.Events.UnityEvent<vAICoverPoint,GameObject> { };
[Tooltip("Auto assign the properties : Corner enum, LeftCorner and RightCorner ")]
public bool autoDetectCorner = true;
public LayerMask mask = 1 << 0;
[System.Flags]
public enum Corner
{
Left =1,Right=2
}
[vEnumFlag]
public Corner corner ;
public float offsetPosePositionX;
public float rayCastNeighborOffsetX =.15f;
public vAICoverPoint leftCorner, rightCorner;
public vAICoverPoint left, right;
public CoverEvent onEnterCover;
public bool showRays;
RaycastHit hit;
float space => ((boxCollider.size.z ));
PhysicsScene physics;
public bool isValid;
private void Awake()
{
if (boxCollider) boxCollider.isTrigger = true;
}
private IEnumerator Start()
{
gameObject.SetActive(isValid =CheckPosePositionInNavMesh());
yield return new WaitForEndOfFrame();
CheckConnections();
}
public virtual bool CheckPosePositionInNavMesh()
{
NavMeshHit hit;
return NavMesh.SamplePosition(posePosition, out hit, .1f, NavMesh.AllAreas);
}
public void CheckConnections()
{
if (autoDetectCorner) corner = (Corner)(0);
mask = 1 << 0 | 1 << gameObject.layer;
if (gameObject.scene == null) return;
physics = PhysicsSceneExtensions.GetPhysicsScene(gameObject.scene);
CheckLeftConnection();
CheckRightConnection();
}
private void CheckRightConnection()
{
bool isRight = false;
vAICoverPoint _possibleRight = null;
if (RayCastConnections(transform.position + transform.TransformDirection(boxCollider.center) + transform.right*boxCollider.size.x* rayCastNeighborOffsetX , -transform.right, out hit, boxCollider.size.x * (1.1f+rayCastNeighborOffsetX ), mask))
{
_possibleRight = hit.transform.gameObject.GetComponent<vAICoverPoint>();
}
else
{
for (int i = 0; i < 6; i++)
{
var dir = Quaternion.AngleAxis((i * 15), Vector3.up) * -transform.right;
var rayOrigin = boxCollider.bounds.center - transform.right * boxCollider.size.x * 0.45f;
if (RayCastConnections(rayOrigin, dir, out hit, space, mask))
{
_possibleRight = hit.transform.gameObject.GetComponent<vAICoverPoint>();
if (showRays) Debug.DrawLine(rayOrigin, hit.point, Color.red);
if (_possibleRight) break;
}
else if (showRays) Debug.DrawRay(rayOrigin, dir * space, Color.green);
}
if (!_possibleRight)
{
for (int i = 0; i < 6; i++)
{
var dir = Quaternion.AngleAxis(-(i * 15), Vector3.up) * -transform.right;
var rayOrigin = boxCollider.bounds.center - transform.right * boxCollider.size.x * 0.45f;
if (RayCastConnections(rayOrigin, dir, out hit, space, mask))
{
_possibleRight = hit.transform.gameObject.GetComponent<vAICoverPoint>();
if (showRays) Debug.DrawLine(rayOrigin, hit.point, Color.red);
if (_possibleRight) break;
}
else if (showRays) Debug.DrawRay(rayOrigin, dir * space, Color.yellow);
}
}
}
if (_possibleRight)
{
var angle = (_possibleRight.transform.eulerAngles - transform.eulerAngles).NormalizeAngle();
right = angle.y > -75? _possibleRight : null;
isRight = right == null;
rightCorner = right == null ? _possibleRight : null;
}
else
{
right = null;
rightCorner = null;
isRight = true;
}
if (isRight && autoDetectCorner) corner |= Corner.Right;
}
private void CheckLeftConnection()
{
bool isLeft = false;
vAICoverPoint _possibleLeft = null;
if (RayCastConnections(transform.position+transform.TransformDirection(boxCollider.center) - transform.right * boxCollider.size.x * rayCastNeighborOffsetX , transform.right, out hit, boxCollider.size.x * (1.1f + rayCastNeighborOffsetX ), mask))
{
_possibleLeft = hit.transform.gameObject.GetComponent<vAICoverPoint>();
}
else
{
for (int i = 0; i < 6; i++)
{
var dir = Quaternion.AngleAxis(-(i * 15), Vector3.up) * transform.right;
var rayOrigin = boxCollider.bounds.center + transform.right * boxCollider.size.x * 0.45f;
if (RayCastConnections(rayOrigin, dir, out hit, space, mask))
{
_possibleLeft = hit.transform.gameObject.GetComponent<vAICoverPoint>();
if (showRays) Debug.DrawLine(rayOrigin, hit.point, Color.red);
if (_possibleLeft) break;
}
else if (showRays) Debug.DrawRay(rayOrigin, dir*space, Color.green);
}
if (!_possibleLeft)
{
for (int i = 0; i < 6; i++)
{
var dir = Quaternion.AngleAxis((i * 15), Vector3.up) * transform.right;
var rayOrigin = boxCollider.bounds.center + transform.right * boxCollider.size.x * 0.45f;
if (RayCastConnections(rayOrigin, dir, out hit, space, mask))
{
_possibleLeft = hit.transform.gameObject.GetComponent<vAICoverPoint>();
if (showRays) Debug.DrawLine(rayOrigin, hit.point, Color.red);
if (_possibleLeft) break;
}
else if (showRays) Debug.DrawRay(rayOrigin, dir * space, Color.yellow);
}
}
}
if (_possibleLeft)
{
var angle = (_possibleLeft.transform.eulerAngles - transform.eulerAngles).NormalizeAngle();
left = angle.y < 75 ? _possibleLeft : null;
isLeft = left == null;
leftCorner = left == null ? _possibleLeft : null;
}
else
{
left = null;
leftCorner = null;
isLeft = true;
}
if (isLeft && autoDetectCorner) corner |= Corner.Left;
}
private void OnDrawGizmos()
{
if (!Application.isPlaying) isValid = true;
CheckConnections();
var matrix = Gizmos.matrix;
Gizmos.matrix = Matrix4x4.TRS(transform.position + transform.TransformDirection(boxCollider.center) + transform.forward * posePositionZ * 0.45f, transform.rotation, new Vector3(boxCollider.BoxSize().x, boxCollider.BoxSize().y, 0.01f));
Gizmos.color = isValid? Color.white * 0.8f:Color.red*0.8f;
Gizmos.DrawWireCube(Vector3.zero, new Vector3(1, 1, 0));
Gizmos.color = isValid ? Color.white * 0.25f : Color.red * 0.25f;
Gizmos.DrawCube(Vector3.zero, Vector3.one);
Gizmos.matrix = matrix;
}
private void OnDrawGizmosSelected()
{
}
protected virtual bool RayCastConnections(Vector3 rayOrigin,Vector3 dir,out RaycastHit hit,float distance,LayerMask mask)
{
if(Application.isPlaying)
{
return Physics.Raycast(rayOrigin, dir, out hit, distance, mask, QueryTriggerInteraction.Collide);
}
else
{
return physics.Raycast(rayOrigin, dir, out hit, distance, mask, QueryTriggerInteraction.Collide);
}
}
public float posePositionZ => boxCollider? boxCollider.size.z:0f;
public BoxCollider boxCollider
{
get
{
if (_boxCollider == null) _boxCollider = GetComponent<BoxCollider>();
if (_boxCollider == null) _boxCollider = gameObject.AddComponent<BoxCollider>();
return _boxCollider;
}
set
{
_boxCollider = value;
}
}
protected BoxCollider _boxCollider;
public Vector3 posePosition
{
get
{
return transform.position + transform.forward * posePositionZ + transform.right*offsetPosePositionX;
}
}
public bool isOccuped;
public Vector3 rightCornerP
{
get
{
var corners = new Vector3[2];
var p = posePosition;
corners[0] = p - transform.right * (boxCollider.size.z * 1.5f);
corners[1] = rightCorner? rightCorner.posePosition + rightCorner.transform.right * (rightCorner.boxCollider.size.z * 1.5f):corners[0];
return (corners[0]+ corners[1])/2;
}
}
public Vector3 leftCornerP
{
get
{
var corners = new Vector3[2];
var p = posePosition;
corners[0] = p + transform.right * (boxCollider.size.z * 1.5f);
corners[1] = leftCorner ? leftCorner.posePosition - leftCorner.transform.right * (leftCorner.boxCollider.size.z * 1.5f) : corners[0];
return (corners[0] + corners[1]) / 2;
}
}
public void EnterCover(GameObject visitor)
{
onEnterCover.Invoke(this, visitor);
}
}
}