[fix] 도주 대체 경로 찾는 중 워프 발생하여 수정 시작

- 텔레포트 쿨타임 생성
- 텔레포트 로직 변경 (플레이어 뒤로 순간이동)
- 코루틴 관리를 위한 메서드 생성
This commit is contained in:
Fiore 2025-04-29 18:08:45 +09:00
parent 5dcde907a1
commit 71a904e57e
4 changed files with 100 additions and 40 deletions

BIN
Assets/JYY/Scenes/MonsterTest.unity (Stored with Git LFS)

Binary file not shown.

View File

@ -1,16 +1,45 @@
using System;
using System.Collections; using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using UnityEngine; using UnityEngine;
using UnityEngine.AI;
using Random = UnityEngine.Random;
public class CasterDemonController : EnemyController public class CasterDemonController : EnemyController
{ {
private bool _doneBattleSequence = true; private bool _doneBattleSequence = true;
private bool _isFirstNoPath = true; private bool _isFirstNoPath = true;
private Coroutine _currentSequence;
[SerializeField] private Transform teleportTransform; [SerializeField] private Transform teleportTransform;
[SerializeField] private Transform bulletShotPosition; [SerializeField] private Transform bulletShotPosition;
[SerializeField] private GameObject magicMissilePrefab; [SerializeField] private GameObject magicMissilePrefab;
[SerializeField] private GameObject teleportEffectPrefab; [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() public override void BattleSequence()
{ {
// 전투 행동이 이미 진행 중일 경우 실행 막기 // 전투 행동이 이미 진행 중일 경우 실행 막기
@ -25,7 +54,6 @@ public class CasterDemonController : EnemyController
} }
} }
private void Thinking() private void Thinking()
{ {
int selectedPattern = Random.Range(0, 10); int selectedPattern = Random.Range(0, 10);
@ -51,34 +79,22 @@ public class CasterDemonController : EnemyController
case 8: case 8:
case 9: case 9:
StartCoroutine(ShotMagicMissile()); SetSequence(ShotMagicMissile());
break; break;
} }
} }
public override void OnCannotFleeBehaviour() public override void OnCannotFleeBehaviour()
{ {
// 구석에 끼인 경우 탈출 if(CanTeleport)
SetSequence(Teleport());
Debug.Log("## 텔레포트 시전");
Teleport();
} }
private IEnumerator ShotMagicMissile() private IEnumerator ShotMagicMissile()
{ {
for (int i = 0; i < 3; i++) for (int i = 0; i < 3; i++)
{ {
// 1. 기본 위치 var aimPosition = TargetPosOracle(out var basePos, out var rb);
Vector3 basePos = TraceTargetTransform.position;
Vector3 aimPosition = basePos;
// 2. 플레이어 Rigidbody로 속도 얻기
if (TraceTargetTransform.TryGetComponent<Rigidbody>(out var rb))
{
// 아주 짧은 시간만 예측
float predictionTime = 0.3f;
aimPosition += rb.velocity * predictionTime;
}
// 높이는 변경할 필요 없음 // 높이는 변경할 필요 없음
float fixedY = bulletShotPosition.position.y; float fixedY = bulletShotPosition.position.y;
@ -104,21 +120,59 @@ public class CasterDemonController : EnemyController
_doneBattleSequence = true; _doneBattleSequence = true;
} }
private void Teleport() private Vector3 TargetPosOracle(out Vector3 basePos, out Rigidbody rb)
{ {
if (teleportEffectPrefab != null) // 1. 기본 위치
Instantiate(teleportEffectPrefab, transform.position, Quaternion.identity); basePos = TraceTargetTransform.position;
Vector3 aimPosition = basePos;
if (Agent != null && teleportTransform != null) // 2. 플레이어 Rigidbody로 속도 얻기
Agent.Warp(teleportTransform.position); if (TraceTargetTransform.TryGetComponent<Rigidbody>(out rb))
else if (teleportTransform != null) {
transform.position = teleportTransform.position; // 아주 짧은 시간만 예측
float predictionTime = 0.3f;
aimPosition += rb.velocity * predictionTime;
}
if (teleportEffectPrefab != null && teleportTransform != null) return aimPosition;
Instantiate(teleportEffectPrefab, teleportTransform.position, Quaternion.identity);
} }
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);
}
} }

View File

