diff --git a/Assets/JYY/Scenes/MonsterTest.unity b/Assets/JYY/Scenes/MonsterTest.unity index ba13a3ca..a20361d2 100644 --- a/Assets/JYY/Scenes/MonsterTest.unity +++ b/Assets/JYY/Scenes/MonsterTest.unity @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3402276a5eb04d5f3eab27539ff775edf472d2829fe28e02aed24953cf712aff -size 21005 +oid sha256:02d552590e52618fe85e7e322810c4818ae3ce2769ca1cbc8b033b76502cbe68 +size 21370 diff --git a/Assets/JYY/Scenes/MonsterTest/NavMesh-Plane.asset b/Assets/JYY/Scenes/MonsterTest/NavMesh-Plane.asset index afe2d704..378941cb 100644 Binary files a/Assets/JYY/Scenes/MonsterTest/NavMesh-Plane.asset and b/Assets/JYY/Scenes/MonsterTest/NavMesh-Plane.asset differ diff --git a/Assets/Scripts/Character/Enemy/CasterDemonController.cs b/Assets/Scripts/Character/Enemy/CasterDemonController.cs index 147436cf..364ff437 100644 --- a/Assets/Scripts/Character/Enemy/CasterDemonController.cs +++ b/Assets/Scripts/Character/Enemy/CasterDemonController.cs @@ -1,16 +1,45 @@ +using System; using System.Collections; using System.Collections.Generic; using UnityEngine; +using UnityEngine.AI; +using Random = UnityEngine.Random; public class CasterDemonController : EnemyController { private bool _doneBattleSequence = true; private bool _isFirstNoPath = true; + private Coroutine _currentSequence; + [SerializeField] private Transform teleportTransform; [SerializeField] private Transform bulletShotPosition; [SerializeField] private GameObject magicMissilePrefab; [SerializeField] private GameObject teleportEffectPrefab; + [SerializeField] private float teleportDistance = 4f; // 플레이어 뒤로 떨어질 거리 + + + // 텔레포트 쿨타임 + private float _teleportTimer = 0; + private const float TeleportThresholdTime = 3.5f; + + private bool CanTeleport { + get + { + if (_teleportTimer >= TeleportThresholdTime ) + { + _teleportTimer = 0; + return true; + } + return false; + } + } + + private void LateUpdate() + { + _teleportTimer += Time.deltaTime; + } + public override void BattleSequence() { // 전투 행동이 이미 진행 중일 경우 실행 막기 @@ -25,7 +54,6 @@ public class CasterDemonController : EnemyController } } - private void Thinking() { int selectedPattern = Random.Range(0, 10); @@ -51,34 +79,22 @@ public class CasterDemonController : EnemyController case 8: case 9: - StartCoroutine(ShotMagicMissile()); + SetSequence(ShotMagicMissile()); break; } } public override void OnCannotFleeBehaviour() { - // 구석에 끼인 경우 탈출 - - Debug.Log("## 텔레포트 시전"); - Teleport(); + if(CanTeleport) + SetSequence(Teleport()); } private IEnumerator ShotMagicMissile() { for (int i = 0; i < 3; i++) { - // 1. 기본 위치 - Vector3 basePos = TraceTargetTransform.position; - Vector3 aimPosition = basePos; - - // 2. 플레이어 Rigidbody로 속도 얻기 - if (TraceTargetTransform.TryGetComponent(out var rb)) - { - // 아주 짧은 시간만 예측 - float predictionTime = 0.3f; - aimPosition += rb.velocity * predictionTime; - } + var aimPosition = TargetPosOracle(out var basePos, out var rb); // 높이는 변경할 필요 없음 float fixedY = bulletShotPosition.position.y; @@ -104,21 +120,59 @@ public class CasterDemonController : EnemyController _doneBattleSequence = true; } - private void Teleport() + private Vector3 TargetPosOracle(out Vector3 basePos, out Rigidbody rb) { - if (teleportEffectPrefab != null) - Instantiate(teleportEffectPrefab, transform.position, Quaternion.identity); + // 1. 기본 위치 + basePos = TraceTargetTransform.position; + Vector3 aimPosition = basePos; - if (Agent != null && teleportTransform != null) - Agent.Warp(teleportTransform.position); - else if (teleportTransform != null) - transform.position = teleportTransform.position; + // 2. 플레이어 Rigidbody로 속도 얻기 + if (TraceTargetTransform.TryGetComponent(out rb)) + { + // 아주 짧은 시간만 예측 + float predictionTime = 0.3f; + aimPosition += rb.velocity * predictionTime; + } - if (teleportEffectPrefab != null && teleportTransform != null) - Instantiate(teleportEffectPrefab, teleportTransform.position, Quaternion.identity); + return aimPosition; } + private IEnumerator Teleport() + { + Vector3 startPos = transform.position; + if (teleportEffectPrefab != null) + Instantiate(teleportEffectPrefab, startPos, Quaternion.identity); + // 플레이어 뒤쪽 위치 계산 + Vector3 playerPos = TraceTargetTransform.position; + Vector3 behindDir = -TraceTargetTransform.forward; + Vector3 targetPos = playerPos + behindDir.normalized * teleportDistance; + // NavMesh 유효 위치 확인 + Vector3 finalPos = targetPos; + if (NavMesh.SamplePosition(targetPos, out NavMeshHit hit, 1f, NavMesh.AllAreas)) + { + finalPos = hit.position; + } + + yield return new WaitForSeconds(0.3f); + + // 텔레포트 실행 + Agent.Warp(finalPos); + + if (teleportEffectPrefab != null) + Instantiate(teleportEffectPrefab, finalPos, Quaternion.identity); + + yield return null; + } + + private void SetSequence(IEnumerator newSequence) + { + if (_currentSequence != null) + { + StopCoroutine(_currentSequence); + } + _currentSequence = StartCoroutine(newSequence); + } } diff --git a/Assets/Scripts/Character/Enemy/EnemyState/Caster/EnemyStateFlee.cs b/Assets/Scripts/Character/Enemy/EnemyState/Caster/EnemyStateFlee.cs index e6b8d161..4071dd81 100644 --- a/Assets/Scripts/Character/Enemy/EnemyState/Caster/EnemyStateFlee.cs +++ b/Assets/Scripts/Character/Enemy/EnemyState/Caster/EnemyStateFlee.cs @@ -10,13 +10,14 @@ public class EnemyStateFlee :IEnemyState // 경로 탐색 주기 조절용 private float _fleeSearchTimer = 0; - private float _fleeThresholdTime = 0.2f; + private const float FleeThresholdTime = 0.2f; // 막다른길 검사용 private Vector3 _lastPosition; private float _stuckTimer = 0f; private const float StuckThresholdTime = 1f; // 1초 동안 거의 못 움직이면 막힌 걸로 간주 private const float StuckMoveThreshold = 0.1f; // 이내 이동은 “제자리”로 본다 + private int _stuckCount = 0; public void Enter(EnemyController enemyController) { @@ -25,7 +26,6 @@ public class EnemyStateFlee :IEnemyState _playerTransform = _enemyController.TraceTargetTransform; _lastPosition = _enemyController.transform.position; - _enemyController.Agent.ResetPath(); _enemyController.Agent.isStopped = false; _stuckTimer = 0f; @@ -76,8 +76,6 @@ public class EnemyStateFlee :IEnemyState _stuckTimer += Time.deltaTime; if (_stuckTimer >= StuckThresholdTime) { - - Debug.Log("## 끼임"); HandleDeadEnd(); _stuckTimer = 0f; } @@ -91,7 +89,7 @@ public class EnemyStateFlee :IEnemyState private void FindPositionFlee() { _fleeSearchTimer += Time.deltaTime; - if (_fleeSearchTimer <= _fleeThresholdTime) return; + if (_fleeSearchTimer <= FleeThresholdTime) return; // 1) 목표 도망 위치 계산 Vector3 fleeDirection = (_enemyController.transform.position - _playerTransform.position).normalized; @@ -107,6 +105,13 @@ public class EnemyStateFlee :IEnemyState private void HandleDeadEnd() { + if (_stuckCount >= 4) + { + _enemyController.OnCannotFleeBehaviour(); + _stuckCount = 0; + return; + } + _stuckCount++; // 무작위 도망 지점 샘플링 시도 Vector3 randomDirection = Random.insideUnitSphere * (_fleeDistance * 2); randomDirection += _playerTransform.position; @@ -116,14 +121,15 @@ public class EnemyStateFlee :IEnemyState // 샘플링에 성공했으면 일단 그 위치로 가 보도록 세팅 Debug.Log("## 일단 가봄"); _enemyController.Agent.SetDestination(hit.position); - _enemyController.OnCannotFleeBehaviour(); - } - else - { - // 대체 경로도 찾을 수 없는 경우 - Debug.Log("## 대체 경로도 못찾음"); - _enemyController.OnCannotFleeBehaviour(); + // _enemyController.OnCannotFleeBehaviour(); } + + // else + // { + // // 대체 경로도 찾을 수 없는 경우 + // Debug.Log("## 대체 경로도 못찾음"); + // _enemyController.OnCannotFleeBehaviour(); + // } }