[UI] 보스 체력바 UI 및 영역 진입 트리거 시스템 추가
- BossHealthBarUI: 보스 체력 변화를 자동으로 UI에 반영하는 컴포넌트 - BossArea: 플레이어 진입 시 연결된 보스의 체력바 표시 - BossEnemy: 스폰 이벤트(OnBossSpawned) 추가로 UI 자동 연결 지원 - UI_BossHealthBar.prefab: BossHealthBarUI 컴포넌트 적용
This commit is contained in:
250
Assets/Scripts/Enemy/BossArea.cs
Normal file
250
Assets/Scripts/Enemy/BossArea.cs
Normal file
@@ -0,0 +1,250 @@
|
||||
using System;
|
||||
using UnityEngine;
|
||||
|
||||
using Colosseum.UI;
|
||||
using Colosseum.Player;
|
||||
|
||||
namespace Colosseum.Enemy
|
||||
{
|
||||
/// <summary>
|
||||
/// 보스 영역 트리거.
|
||||
/// 플레이어가 이 영역에 진입하면 연결된 보스의 체력바 UI를 표시합니다.
|
||||
/// </summary>
|
||||
[RequireComponent(typeof(Collider))]
|
||||
public class BossArea : MonoBehaviour
|
||||
{
|
||||
[Header("Boss Reference")]
|
||||
[Tooltip("이 영역에 연결된 보스")]
|
||||
[SerializeField] private BossEnemy boss;
|
||||
|
||||
[Header("UI Settings")]
|
||||
[Tooltip("보스 체력바 UI (없으면 씬에서 자동 검색)")]
|
||||
[SerializeField] private BossHealthBarUI bossHealthBarUI;
|
||||
|
||||
[Header("Trigger Settings")]
|
||||
[Tooltip("플레이어 퇴장 시 UI 숨김 여부")]
|
||||
[SerializeField] private bool hideOnExit = false;
|
||||
|
||||
[Tooltip("영역 진입 시 한 번만 표시")]
|
||||
[SerializeField] private bool showOnceOnly = false;
|
||||
|
||||
// 이벤트
|
||||
/// <summary>
|
||||
/// 플레이어 진입 시 호출
|
||||
/// </summary>
|
||||
public event Action OnPlayerEnter;
|
||||
|
||||
/// <summary>
|
||||
/// 플레이어 퇴장 시 호출
|
||||
/// </summary>
|
||||
public event Action OnPlayerExit;
|
||||
|
||||
// 상태
|
||||
private bool hasShownUI = false;
|
||||
private bool isPlayerInArea = false;
|
||||
private Collider triggerCollider;
|
||||
|
||||
[Header("Debug")]
|
||||
[SerializeField] private bool debugMode = false;
|
||||
|
||||
/// <summary>
|
||||
/// 연결된 보스
|
||||
/// </summary>
|
||||
public BossEnemy Boss => boss;
|
||||
|
||||
/// <summary>
|
||||
/// 플레이어가 영역 내에 있는지 여부
|
||||
/// </summary>
|
||||
public bool IsPlayerInArea => isPlayerInArea;
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
// Collider 설정 확인
|
||||
triggerCollider = GetComponent<Collider>();
|
||||
if (triggerCollider != null && !triggerCollider.isTrigger)
|
||||
{
|
||||
Debug.LogWarning($"[BossArea] {name}: Collider가 Trigger가 아닙니다. 자동으로 Trigger로 설정합니다.");
|
||||
triggerCollider.isTrigger = true;
|
||||
}
|
||||
}
|
||||
|
||||
private void Start()
|
||||
{
|
||||
// BossHealthBarUI 자동 검색
|
||||
if (bossHealthBarUI == null)
|
||||
{
|
||||
bossHealthBarUI = FindObjectOfType<BossHealthBarUI>();
|
||||
if (bossHealthBarUI == null)
|
||||
{
|
||||
Debug.LogWarning($"[BossArea] {name}: BossHealthBarUI를 찾을 수 없습니다.");
|
||||
}
|
||||
}
|
||||
|
||||
// 보스 참조 확인
|
||||
if (boss == null)
|
||||
{
|
||||
Debug.LogWarning($"[BossArea] {name}: 연결된 보스가 없습니다.");
|
||||
}
|
||||
}
|
||||
|
||||
private void OnTriggerEnter(Collider other)
|
||||
{
|
||||
// 이미 표시했고 한 번만 표시 설정이면 무시
|
||||
if (showOnceOnly && hasShownUI)
|
||||
return;
|
||||
|
||||
// 플레이어 확인 (태그 또는 컴포넌트)
|
||||
if (!IsPlayer(other, out var playerController))
|
||||
return;
|
||||
|
||||
isPlayerInArea = true;
|
||||
ShowBossHealthBar();
|
||||
|
||||
OnPlayerEnter?.Invoke();
|
||||
|
||||
if (debugMode)
|
||||
Debug.Log($"[BossArea] {name}: 플레이어 진입 - 보스: {boss?.name ?? "없음"}");
|
||||
}
|
||||
|
||||
private void OnTriggerExit(Collider other)
|
||||
{
|
||||
// 플레이어 확인
|
||||
if (!IsPlayer(other, out var playerController))
|
||||
return;
|
||||
|
||||
isPlayerInArea = false;
|
||||
|
||||
if (hideOnExit)
|
||||
{
|
||||
HideBossHealthBar();
|
||||
}
|
||||
|
||||
OnPlayerExit?.Invoke();
|
||||
|
||||
if (debugMode)
|
||||
Debug.Log($"[BossArea] {name}: 플레이어 퇴장");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 보스 체력바 표시
|
||||
/// </summary>
|
||||
public void ShowBossHealthBar()
|
||||
{
|
||||
if (boss == null || bossHealthBarUI == null)
|
||||
return;
|
||||
|
||||
// BossHealthBarUI에 보스 설정
|
||||
bossHealthBarUI.SetBoss(boss);
|
||||
hasShownUI = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 보스 체력바 숨김
|
||||
/// </summary>
|
||||
public void HideBossHealthBar()
|
||||
{
|
||||
if (bossHealthBarUI == null)
|
||||
return;
|
||||
|
||||
bossHealthBarUI.gameObject.SetActive(false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 플레이어 여부 확인
|
||||
/// </summary>
|
||||
private bool IsPlayer(Collider other, out PlayerNetworkController playerController)
|
||||
{
|
||||
playerController = null;
|
||||
|
||||
// 1. 태그로 확인
|
||||
if (other.CompareTag("Player"))
|
||||
{
|
||||
playerController = other.GetComponent<PlayerNetworkController>();
|
||||
return true;
|
||||
}
|
||||
|
||||
// 2. 컴포넌트로 확인
|
||||
playerController = other.GetComponent<PlayerNetworkController>();
|
||||
if (playerController != null)
|
||||
return true;
|
||||
|
||||
// 3. 부모에서 검색
|
||||
playerController = other.GetComponentInParent<PlayerNetworkController>();
|
||||
return playerController != null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 보스 수동 설정
|
||||
/// </summary>
|
||||
public void SetBoss(BossEnemy newBoss)
|
||||
{
|
||||
boss = newBoss;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// UI 수동 설정
|
||||
/// </summary>
|
||||
public void SetHealthBarUI(BossHealthBarUI ui)
|
||||
{
|
||||
bossHealthBarUI = ui;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 상태 초기화 (재진입 허용)
|
||||
/// </summary>
|
||||
public void ResetState()
|
||||
{
|
||||
hasShownUI = false;
|
||||
isPlayerInArea = false;
|
||||
}
|
||||
|
||||
#region Debug Gizmos
|
||||
|
||||
private void OnDrawGizmos()
|
||||
{
|
||||
if (!debugMode)
|
||||
return;
|
||||
|
||||
// 영역 시각화
|
||||
Gizmos.color = new Color(1f, 0.5f, 0f, 0.3f); // 주황색 반투명
|
||||
|
||||
var col = GetComponent<Collider>();
|
||||
if (col is BoxCollider boxCol)
|
||||
{
|
||||
Gizmos.matrix = transform.localToWorldMatrix;
|
||||
Gizmos.DrawCube(boxCol.center, boxCol.size);
|
||||
}
|
||||
else if (col is SphereCollider sphereCol)
|
||||
{
|
||||
Gizmos.DrawSphere(transform.position + sphereCol.center, sphereCol.radius);
|
||||
}
|
||||
else if (col is CapsuleCollider capsuleCol)
|
||||
{
|
||||
// 캡슐은 구+실린더로 근접 표현
|
||||
Gizmos.DrawWireSphere(transform.position + capsuleCol.center, capsuleCol.radius);
|
||||
}
|
||||
|
||||
// 보스 연결 표시
|
||||
if (boss != null)
|
||||
{
|
||||
Gizmos.color = Color.red;
|
||||
Gizmos.DrawLine(transform.position, boss.transform.position);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnDrawGizmosSelected()
|
||||
{
|
||||
// 선택 시 더 명확하게 표시
|
||||
Gizmos.color = new Color(1f, 0.3f, 0f, 0.5f);
|
||||
|
||||
var col = GetComponent<Collider>();
|
||||
if (col is BoxCollider boxCol)
|
||||
{
|
||||
Gizmos.matrix = transform.localToWorldMatrix;
|
||||
Gizmos.DrawCube(boxCol.center, boxCol.size);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
2
Assets/Scripts/Enemy/BossArea.cs.meta
Normal file
2
Assets/Scripts/Enemy/BossArea.cs.meta
Normal file
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1735054b0ca6d674b99668aeb74ba273
|
||||
@@ -47,6 +47,16 @@ namespace Colosseum.Enemy
|
||||
public event System.Action<int> OnPhaseChanged; // phaseIndex
|
||||
public event System.Action<float> OnPhaseTransitionStart; // transitionDuration
|
||||
public event System.Action OnPhaseTransitionEnd;
|
||||
// 정적 이벤트 (UI 자동 연결용)
|
||||
/// <summary>
|
||||
/// 보스 스폰 시 발생하는 정적 이벤트
|
||||
/// </summary>
|
||||
public static event System.Action<BossEnemy> OnBossSpawned;
|
||||
|
||||
/// <summary>
|
||||
/// 현재 활성화된 보스 (Scene에 하나만 존재한다고 가정)
|
||||
/// </summary>
|
||||
public static BossEnemy ActiveBoss { get; private set; }
|
||||
|
||||
// Properties
|
||||
public int CurrentPhaseIndex => currentPhaseIndex;
|
||||
@@ -71,8 +81,18 @@ namespace Colosseum.Enemy
|
||||
{
|
||||
behaviorAgent.Graph = initialBehaviorGraph;
|
||||
}
|
||||
|
||||
// 정적 이벤트 발생 (UI 자동 연결용)
|
||||
ActiveBoss = this;
|
||||
OnBossSpawned?.Invoke(this);
|
||||
|
||||
if (debugMode)
|
||||
{
|
||||
Debug.Log($"[Boss] Boss spawned: {name}");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
protected override void InitializeStats()
|
||||
{
|
||||
base.InitializeStats();
|
||||
|
||||
Reference in New Issue
Block a user