324 lines
11 KiB
C#
324 lines
11 KiB
C#
using System;
|
|
using System.Collections;
|
|
using Sirenix.OdinInspector;
|
|
using UnityEngine;
|
|
|
|
// ReSharper disable once CheckNamespace
|
|
namespace BlueWaterProject
|
|
{
|
|
public enum EscapeMode
|
|
{
|
|
NONE = -1,
|
|
STRAIGHT,
|
|
ZIGZAG,
|
|
TOWARDS
|
|
}
|
|
|
|
public class Fish : MonoBehaviour
|
|
{
|
|
// 초기화 방식
|
|
[Title("초기화 방식")]
|
|
[SerializeField] private bool autoInit = true;
|
|
|
|
// 물고기의 기본 설정
|
|
[Title("물고기의 기본 설정")]
|
|
[SerializeField] private bool isDrawGizmos = true;
|
|
|
|
[Tooltip("타겟 인식 범위")]
|
|
[SerializeField] private float viewRadius = 10f;
|
|
|
|
[Tooltip("이동속도")]
|
|
[SerializeField] private float moveSpd = 500f;
|
|
|
|
[Tooltip("회전속도")]
|
|
[SerializeField] private float rotationSpeed = 10f;
|
|
|
|
[Tooltip("랜덤 방향으로 도주 여부")]
|
|
[SerializeField] private bool isRandomAngle = true;
|
|
|
|
[ShowIf("@isRandomAngle")]
|
|
[Tooltip("도망가는 방향의 랜덤 각도")]
|
|
[SerializeField] private float randomAngle = 180f;
|
|
|
|
[Tooltip("타겟을 재검색하는 시간")]
|
|
[SerializeField] private float rescanTime = 0.5f;
|
|
|
|
[Tooltip("도망가는 시간")]
|
|
[SerializeField] private float escapeTime = 10f;
|
|
|
|
[Tooltip("리스폰 여부")]
|
|
[SerializeField] private bool isRespawn = true;
|
|
|
|
[ShowIf("@isRespawn == true")]
|
|
[Tooltip("리스폰되는데 걸리는 시간")]
|
|
[SerializeField] private float respawnTime = 5f;
|
|
|
|
[Tooltip("도망치면서 랜덤 방향 전환 여부")]
|
|
[SerializeField] private bool randomDirectionChange;
|
|
|
|
[ShowIf("@!randomDirectionChange")]
|
|
[Tooltip("도망치면서 방향 전환하는데 걸리는 시간")]
|
|
[SerializeField] private float directionChangeInterval = 1f;
|
|
|
|
[ShowIf("@randomDirectionChange")]
|
|
[Tooltip("도망치면서 방향 전환하는데 걸리는 랜덤 최소 시간")]
|
|
[SerializeField] private float minDirectionChangeInterval = 0.1f;
|
|
|
|
[ShowIf("@randomDirectionChange")]
|
|
[Tooltip("도망치면서 방향 전환하는데 걸리는 랜덤 최대 시간")]
|
|
[SerializeField] private float maxDirectionChangeInterval = 1f;
|
|
|
|
[Tooltip("도주 방식")]
|
|
[SerializeField] private EscapeMode escapeMode = EscapeMode.STRAIGHT;
|
|
|
|
// ZIGZAG
|
|
[Title("ZIGZAG")]
|
|
[ShowIf("@escapeMode == EscapeMode.ZIGZAG")]
|
|
[Tooltip("흔들림의 정도")]
|
|
[SerializeField] private bool randomZigzag;
|
|
|
|
[ShowIf("@escapeMode == EscapeMode.ZIGZAG && !randomZigzag")]
|
|
[Tooltip("흔들림의 정도")]
|
|
[SerializeField] private float zigzagAmplitude = 1f;
|
|
|
|
[ShowIf("@escapeMode == EscapeMode.ZIGZAG && !randomZigzag")]
|
|
[Tooltip("흔들림의 주기")]
|
|
[SerializeField] private float zigzagFrequency = 1f;
|
|
|
|
[ShowIf("@escapeMode == EscapeMode.ZIGZAG && randomZigzag")]
|
|
[Tooltip("흔들림의 정도 랜덤 최솟값")]
|
|
[SerializeField] private float minZigzagAmplitude = 0.1f;
|
|
|
|
[ShowIf("@escapeMode == EscapeMode.ZIGZAG && randomZigzag")]
|
|
[Tooltip("흔들림의 정도 랜덤 최댓값")]
|
|
[SerializeField] private float maxZigzagAmplitude = 1f;
|
|
|
|
[Space]
|
|
[ShowIf("@escapeMode == EscapeMode.ZIGZAG && randomZigzag")]
|
|
[Tooltip("흔들림의 주기 랜덤 최솟값")]
|
|
[SerializeField] private float minZigzagFrequency = 0.1f;
|
|
|
|
[ShowIf("@escapeMode == EscapeMode.ZIGZAG && randomZigzag")]
|
|
[Tooltip("흔들림의 주기 랜덤 최댓값")]
|
|
[SerializeField] private float maxZigzagFrequency = 1f;
|
|
|
|
// 디버깅
|
|
[Title("디버깅")]
|
|
[SerializeField] private Collider[] hitColliders = new Collider[MAX_HIT_NUM];
|
|
[SerializeField] private LayerMask targetLayer;
|
|
|
|
private Rigidbody rb;
|
|
private Coroutine findTargetCoroutine;
|
|
private Coroutine escapeCoroutine;
|
|
private WaitForSeconds findCoroutineTime;
|
|
private Vector3 spawnPos;
|
|
|
|
private const int MAX_HIT_NUM = 3;
|
|
|
|
private void OnValidate()
|
|
{
|
|
findCoroutineTime = new WaitForSeconds(rescanTime);
|
|
}
|
|
|
|
private void OnDrawGizmosSelected()
|
|
{
|
|
if (!isDrawGizmos) return;
|
|
|
|
Gizmos.color = Color.red;
|
|
Gizmos.DrawWireSphere(transform.position, viewRadius);
|
|
}
|
|
|
|
[Button("셋팅 초기화")]
|
|
private void Init()
|
|
{
|
|
rb = GetComponent<Rigidbody>();
|
|
|
|
targetLayer = LayerMask.GetMask("Player");
|
|
}
|
|
|
|
#region Preset
|
|
|
|
[HorizontalGroup("Split")]
|
|
[Button("프리셋 1번")]
|
|
private void Preset1()
|
|
{
|
|
isDrawGizmos = true;
|
|
viewRadius = 20f;
|
|
moveSpd = 500f;
|
|
rotationSpeed = 10f;
|
|
isRandomAngle = false;
|
|
rescanTime = 0.5f;
|
|
escapeTime = 10f;
|
|
isRespawn = true;
|
|
respawnTime = 5f;
|
|
randomDirectionChange = false;
|
|
directionChangeInterval = 3f;
|
|
escapeMode = EscapeMode.STRAIGHT;
|
|
}
|
|
|
|
[HorizontalGroup("Split")]
|
|
[Button("프리셋 2번")]
|
|
private void Preset2()
|
|
{
|
|
isDrawGizmos = true;
|
|
viewRadius = 20f;
|
|
moveSpd = 500f;
|
|
rotationSpeed = 10f;
|
|
isRandomAngle = true;
|
|
randomAngle = 120f;
|
|
rescanTime = 0.5f;
|
|
escapeTime = 10f;
|
|
isRespawn = true;
|
|
respawnTime = 5f;
|
|
randomDirectionChange = true;
|
|
minDirectionChangeInterval = 1f;
|
|
maxDirectionChangeInterval = 3f;
|
|
directionChangeInterval = 3f;
|
|
escapeMode = EscapeMode.ZIGZAG;
|
|
randomZigzag = false;
|
|
zigzagAmplitude = 0.5f;
|
|
zigzagFrequency = 1f;
|
|
}
|
|
|
|
[HorizontalGroup("Split")]
|
|
[Button("프리셋 3번")]
|
|
private void Preset3()
|
|
{
|
|
isDrawGizmos = true;
|
|
viewRadius = 20f;
|
|
moveSpd = 500f;
|
|
rotationSpeed = 10f;
|
|
isRandomAngle = true;
|
|
randomAngle = 120f;
|
|
rescanTime = 0.5f;
|
|
escapeTime = 10f;
|
|
isRespawn = true;
|
|
respawnTime = 5f;
|
|
randomDirectionChange = true;
|
|
minDirectionChangeInterval = 1f;
|
|
maxDirectionChangeInterval = 3f;
|
|
directionChangeInterval = 3f;
|
|
escapeMode = EscapeMode.ZIGZAG;
|
|
randomZigzag = true;
|
|
minZigzagAmplitude = 0.1f;
|
|
maxZigzagAmplitude = 2f;
|
|
minZigzagFrequency = 0.1f;
|
|
maxZigzagFrequency = 2f;
|
|
}
|
|
|
|
#endregion
|
|
|
|
private void Awake()
|
|
{
|
|
if (autoInit)
|
|
{
|
|
Init();
|
|
}
|
|
}
|
|
|
|
private void OnEnable()
|
|
{
|
|
hitColliders = new Collider[MAX_HIT_NUM];
|
|
findCoroutineTime = new WaitForSeconds(rescanTime);
|
|
|
|
if (findTargetCoroutine != null) return;
|
|
|
|
findTargetCoroutine = StartCoroutine(FindTargetCoroutine());
|
|
}
|
|
|
|
private void Start()
|
|
{
|
|
spawnPos = transform.position;
|
|
}
|
|
|
|
private IEnumerator FindTargetCoroutine()
|
|
{
|
|
while (true)
|
|
{
|
|
var size = Physics.OverlapSphereNonAlloc(transform.position, viewRadius, hitColliders, targetLayer);
|
|
for (var i = 0; i < size; i++)
|
|
{
|
|
var hitCollider = hitColliders[i];
|
|
if (hitCollider == null || !hitCollider.CompareTag("ShipPlayer")) continue;
|
|
|
|
findTargetCoroutine = null;
|
|
escapeCoroutine = StartCoroutine(EscapeCoroutine(hitCollider));
|
|
yield break;
|
|
}
|
|
yield return findCoroutineTime;
|
|
}
|
|
}
|
|
|
|
private IEnumerator EscapeCoroutine(Collider targetCollider)
|
|
{
|
|
var currentDirectionChangeInterval = randomDirectionChange ?
|
|
UnityEngine.Random.Range(minDirectionChangeInterval, maxDirectionChangeInterval)
|
|
: directionChangeInterval;
|
|
var rotatedEscapeDirection = CalculateEscapeDirection(targetCollider.transform.position);
|
|
|
|
var time = 0f;
|
|
var directionChangeTime = 0f;
|
|
while (time < escapeTime)
|
|
{
|
|
time += Time.deltaTime;
|
|
directionChangeTime += Time.deltaTime;
|
|
|
|
if (directionChangeTime >= currentDirectionChangeInterval)
|
|
{
|
|
rotatedEscapeDirection = CalculateEscapeDirection(targetCollider.transform.position);
|
|
directionChangeTime = 0f;
|
|
currentDirectionChangeInterval = randomDirectionChange ?
|
|
UnityEngine.Random.Range(minDirectionChangeInterval, maxDirectionChangeInterval)
|
|
: directionChangeInterval;
|
|
|
|
if (escapeMode == EscapeMode.ZIGZAG && randomZigzag)
|
|
{
|
|
zigzagFrequency = UnityEngine.Random.Range(minZigzagFrequency, maxZigzagFrequency);
|
|
zigzagAmplitude = UnityEngine.Random.Range(minZigzagAmplitude, maxZigzagAmplitude);
|
|
}
|
|
}
|
|
|
|
var newDirection = escapeMode switch
|
|
{
|
|
EscapeMode.NONE => throw new ArgumentOutOfRangeException(),
|
|
EscapeMode.STRAIGHT => rotatedEscapeDirection,
|
|
EscapeMode.ZIGZAG => rotatedEscapeDirection +
|
|
new Vector3(Mathf.Sin(Time.time * zigzagFrequency) * zigzagAmplitude,0,
|
|
Mathf.Sin(Time.time * zigzagFrequency) * zigzagAmplitude),
|
|
EscapeMode.TOWARDS => -rotatedEscapeDirection,
|
|
_ => throw new ArgumentOutOfRangeException()
|
|
};
|
|
|
|
rb.velocity = newDirection.normalized * (moveSpd * Time.deltaTime);
|
|
var targetRotation = Quaternion.LookRotation(newDirection);
|
|
transform.rotation = Quaternion.Slerp(transform.rotation, targetRotation, rotationSpeed * Time.deltaTime);
|
|
|
|
yield return null;
|
|
}
|
|
|
|
rb.velocity = Vector3.zero;
|
|
escapeCoroutine = null;
|
|
|
|
if (isRespawn)
|
|
{
|
|
FishManager.Inst.RespawnFish(gameObject, respawnTime, spawnPos);
|
|
}
|
|
gameObject.SetActive(false);
|
|
}
|
|
|
|
private Vector3 CalculateEscapeDirection(Vector3 targetPos)
|
|
{
|
|
var escapeDirection = (transform.position - targetPos).normalized;
|
|
escapeDirection.y = 0;
|
|
|
|
if (!isRandomAngle) return escapeDirection;
|
|
|
|
var randomRotationAngle = UnityEngine.Random.Range(-randomAngle * 0.5f, randomAngle * 0.5f);
|
|
var rotation = Quaternion.Euler(0, randomRotationAngle, 0);
|
|
return rotation * escapeDirection;
|
|
|
|
}
|
|
}
|
|
}
|
|
|