물 보스는 유닛 소환을 막는 스킬을 사용한다.
보스가 나타나기 전 UI
보스 나타난 후 UI
물보스는 일정 체력이 되었을 때 분열하므로 보스 체력을 보여주지 않도록 했다.
보스가 스킬을 사용하면 Boss 스킬이 체력바 아래에 생기고, 소환 버튼에 카운트다운 UI가 생긴다.
저 카운트다운 UI는 물 보스 스킬 사용시에만 사용하므로 동적생성으로 Instantiate 해주었다.
어려웠던 점
물 보스는 분열할 때 자기 자신을 복제한다.
즉 스킬 중복 사용이나 동적 UI 중복 생성, 분열 보스는 다른 Init 로직을 사용하는 등의 예외처리가 굉장히 많이 필요했다.
BlockUnitSpawnSkill 스크립트
using System.Collections;
using UnityEngine;
public class BlockUnitSpawnSkill : CoolTimeSkill
{
[Header("# Block Unit Spawn Skill")]
[SerializeField] private int blockDuration;
[SerializeField] private GameObject uiPrefab;
[Header("# Block Unit Spawn Effect")]
[SerializeField] private GameObject blockEffect;
private Coroutine bossToUseSkillCoroutine;
private Boss_Water waterBoss;
private void Start()
{
waterBoss = boss as Boss_Water;
}
public override void UseSkill()
{
base.UseSkill();
if (IsReadyToUse() && !isStun && !UIManager.Instance.UI_Interface.IsDisableSummonBtn)
{
isReadyToUseSkill = false;
boss.SkillHandler.SetSkillInfo(
ESkillType.CoolTime,
0.7f,
boss.AnimationData.Skill2ParameterHash,
() => {
if (waterBoss.uI_BlockUnitSpawn != null)
waterBoss.uI_BlockUnitSpawn.SetActiveCountDownUI(true);
waterBoss.uI_BlockUnitSpawn.StartBlockUnitSpawn(blockDuration);
if (bossToUseSkillCoroutine != null)
StopCoroutine(bossToUseSkillCoroutine);
bossToUseSkillCoroutine= StartCoroutine(SetBossToUseSkill());}
);
boss.StateMachine.ChangeState(boss.StateMachine.SkillState);
}
}
public override void EndSkill()
{
base.EndSkill();
if (bossToUseSkillCoroutine != null)
StopCoroutine(bossToUseSkillCoroutine);
blockEffect.SetActive(false);
UIManager.Instance.UI_Interface.UnBlockSummonBtn();
UIManager.Instance.UI_Interface.DeActivateBossSkillIndicator();
if (waterBoss.uI_BlockUnitSpawn!=null)
waterBoss.uI_BlockUnitSpawn.DestroyUI();
}
private IEnumerator SetBossToUseSkill()
{
blockEffect.SetActive(true);
yield return moveDelayWaitForSeconds;
blockEffect.SetActive(false);
boss.StateMachine.ChangeState(boss.StateMachine.MoveState);
boss.SkillHandler.FinishSkillCast();
StartCoroutine(SkillCoolTime());
}
}
WaterBoss 스크립트
using System.Collections;
using UnityEngine;
public class Boss_Water : Boss
{
[Header("# 분열 스킬 발동 체력 조건")]
[SerializeField] private float splitPercentage;
[field : Header("# 분열 스킬 최대 사용 가능 횟수")]
[field : SerializeField] public int MaxSplitCount { get; private set; }
[field:Header("# 분열 스킬 현재 사용 가능 횟수")]
[field: SerializeField] public int CurSplitCount { get; private set; }
[SerializeField] private GameObject uiPrefab;
public UI_BlockUnitSpawn uI_BlockUnitSpawn;
private Vector3 originScale;
protected override void Awake()
{
base.Awake();
originScale = transform.localScale;
}
public override void EnemyInit(int key)
{
base.EnemyInit(key);
PatternHandler.OnHealthChangePatternEvent += UseSplitSkill;
CurSplitCount = MaxSplitCount;
InitUI();
transform.localScale = originScale;
UIManager.Instance.UI_Interface.ActivateWBWarningTXT();
}
public override void DEV_EnemyInit(EnemyData data)
{
base.DEV_EnemyInit(data);
PatternHandler.OnHealthChangePatternEvent += UseSplitSkill;
CurSplitCount = MaxSplitCount;
InitUI();
transform.localScale = originScale;
UIManager.Instance.UI_Interface.ActivateWBWarningTXT();
}
protected override void EnemyDie()
{
base.EnemyDie();
PatternHandler.OnHealthChangePatternEvent -= UseSplitSkill;
// 마지막 한 마리가 죽을 때 == 스테이지 끝날 때 DeActivate
if(GameManager.Instance.Stage.SpawnEnemyList.Count ==1)
UIManager.Instance.UI_Interface.DeActivateWBWarningTXT();
}
public void SplitBossInit(EnemyData enemyData, EnemyStat curData, int splitCount, UI_BlockUnitSpawn ui)
{
if(uI_BlockUnitSpawn!=null)
uI_BlockUnitSpawn = ui;
EnemyData = enemyData;
Stat = new EnemyStat(GetRandomSpeed(enemyData.MoveSpeed), curData.MaxHealth, enemyData.Toughness);
StateMachine.ChangeState(StateMachine.MoveState);
HealthSystem.InitHealthSystem(HealthSystem.GetCurHealth());
HealthSystem.SetWayPoint(EffectWayPoint);
AddHealthEvent();
transform.localScale *= 0.7f;
CurSplitCount = splitCount;
}
private void InitUI()
{
if (MaxSplitCount == CurSplitCount)
{
GameObject prefab = Instantiate(uiPrefab);
uI_BlockUnitSpawn = prefab.GetComponent<UI_BlockUnitSpawn>();
uI_BlockUnitSpawn.InitUI();
}
}
public void Split()
{
CurSplitCount--;
}
public void UseSplitSkill(float healthPercentage)
{
if (healthPercentage <= splitPercentage && HealthSystem.GetCurHealth()!=0 && CurSplitCount > 0)
{
SkillHandler.CallSkill(EBossSkill.SplitSkill);
}
}
private float GetRandomSpeed(float baseSpeed)
{
return Random.Range(baseSpeed, baseSpeed + 3f);
}
}
UI_BlockUnitSpawn 스크립트
using TMPro;
using UnityEngine;
using System.Collections;
public class UI_BlockUnitSpawn : MonoBehaviour
{
[SerializeField] private TextMeshProUGUI countDownTXT;
private Coroutine blockUnitSpawn;
private readonly WaitForSeconds oneSeconds = new WaitForSeconds(1);
public void InitUI()
{
SetActiveCountDownUI(false);
transform.SetParent(UIManager.Instance.UI_Interface.countDownTransform, false);
}
public void StartBlockUnitSpawn(int duration)
{
if(blockUnitSpawn != null)
StopCoroutine(blockUnitSpawn);
blockUnitSpawn = StartCoroutine(BlockUnitSpawn(duration));
}
private IEnumerator BlockUnitSpawn(int duration)
{
UIManager.Instance.UI_Interface.BlockSummonBtn();
UIManager.Instance.UI_Interface.ActivateBossSkillIndicator($"보스가 유닛 소환 버튼을 막습니다.");
UpdateSummonCountDownTXT(duration);
while (duration > 0)
{
yield return oneSeconds;
duration--;
UpdateSummonCountDownTXT(duration);
}
UIManager.Instance.UI_Interface.UnBlockSummonBtn();
UIManager.Instance.UI_Interface.DeActivateBossSkillIndicator();
SetActiveCountDownUI(false);
}
public void DestroyUI()
{
if (blockUnitSpawn != null)
StopCoroutine(blockUnitSpawn);
Destroy(gameObject);
}
public void SetActiveCountDownUI(bool b)
{
gameObject.SetActive(b);
}
private void UpdateSummonCountDownTXT(int second)
{
countDownTXT.text = second.ToString();
}
}
'게임 개발' 카테고리의 다른 글
[Unity] 디펜스게임 개발 - 분열 스킬 (1) | 2024.07.31 |
---|---|
[Unity] 디펜스게임 개발 - Hp Bar 최적화 (0) | 2024.07.29 |
Enemy가 웨이포인트 주변에서 부르르 떨리는 이슈 (0) | 2024.07.19 |
[Unity] 보스 키우기 (6) - 완성 (0) | 2024.06.26 |
[Unity] 보스 키우기 (5) - 버프 몬스터 (0) | 2024.06.25 |