293 lines
6.7 KiB
C#
293 lines
6.7 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using UnityEngine;
|
|
|
|
public class LocalAvoidance : MonoBehaviour
|
|
{
|
|
[SerializeField]
|
|
private Transform target;
|
|
|
|
[SerializeField]
|
|
private Transform root;
|
|
|
|
[SerializeField]
|
|
private LayerMask avoidanceLayerMask;
|
|
|
|
[SerializeField]
|
|
private LayerMask lureMask;
|
|
|
|
[SerializeField]
|
|
private float distance = 3f;
|
|
|
|
[SerializeField]
|
|
private int rays = 3;
|
|
|
|
[SerializeField]
|
|
private float moveSpeed = 1f;
|
|
|
|
[SerializeField]
|
|
private float rotateSpeed = 4f;
|
|
|
|
[SerializeField]
|
|
private float rotateDelay = 0.1f;
|
|
|
|
[SerializeField]
|
|
private float debugAngle = 280f;
|
|
|
|
[SerializeField]
|
|
private Transform tailRag;
|
|
|
|
public bool IsEnabled = true;
|
|
|
|
private static float baitCheckRadius = 0.5f;
|
|
|
|
private static float lureCheckRadius = 3f;
|
|
|
|
private static float lureCheckDistance = 10f;
|
|
|
|
private float keepDirectionDuration = 2f;
|
|
|
|
private float keepDirectionTimer;
|
|
|
|
private static RaycastHit[] hits = new RaycastHit[1];
|
|
|
|
private Rigidbody rb;
|
|
|
|
private bool surfacePushAway = true;
|
|
|
|
private bool bottomPushAway = true;
|
|
|
|
private Vector3 moveDir;
|
|
|
|
private Vector3 sensorDirection;
|
|
|
|
public bool IsUpdateEnabled;
|
|
|
|
private bool IsOnSurface => !GameWaterSystem.IsPositionUnderWater(base.transform.position + (SurfacePushAway ? (Vector3.up * 0.5f) : Vector3.zero));
|
|
|
|
public float MoveSpeed
|
|
{
|
|
get
|
|
{
|
|
return moveSpeed;
|
|
}
|
|
set
|
|
{
|
|
moveSpeed = value;
|
|
}
|
|
}
|
|
|
|
public Transform TailRag
|
|
{
|
|
get
|
|
{
|
|
return tailRag;
|
|
}
|
|
set
|
|
{
|
|
tailRag = value;
|
|
}
|
|
}
|
|
|
|
public Transform Target
|
|
{
|
|
get
|
|
{
|
|
return target;
|
|
}
|
|
set
|
|
{
|
|
target = value;
|
|
}
|
|
}
|
|
|
|
public bool SurfacePushAway
|
|
{
|
|
get
|
|
{
|
|
return surfacePushAway;
|
|
}
|
|
set
|
|
{
|
|
surfacePushAway = value;
|
|
}
|
|
}
|
|
|
|
public bool BottomPushAway
|
|
{
|
|
get
|
|
{
|
|
return bottomPushAway;
|
|
}
|
|
set
|
|
{
|
|
bottomPushAway = value;
|
|
}
|
|
}
|
|
|
|
public float KeepDirectionDuration
|
|
{
|
|
get
|
|
{
|
|
return keepDirectionDuration;
|
|
}
|
|
set
|
|
{
|
|
keepDirectionDuration = value;
|
|
}
|
|
}
|
|
|
|
public event Action<RaycastHit> OnLureHit;
|
|
|
|
private void Start()
|
|
{
|
|
root = base.transform.GetChild(0);
|
|
sensorDirection = base.transform.forward;
|
|
rb = GetComponent<Rigidbody>();
|
|
avoidanceLayerMask = LayerMask.GetMask("Default", "Terrain", "UnderwaterObject", "Bridges");
|
|
lureMask = LayerMask.GetMask("BaitLure");
|
|
}
|
|
|
|
public void ResetRoot()
|
|
{
|
|
base.transform.rotation = root.rotation;
|
|
root.localEulerAngles = Vector3.zero;
|
|
}
|
|
|
|
private void FixedUpdate()
|
|
{
|
|
if (!(target == null) && IsEnabled)
|
|
{
|
|
Vector3 position = base.transform.position;
|
|
moveDir = Vector3.zero;
|
|
Raycasting();
|
|
Quaternion b = ((sensorDirection == Vector3.zero) ? base.transform.rotation : Quaternion.LookRotation(sensorDirection));
|
|
float num = Quaternion.Angle(root.rotation, b);
|
|
Vector3 vector = Vector3.Cross(root.forward, sensorDirection);
|
|
float num2 = num / 90f;
|
|
Vector3 localPosition = tailRag.localPosition;
|
|
localPosition.x = ((vector.y < 0f) ? (0f - num2) : num2);
|
|
tailRag.localPosition = localPosition;
|
|
rb.AddForce(sensorDirection * MoveSpeed, ForceMode.Acceleration);
|
|
root.rotation = Quaternion.Lerp(root.rotation, b, Time.deltaTime * rotateSpeed);
|
|
Vector3.Distance(base.transform.position, position);
|
|
}
|
|
}
|
|
|
|
private void Raycasting()
|
|
{
|
|
UpdateRaycasting();
|
|
}
|
|
|
|
private void UpdateRaycasting()
|
|
{
|
|
if (!IsUpdateEnabled)
|
|
{
|
|
return;
|
|
}
|
|
IsUpdateEnabled = false;
|
|
sensorDirection = GetPassageDirection();
|
|
Debug.DrawLine(base.transform.position, base.transform.position + sensorDirection, Color.yellow);
|
|
if (Physics.SphereCastNonAlloc(base.transform.position, baitCheckRadius, root.forward, hits, lureCheckDistance, lureMask) > 0)
|
|
{
|
|
if (hits[0].transform.CompareTag("BaitLure"))
|
|
{
|
|
this.OnLureHit?.Invoke(hits[0]);
|
|
}
|
|
}
|
|
else if (Physics.SphereCastNonAlloc(base.transform.position, lureCheckRadius, root.forward, hits, lureCheckDistance, lureMask) > 0 && hits[0].transform.CompareTag("BaitLure") && hits[0].collider.GetComponent<FLure>() != null)
|
|
{
|
|
this.OnLureHit?.Invoke(hits[0]);
|
|
}
|
|
}
|
|
|
|
private Vector3 GetPassageDirection()
|
|
{
|
|
Vector3 hitPoint = target.position - base.transform.position;
|
|
Vector3 toTargetForward = hitPoint.normalized;
|
|
float b = Vector3.Distance(target.position, base.transform.position) * 0.7f;
|
|
float a = Mathf.Max(distance, MoveSpeed);
|
|
a = Mathf.Min(a, b);
|
|
Vector3 result = toTargetForward;
|
|
Vector3 hitNormal;
|
|
Vector3 perpendicular;
|
|
List<Vector3> dirList;
|
|
if (Time.time - keepDirectionTimer < 0f)
|
|
{
|
|
result = sensorDirection;
|
|
}
|
|
else
|
|
{
|
|
hitNormal = Vector3.zero;
|
|
Vector3 hitPoint2 = Vector3.zero;
|
|
bool num = RaycastForObstacles(toTargetForward, a, out hitNormal, out hitPoint2);
|
|
bool flag = false;
|
|
if (!num)
|
|
{
|
|
flag = RaycastForObstacles(maxDistance: Mathf.Min(bottomPushAway ? 0.3f : 0.05f, b), direction: -Vector3.up, hitNormal: out hitNormal, hitPoint: out hitPoint2);
|
|
}
|
|
if (num || flag)
|
|
{
|
|
Vector3 lhs = ((Mathf.Abs(Vector3.Dot(hitNormal, Vector3.up)) < 0.99f) ? Vector3.up : Vector3.right);
|
|
hitPoint = Vector3.Cross(lhs, hitNormal);
|
|
perpendicular = hitPoint.normalized;
|
|
dirList = new List<Vector3>();
|
|
dirList.Add(hitNormal);
|
|
AddDirectionsAround(30f);
|
|
AddDirectionsAround(70f);
|
|
dirList.Sort(delegate(Vector3 rhs, Vector3 rhs2)
|
|
{
|
|
float value = Vector3.Dot(toTargetForward, rhs);
|
|
return Vector3.Dot(toTargetForward, rhs2).CompareTo(value);
|
|
});
|
|
foreach (Vector3 item in dirList)
|
|
{
|
|
if (!RaycastForObstacles(item, a, out hitPoint, out var _))
|
|
{
|
|
result = item;
|
|
break;
|
|
}
|
|
}
|
|
keepDirectionTimer = Time.time + KeepDirectionDuration;
|
|
}
|
|
}
|
|
if (IsOnSurface)
|
|
{
|
|
Vector3 hitPoint3 = new Vector3(root.forward.x, 0f, root.forward.z).normalized * 3f + Vector3.down;
|
|
Vector3 normalized = hitPoint3.normalized;
|
|
if (!RaycastForObstacles(normalized, a, out hitPoint3, out hitPoint))
|
|
{
|
|
result = normalized;
|
|
}
|
|
}
|
|
return result;
|
|
void AddDirectionsAround(float angle)
|
|
{
|
|
for (int i = 0; i < 4; i++)
|
|
{
|
|
Vector3 vector = hitNormal;
|
|
vector = Quaternion.AngleAxis(angle, perpendicular) * vector;
|
|
vector = Quaternion.AngleAxis(90f * (float)i, hitNormal) * vector;
|
|
vector.Normalize();
|
|
dirList.Add(vector);
|
|
}
|
|
}
|
|
}
|
|
|
|
private bool RaycastForObstacles(Vector3 direction, float maxDistance, out Vector3 hitNormal, out Vector3 hitPoint, float originBackwardOffset = 0.3f)
|
|
{
|
|
Vector3 origin = -direction * originBackwardOffset + base.transform.position;
|
|
maxDistance += originBackwardOffset;
|
|
bool num = Physics.RaycastNonAlloc(origin, direction, hits, maxDistance, avoidanceLayerMask) > 0;
|
|
if (num)
|
|
{
|
|
hitNormal = hits[0].normal;
|
|
hitPoint = hits[0].point;
|
|
return num;
|
|
}
|
|
hitNormal = Vector3.zero;
|
|
hitPoint = Vector3.zero;
|
|
return num;
|
|
}
|
|
}
|