Degulleo3D/Assets/Scripts/Character/Enemy/CasterDemonController.cs
fiore 13588aa693 [feat] 슬로우 디버프 마법 생성
- 몬스터 에셋 적용
- 슬로우 마법 기능 추가

Work in JIRA ISSUE DEG-100
2025-05-01 15:03:23 +09:00

223 lines
5.9 KiB
C#

using System;
using System.Collections;
using System.Collections.Generic;
using Unity.VisualScripting;
using UnityEngine;
using UnityEngine.AI;
using Random = UnityEngine.Random;
public class CasterDemonController : EnemyController
{
// Animation
public static readonly int Cast = Animator.StringToHash("Cast");
public static readonly int Flee = Animator.StringToHash("Flee");
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;
[Header("각종 데미지 이펙트 세트")]
[SerializeField] private GameObject chariotWarning;
[SerializeField] private GameObject chariotEffect;
[Space(10)]
[SerializeField] private GameObject slowFieldWarning;
[SerializeField] private GameObject slowFieldEffect;
private float _teleportDistance = 4f; // 플레이어 뒤로 떨어질 거리
// 텔레포트 쿨타임
private float _teleportTimer = 0;
private const float TeleportThresholdTime = 20f;
private bool CanTeleport {
get
{
if (_teleportTimer >= TeleportThresholdTime )
{
_teleportTimer = 0;
return true;
}
return false;
}
}
private void LateUpdate()
{
_teleportTimer += Time.deltaTime;
}
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:
// SetSequence(ShotMagicMissile());
SetSequence(SlowFieldSpell());
break;
}
}
public override void OnCannotFleeBehaviour(Action action)
{
if (CanTeleport)
{
action();
Teleport();
}
}
private IEnumerator ShotMagicMissile()
{
for (int i = 0; i < 3; i++)
{
var aimPosition = TargetPosOracle(out var basePos, out var rb);
// 플레이어 위치를 바라보고
transform.LookAt(aimPosition);
// 미사일 생성 및 초기화
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 Vector3 TargetPosOracle(out Vector3 basePos, out Rigidbody rb)
{
// 1. 기본 위치
basePos = TraceTargetTransform.position;
Vector3 aimPosition = basePos;
// 2. 플레이어 Rigidbody로 속도 얻기
if (TraceTargetTransform.TryGetComponent<Rigidbody>(out rb))
{
// 아주 짧은 시간만 예측
float predictionTime = 0.3f;
aimPosition += rb.velocity * predictionTime;
}
// 높이는 변경할 필요 없음
float fixedY = bulletShotPosition.position.y;
aimPosition.y = fixedY;
return aimPosition;
}
private void Teleport()
{
Vector3 startPos = transform.position;
if (teleportEffectPrefab != null)
Instantiate(teleportEffectPrefab, startPos, Quaternion.identity);
// 텔레포트와 함께 시전하는 범위 공격
var aoe = Instantiate(chariotWarning, startPos, Quaternion.identity).GetComponent<ChariotAoeController>();
var effectData = new DamageEffectData
{
damage = (int)attackPower,
radius = 10,
delay = 1.5f,
targetLayer = TargetLayerMask,
explosionEffectPrefab = chariotEffect
};
aoe.SetEffect(effectData, null, null);
// 중앙으로 이동
Agent.Warp(Vector3.zero);
if (teleportEffectPrefab != null)
Instantiate(teleportEffectPrefab, Vector3.zero, Quaternion.identity);
}
private IEnumerator SlowFieldSpell()
{
var aimPosition = TargetPosOracle(out var basePos, out var rb);
// 1. 시전 애니메이션
transform.LookAt(aimPosition);
SetAnimation(Cast);
// 2. 장판 생성과 세팅
var effectData = new DamageEffectData
{
damage = 0,
radius = 7.5f,
delay = 2.5f,
targetLayer = TargetLayerMask,
explosionEffectPrefab = slowFieldEffect
};
var fixedPos = new Vector3(aimPosition.x, 0, aimPosition.z);
var warning = Instantiate(chariotWarning, fixedPos, Quaternion.identity).GetComponent<MagicAoEField>();
warning.SetEffect(effectData, null, null);
// TODO : 효과 적용
// 3. 짧은 텀 후 끝내기
yield return new WaitForSeconds(1f);
_doneBattleSequence = true;
}
private void SetSequence(IEnumerator newSequence)
{
if (_currentSequence != null)
{
StopCoroutine(_currentSequence);
}
_currentSequence = StartCoroutine(newSequence);
}
}