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 OnLureHit; private void Start() { root = base.transform.GetChild(0); sensorDirection = base.transform.forward; rb = GetComponent(); 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() != 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 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(); 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; } }