@ -10,13 +10,14 @@ public class EnemyStateFlee :IEnemyState
// 경로 탐색 주기 조절용 // 경로 탐색 주기 조절용
private float _fleeSearchTimer = 0; private float _fleeSearchTimer = 0;
private float _fleeThresholdTime = 0.2f; private const float FleeThresholdTime = 0.2f;
// 막다른길 검사용 // 막다른길 검사용
private Vector3 _lastPosition; private Vector3 _lastPosition;
private float _stuckTimer = 0f; private float _stuckTimer = 0f;
private const float StuckThresholdTime = 1f; // 1초 동안 거의 못 움직이면 막힌 걸로 간주 private const float StuckThresholdTime = 1f; // 1초 동안 거의 못 움직이면 막힌 걸로 간주
private const float StuckMoveThreshold = 0.1f; // 이내 이동은 “제자리”로 본다 private const float StuckMoveThreshold = 0.1f; // 이내 이동은 “제자리”로 본다
private int _stuckCount = 0;
public void Enter(EnemyController enemyController) public void Enter(EnemyController enemyController)
{ {
@ -25,7 +26,6 @@ public class EnemyStateFlee :IEnemyState
_playerTransform = _enemyController.TraceTargetTransform; _playerTransform = _enemyController.TraceTargetTransform;
_lastPosition = _enemyController.transform.position; _lastPosition = _enemyController.transform.position;
_enemyController.Agent.ResetPath(); _enemyController.Agent.ResetPath();
_enemyController.Agent.isStopped = false; _enemyController.Agent.isStopped = false;
_stuckTimer = 0f; _stuckTimer = 0f;
@ -76,8 +76,6 @@ public class EnemyStateFlee :IEnemyState
_stuckTimer += Time.deltaTime; _stuckTimer += Time.deltaTime;
if (_stuckTimer >= StuckThresholdTime) if (_stuckTimer >= StuckThresholdTime)
{ {
Debug.Log("## 끼임");
HandleDeadEnd(); HandleDeadEnd();
_stuckTimer = 0f; _stuckTimer = 0f;
} }
@ -91,7 +89,7 @@ public class EnemyStateFlee :IEnemyState
private void FindPositionFlee() private void FindPositionFlee()
{ {
_fleeSearchTimer += Time.deltaTime; _fleeSearchTimer += Time.deltaTime;
if (_fleeSearchTimer <= _fleeThresholdTime) return; if (_fleeSearchTimer <= FleeThresholdTime) return;
// 1) 목표 도망 위치 계산 // 1) 목표 도망 위치 계산
Vector3 fleeDirection = (_enemyController.transform.position - _playerTransform.position).normalized; Vector3 fleeDirection = (_enemyController.transform.position - _playerTransform.position).normalized;
@ -107,6 +105,13 @@ public class EnemyStateFlee :IEnemyState
private void HandleDeadEnd() private void HandleDeadEnd()
{ {
if (_stuckCount >= 4)
{
_enemyController.OnCannotFleeBehaviour();
_stuckCount = 0;
return;
}
_stuckCount++;
// 무작위 도망 지점 샘플링 시도 // 무작위 도망 지점 샘플링 시도
Vector3 randomDirection = Random.insideUnitSphere * (_fleeDistance * 2); Vector3 randomDirection = Random.insideUnitSphere * (_fleeDistance * 2);
randomDirection += _playerTransform.position; randomDirection += _playerTransform.position;
@ -116,14 +121,15 @@ public class EnemyStateFlee :IEnemyState
// 샘플링에 성공했으면 일단 그 위치로 가 보도록 세팅 // 샘플링에 성공했으면 일단 그 위치로 가 보도록 세팅
Debug.Log("## 일단 가봄"); Debug.Log("## 일단 가봄");
_enemyController.Agent.SetDestination(hit.position); _enemyController.Agent.SetDestination(hit.position);
_enemyController.OnCannotFleeBehaviour(); // _enemyController.OnCannotFleeBehaviour();
}
else
{
// 대체 경로도 찾을 수 없는 경우
Debug.Log("## 대체 경로도 못찾음");
_enemyController.OnCannotFleeBehaviour();
} }
// else
// {
// // 대체 경로도 찾을 수 없는 경우
// Debug.Log("## 대체 경로도 못찾음");
// _enemyController.OnCannotFleeBehaviour();
// }
} }