From 71a904e57ebc3cb30471a47a453275ad161bddd1 Mon Sep 17 00:00:00 2001 From: Fiore Date: Tue, 29 Apr 2025 18:08:45 +0900 Subject: [PATCH] =?UTF-8?q?[fix]=20=EB=8F=84=EC=A3=BC=20=20=EB=8C=80?= =?UTF-8?q?=EC=B2=B4=20=EA=B2=BD=EB=A1=9C=20=EC=B0=BE=EB=8A=94=20=EC=A4=91?= =?UTF-8?q?=20=EC=9B=8C=ED=94=84=20=EB=B0=9C=EC=83=9D=ED=95=98=EC=97=AC=20?= =?UTF-8?q?=EC=88=98=EC=A0=95=20=EC=8B=9C=EC=9E=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 텔레포트 쿨타임 생성 - 텔레포트 로직 변경 (플레이어 뒤로 순간이동) - 코루틴 관리를 위한 메서드 생성 --- Assets/JYY/Scenes/MonsterTest.unity | 4 +- .../Scenes/MonsterTest/NavMesh-Plane.asset | Bin 10020 -> 7236 bytes .../Character/Enemy/CasterDemonController.cs | 106 +++++++++++++----- .../Enemy/EnemyState/Caster/EnemyStateFlee.cs | 30 +++-- 4 files changed, 100 insertions(+), 40 deletions(-) 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 afe2d7047253e0595e4c2cb1fb58a9cefb3dda00..378941cbeebab35f974ba361f5706161ead1b2e4 100644 GIT binary patch delta 428 zcmZ4Dcf?|X9FvU8MujEfOerjrFN&K_ejv_0nL|Q^g@u8EVe$$Y0|f?#69*X>6qZ3~ z28OgWHwT~`5FBt=z3R}x$v zC%L)lJl2z5?z8)BJK00Qzz5-0kWQFePdI|y3gSZ@2;@W95E|leWH!hRlWUYAo|pvo z#Omy_^`{uuyA>^RU%_3{&^6gW(aZ?s2@p8p2niKTn~;11^cB$58Hx&%9n{RgK7p9r o^LTS@h429zDZ}?$GW&NoPhOxF!IPGD!tv}GA6pPTnMGXz09f3ewg3PC delta 2247 zcmb7_TSyd97{|}9?jjcIgO}Mgbu2>*m&}EXV9VXqq+}~e%diVw3@k{9iU?xUZjzRD zLVE}*Q4=Gqhw_4vEn7agMmI{KLLb@)B20QvB5l6;=G#5%1@d7z-_Dtt|L^?2|D5Al z-Q=uG)TgFAlAGf6Ig$KLToxaWTfoDl=$I(R*n8zyY$s6XGWH;NXI0(@gJTF2+J#{GpV_ zdrd|I(-Bztpsys;T2fyb;)`_bEBqmX!K11#yP@*?;t;W5hENnl$MhOe@Sb^Kby`kD zPWz(jilor_QMSw|ek(QQXRdRa#m|^CjF29t zQ{odHbZ~VAUhLBPkD{r z%0YWW)uHCZaIpc90G=VqdjTe30R{lXH4D_{!ar)dSOXY5KL;2yx&&_^80bwxt7+>y zZ(&xaA@}R9{u ze>FUH^F22oG_T5pJwtVYK1~&Yq_mY&2fz^r&yJVF^vZq}WPdc`qU!e8-1t}IFOf@`#V6(1&u2>|0`PFI}UpEp_SJi(v^vlzm zA{QJmI<7O*0i!KkjRx@17Ym*vM<5y$2Y7~RxQ8Dx;{lVV9x%Fr_ItI>MQ0y;+B|Z+ zBjTMX4+jj$2M!o`87vgCIU4}X5e(G?9SBU|fKl!VYM6S!AXwL{*$DA3hrU`BZmP<+oUOP6zLe-?7kY&36oV L3v4j*ltuDCRfg&S 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(); + // } }