diff --git a/Assets/JYY/Prefabs/Bullets.meta b/Assets/JYY/Prefabs/Bullets.meta new file mode 100644 index 00000000..760b6a99 --- /dev/null +++ b/Assets/JYY/Prefabs/Bullets.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 37e30a87d9c904c898232118c82b5fbc +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/JYY/Prefabs/Bullets/Dummy Magic Missaile.prefab b/Assets/JYY/Prefabs/Bullets/Dummy Magic Missaile.prefab new file mode 100644 index 00000000..7a7770e6 --- /dev/null +++ b/Assets/JYY/Prefabs/Bullets/Dummy Magic Missaile.prefab @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:1b880c281eae23192aec88f2cdc857d261f1b7542d69ca291348ce67ce506ba0 +size 3412 diff --git a/Assets/JYY/Prefabs/Bullets/Dummy Magic Missaile.prefab.meta b/Assets/JYY/Prefabs/Bullets/Dummy Magic Missaile.prefab.meta new file mode 100644 index 00000000..5f83d1a3 --- /dev/null +++ b/Assets/JYY/Prefabs/Bullets/Dummy Magic Missaile.prefab.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 9c16b44c8736e4007ad5f0733ce433e1 +PrefabImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/JYY/Prefabs/[Enemy] Dummy Monster.prefab b/Assets/JYY/Prefabs/[Enemy] Dummy Monster.prefab new file mode 100644 index 00000000..59bde51d --- /dev/null +++ b/Assets/JYY/Prefabs/[Enemy] Dummy Monster.prefab @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:2f3a5655d83502f88a2dfe4ff925a8705d756b4b6b7158060da8886e306dea8e +size 7232 diff --git a/Assets/JYY/Prefabs/[Enemy] Dummy Monster.prefab.meta b/Assets/JYY/Prefabs/[Enemy] Dummy Monster.prefab.meta new file mode 100644 index 00000000..ef3b4753 --- /dev/null +++ b/Assets/JYY/Prefabs/[Enemy] Dummy Monster.prefab.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 06eebdb9d2c03437fb632d9e15fd1078 +PrefabImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/JYY/Scenes/MonsterTest.unity b/Assets/JYY/Scenes/MonsterTest.unity index d772a68e..ba13a3ca 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:efc792ecb073a66263abf936bd751584b41224bde07c7f02418d10223d0c07f4 -size 22331 +oid sha256:3402276a5eb04d5f3eab27539ff775edf472d2829fe28e02aed24953cf712aff +size 21005 diff --git a/Assets/Scripts/Character/Enemy/Bullet.meta b/Assets/Scripts/Character/Enemy/Bullet.meta new file mode 100644 index 00000000..48dcbd7d --- /dev/null +++ b/Assets/Scripts/Character/Enemy/Bullet.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: a636af603b3af4d1baccb8296add7485 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/Character/Enemy/Bullet/BulletBase.cs b/Assets/Scripts/Character/Enemy/Bullet/BulletBase.cs new file mode 100644 index 00000000..d3c1a9e9 --- /dev/null +++ b/Assets/Scripts/Character/Enemy/Bullet/BulletBase.cs @@ -0,0 +1,75 @@ +using UnityEngine; + +public struct BulletData +{ + public Vector3 TargetPos; + public float Damage; + public float LifeTime; + public float Speed; + + public BulletData(Vector3 targetPos, float damage, float lifeTime, float speed) + { + TargetPos = targetPos; + Damage = damage; + LifeTime = lifeTime; + Speed = speed; + } +} +[RequireComponent(typeof(Collider))] +public class BulletBase : MonoBehaviour +{ + // 데이터 + private float _speed = 5f; + private float _damage = 1f; + private float _lifeTime = 10f; + + // 내부용 + private Vector3 _direction = Vector3.forward; + private float _timer; + + public virtual void Initialize(BulletData bulletData) + { + _speed = bulletData.Speed; + _damage = bulletData.Damage; + _lifeTime = bulletData.LifeTime; + + // 발사 위치 기준 목표 방향만 계산 + _direction = (bulletData.TargetPos - transform.position).normalized; + + // 탄환이 바라보는 방향 세팅 + transform.rotation = Quaternion.LookRotation(_direction); + + _timer = 0f; + } + + private void Update() + { + // 1) 이동 + transform.position += _direction * (_speed * Time.deltaTime); + + // 2) 수명 카운트 + _timer += Time.deltaTime; + if (_timer >= _lifeTime) + { + DestroyBullet(); + } + } + + private void OnTriggerEnter(Collider other) + { + // TODO: 주인공 캐릭터를 찾는 로직 추가 필요 + // 주인공 스크립트를 찾아 처리할 것. + var character = other.GetComponent(); + if (character != null) + { + character.TakeDamage(_damage); + DestroyBullet(); + } + } + + protected virtual void DestroyBullet() + { + Debug.Log("## Bullet destroyed"); + Destroy(gameObject); + } +} \ No newline at end of file diff --git a/Assets/Scripts/Character/Enemy/Bullet/BulletBase.cs.meta b/Assets/Scripts/Character/Enemy/Bullet/BulletBase.cs.meta new file mode 100644 index 00000000..a4872865 --- /dev/null +++ b/Assets/Scripts/Character/Enemy/Bullet/BulletBase.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 1c349f971ec844b19d94a06e8f93aca0 +timeCreated: 1745890918 \ No newline at end of file diff --git a/Assets/Scripts/Character/Enemy/Bullet/MagicMissile.cs b/Assets/Scripts/Character/Enemy/Bullet/MagicMissile.cs new file mode 100644 index 00000000..0965588b --- /dev/null +++ b/Assets/Scripts/Character/Enemy/Bullet/MagicMissile.cs @@ -0,0 +1,8 @@ +using System.Collections; +using System.Collections.Generic; +using UnityEngine; + +public class MagicMissile : BulletBase +{ + +} diff --git a/Assets/Scripts/Character/Enemy/Bullet/MagicMissile.cs.meta b/Assets/Scripts/Character/Enemy/Bullet/MagicMissile.cs.meta new file mode 100644 index 00000000..51545c2a --- /dev/null +++ b/Assets/Scripts/Character/Enemy/Bullet/MagicMissile.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: cc3f5b56a395f448c881888076d83cba +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/Character/Enemy/CasterDemonController.cs b/Assets/Scripts/Character/Enemy/CasterDemonController.cs index ad492597..147436cf 100644 --- a/Assets/Scripts/Character/Enemy/CasterDemonController.cs +++ b/Assets/Scripts/Character/Enemy/CasterDemonController.cs @@ -4,16 +4,121 @@ using UnityEngine; public class CasterDemonController : EnemyController { + private bool _doneBattleSequence = true; + private bool _isFirstNoPath = true; + [SerializeField] private Transform teleportTransform; + [SerializeField] private Transform bulletShotPosition; + [SerializeField] private GameObject magicMissilePrefab; + [SerializeField] private GameObject teleportEffectPrefab; public override void BattleSequence() { - // TODO : 배틀 중일 때 루프 - Debug.Log("## 몬스터의 교전 행동 루프"); + // 전투 행동이 이미 진행 중일 경우 실행 막기 + if (_doneBattleSequence) + { + // 전투 행동 시작 + _doneBattleSequence = false; + + // TODO : 배틀 중일 때 루프 + Debug.Log("## 몬스터의 교전 행동 루프"); + Thinking(); + } } + private void Thinking() + { + int selectedPattern = Random.Range(0, 10); + + switch (selectedPattern) + { + case 0: + + case 1: + + case 2: + + case 3: + + case 4: + + case 5: + + case 6: + + case 7: + + case 8: + + case 9: + StartCoroutine(ShotMagicMissile()); + break; + } + } + public override void OnCannotFleeBehaviour() { - Debug.Log("## 몬스터가 막다른 길에 몰려 뭔가 함"); + // 구석에 끼인 경우 탈출 + + Debug.Log("## 텔레포트 시전"); + 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; + } + + // 높이는 변경할 필요 없음 + float fixedY = bulletShotPosition.position.y; + aimPosition.y = fixedY; + + // 3. 그 위치를 바라보고 + transform.LookAt(aimPosition); + + // 4. 미사일 생성 및 초기화 + var missile = Instantiate( + magicMissilePrefab, + bulletShotPosition.position, + transform.rotation + ); + missile.GetComponent() + .Initialize(new BulletData(aimPosition, 5f, 10f, 5f)); + + yield return new WaitForSeconds(0.4f); + } + + // 짧은 텀 후 끝내기 + yield return new WaitForSeconds(1f); + _doneBattleSequence = true; + } + + private void Teleport() + { + if (teleportEffectPrefab != null) + Instantiate(teleportEffectPrefab, transform.position, Quaternion.identity); + + if (Agent != null && teleportTransform != null) + Agent.Warp(teleportTransform.position); + else if (teleportTransform != null) + transform.position = teleportTransform.position; + + if (teleportEffectPrefab != null && teleportTransform != null) + Instantiate(teleportEffectPrefab, teleportTransform.position, Quaternion.identity); + } + + + + } diff --git a/Assets/Scripts/Character/Enemy/EnemyState/Caster/EnemyStateFlee.cs b/Assets/Scripts/Character/Enemy/EnemyState/Caster/EnemyStateFlee.cs index 9445ab69..e6b8d161 100644 --- a/Assets/Scripts/Character/Enemy/EnemyState/Caster/EnemyStateFlee.cs +++ b/Assets/Scripts/Character/Enemy/EnemyState/Caster/EnemyStateFlee.cs @@ -6,7 +6,11 @@ public class EnemyStateFlee :IEnemyState private EnemyController _enemyController; private Transform _playerTransform; private float _fleeDistance = 5f; // 도망치는 거리 - private float _attackRange = 7f; // 공격 범위 + private float _safeRange = 7f; // 공격 범위 + + // 경로 탐색 주기 조절용 + private float _fleeSearchTimer = 0; + private float _fleeThresholdTime = 0.2f; // 막다른길 검사용 private Vector3 _lastPosition; @@ -21,9 +25,11 @@ public class EnemyStateFlee :IEnemyState _playerTransform = _enemyController.TraceTargetTransform; _lastPosition = _enemyController.transform.position; + + _enemyController.Agent.ResetPath(); + _enemyController.Agent.isStopped = false; _stuckTimer = 0f; - - + _fleeSearchTimer = 0; } public void Update() @@ -34,8 +40,28 @@ public class EnemyStateFlee :IEnemyState return; } + float currentDist = Vector3.Distance( + _enemyController.transform.position, + _playerTransform.position + ); + if (currentDist >= _safeRange) + { + // 목적지 리셋 후 전투 시작 + _enemyController.Agent.isStopped = true; + _enemyController.Agent.ResetPath(); + _enemyController.BattleSequence(); + return; + } + FindPositionFlee(); + if (!_enemyController.Agent.pathPending && + _enemyController.Agent.pathStatus == NavMeshPathStatus.PathInvalid) + { + Debug.Log("## 길을 못찾음"); + HandleDeadEnd(); + } + // 막힘 감지 (실제 이동 체크) CheckPath(); @@ -44,45 +70,39 @@ public class EnemyStateFlee :IEnemyState private void CheckPath() { - float distance = Vector3.Distance(_enemyController.transform.position, _playerTransform.position); - if (distance < StuckMoveThreshold) + float moved = Vector3.Distance(_enemyController.transform.position, _lastPosition); + if (moved < StuckMoveThreshold) { _stuckTimer += Time.deltaTime; if (_stuckTimer >= StuckThresholdTime) { - // 막다른 길임 : 대체 행동 실행 + + Debug.Log("## 끼임"); HandleDeadEnd(); _stuckTimer = 0f; } } else { - // 정상적인 길: 배틀 루프 실행 - _enemyController.BattleSequence(); _stuckTimer = 0f; } } private void FindPositionFlee() { + _fleeSearchTimer += Time.deltaTime; + if (_fleeSearchTimer <= _fleeThresholdTime) return; + // 1) 목표 도망 위치 계산 Vector3 fleeDirection = (_enemyController.transform.position - _playerTransform.position).normalized; Vector3 fleeTarget = _enemyController.transform.position + fleeDirection * _fleeDistance; // 2) 경로 계산해 보기 - NavMeshPath path = new NavMeshPath(); - _enemyController.Agent.CalculatePath(fleeTarget, path); + _enemyController.Agent.SetDestination(fleeTarget); - if (path.status == NavMeshPathStatus.PathComplete) - { - // 제대로 도망갈 수 있으면 목적지 설정 - _enemyController.Agent.SetDestination(fleeTarget); - } - else - { - // 막다른 길임 : 대체 행동 실행 - HandleDeadEnd(); - } + // 3) 이동 + _enemyController.Agent.isStopped = false; + _fleeSearchTimer = 0; } private void HandleDeadEnd() @@ -93,16 +113,24 @@ public class EnemyStateFlee :IEnemyState if (NavMesh.SamplePosition(randomDirection, out var hit, (_fleeDistance * 2), NavMesh.AllAreas)) { + // 샘플링에 성공했으면 일단 그 위치로 가 보도록 세팅 + Debug.Log("## 일단 가봄"); _enemyController.Agent.SetDestination(hit.position); - return; + _enemyController.OnCannotFleeBehaviour(); + } + else + { + // 대체 경로도 찾을 수 없는 경우 + Debug.Log("## 대체 경로도 못찾음"); + _enemyController.OnCannotFleeBehaviour(); } - - // 대체 경로도 찾을 수 없는 경우 - _enemyController.OnCannotFleeBehaviour(); } + public void Exit() { + _enemyController.Agent.isStopped = true; + _enemyController.Agent.ResetPath(); _playerTransform = null; _enemyController = null; }