Files
2026-03-04 10:03:45 +08:00

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;
}
}