DEG-100 원거리-마법사-보스-몬스터-개발-단계 #3
8
Assets/JYY/Prefabs/Bullets.meta
Normal file
8
Assets/JYY/Prefabs/Bullets.meta
Normal file
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 37e30a87d9c904c898232118c82b5fbc
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
BIN
Assets/JYY/Prefabs/Bullets/Dummy Magic Missaile.prefab
(Stored with Git LFS)
Normal file
BIN
Assets/JYY/Prefabs/Bullets/Dummy Magic Missaile.prefab
(Stored with Git LFS)
Normal file
Binary file not shown.
@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9c16b44c8736e4007ad5f0733ce433e1
|
||||
PrefabImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
BIN
Assets/JYY/Prefabs/[Enemy] Dummy Monster.prefab
(Stored with Git LFS)
Normal file
BIN
Assets/JYY/Prefabs/[Enemy] Dummy Monster.prefab
(Stored with Git LFS)
Normal file
Binary file not shown.
7
Assets/JYY/Prefabs/[Enemy] Dummy Monster.prefab.meta
Normal file
7
Assets/JYY/Prefabs/[Enemy] Dummy Monster.prefab.meta
Normal file
@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 06eebdb9d2c03437fb632d9e15fd1078
|
||||
PrefabImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
BIN
Assets/JYY/Scenes/MonsterTest.unity
(Stored with Git LFS)
BIN
Assets/JYY/Scenes/MonsterTest.unity
(Stored with Git LFS)
Binary file not shown.
8
Assets/Scripts/Character/Enemy/Bullet.meta
Normal file
8
Assets/Scripts/Character/Enemy/Bullet.meta
Normal file
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a636af603b3af4d1baccb8296add7485
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
75
Assets/Scripts/Character/Enemy/Bullet/BulletBase.cs
Normal file
75
Assets/Scripts/Character/Enemy/Bullet/BulletBase.cs
Normal file
@ -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<CharacterBase>();
|
||||
if (character != null)
|
||||
{
|
||||
character.TakeDamage(_damage);
|
||||
DestroyBullet();
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual void DestroyBullet()
|
||||
{
|
||||
Debug.Log("## Bullet destroyed");
|
||||
Destroy(gameObject);
|
||||
}
|
||||
}
|
3
Assets/Scripts/Character/Enemy/Bullet/BulletBase.cs.meta
Normal file
3
Assets/Scripts/Character/Enemy/Bullet/BulletBase.cs.meta
Normal file
@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1c349f971ec844b19d94a06e8f93aca0
|
||||
timeCreated: 1745890918
|
8
Assets/Scripts/Character/Enemy/Bullet/MagicMissile.cs
Normal file
8
Assets/Scripts/Character/Enemy/Bullet/MagicMissile.cs
Normal file
@ -0,0 +1,8 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
public class MagicMissile : BulletBase
|
||||
{
|
||||
|
||||
}
|
11
Assets/Scripts/Character/Enemy/Bullet/MagicMissile.cs.meta
Normal file
11
Assets/Scripts/Character/Enemy/Bullet/MagicMissile.cs.meta
Normal file
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: cc3f5b56a395f448c881888076d83cba
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -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()
|
||||
{
|
||||
// 전투 행동이 이미 진행 중일 경우 실행 막기
|
||||
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<Rigidbody>(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<MagicMissile>()
|
||||
.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);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
@ -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);
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public void Exit()
|
||||
{
|
||||
_enemyController.Agent.isStopped = true;
|
||||
_enemyController.Agent.ResetPath();
|
||||
_playerTransform = null;
|
||||
_enemyController = null;
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user