Compare commits
2 Commits
a7b89ff861
...
c019acd801
| Author | SHA1 | Date | |
|---|---|---|---|
| c019acd801 | |||
| bc52a08d2b |
1049
Assets/External_Used/UI/UI_BossHealthBar.prefab
Normal file
1049
Assets/External_Used/UI/UI_BossHealthBar.prefab
Normal file
File diff suppressed because it is too large
Load Diff
7
Assets/External_Used/UI/UI_BossHealthBar.prefab.meta
Normal file
7
Assets/External_Used/UI/UI_BossHealthBar.prefab.meta
Normal file
@@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2122b1e1b36684a40978673f272f200e
|
||||
PrefabImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -12,7 +12,7 @@ MonoBehaviour:
|
||||
m_Script: {fileID: 11500000, guid: 1ecdc2379b078b246a0bd5c0fb58e346, type: 3}
|
||||
m_Name: NewEnemyData
|
||||
m_EditorClassIdentifier: Colosseum.Game::Colosseum.Enemy.EnemyData
|
||||
enemyName: "\uD14C\uC2A4\uD2B8 \uBCF4\uC2A4"
|
||||
enemyName: Boss The Test
|
||||
description:
|
||||
icon: {fileID: 21300000, guid: 452012ebe6d33bc4bbb53a355f77ce63, type: 3}
|
||||
baseStrength: 10
|
||||
|
||||
@@ -119,6 +119,17 @@ NavMeshSettings:
|
||||
debug:
|
||||
m_Flags: 0
|
||||
m_NavMeshData: {fileID: 0}
|
||||
--- !u!114 &56646641 stripped
|
||||
MonoBehaviour:
|
||||
m_CorrespondingSourceObject: {fileID: 4278009329655597422, guid: 54087b4bd46db9e4fb7da13cf7a6cc69, type: 3}
|
||||
m_PrefabInstance: {fileID: 437791323}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 0}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: f4688fdb7df04437aeb418b961361dc5, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
--- !u!1 &115810401
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
@@ -251,6 +262,7 @@ RectTransform:
|
||||
- {fileID: 1162990049}
|
||||
- {fileID: 7078605117837265129}
|
||||
- {fileID: 1269404371}
|
||||
- {fileID: 678443228}
|
||||
m_Father: {fileID: 0}
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
m_AnchorMin: {x: 0, y: 0}
|
||||
@@ -427,6 +439,17 @@ MonoBehaviour:
|
||||
m_Script: {fileID: 11500000, guid: b5c5d0fa667f83d4399abb45ffcaea31, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier: Colosseum.Game::Colosseum.UI.StatBar
|
||||
--- !u!114 &378850066 stripped
|
||||
MonoBehaviour:
|
||||
m_CorrespondingSourceObject: {fileID: 6434164023740824016, guid: 2122b1e1b36684a40978673f272f200e, type: 3}
|
||||
m_PrefabInstance: {fileID: 1305809339}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 0}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: f4688fdb7df04437aeb418b961361dc5, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
--- !u!1001 &437791323
|
||||
PrefabInstance:
|
||||
m_ObjectHideFlags: 0
|
||||
@@ -611,6 +634,10 @@ PrefabInstance:
|
||||
propertyPath: m_AnchoredPosition.y
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 4521187534841194808, guid: 54087b4bd46db9e4fb7da13cf7a6cc69, type: 3}
|
||||
propertyPath: valueText
|
||||
value:
|
||||
objectReference: {fileID: 56646641}
|
||||
- target: {fileID: 4759263938882641124, guid: 54087b4bd46db9e4fb7da13cf7a6cc69, type: 3}
|
||||
propertyPath: m_AnchorMax.y
|
||||
value: 0
|
||||
@@ -775,6 +802,10 @@ PrefabInstance:
|
||||
propertyPath: m_Enabled
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 7606115145092636445, guid: 54087b4bd46db9e4fb7da13cf7a6cc69, type: 3}
|
||||
propertyPath: 'm_ActiveFontFeatures.Array.data[0]'
|
||||
value: 1801810542
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 7657599853370186409, guid: 54087b4bd46db9e4fb7da13cf7a6cc69, type: 3}
|
||||
propertyPath: m_SizeDelta.x
|
||||
value: 0
|
||||
@@ -831,6 +862,10 @@ PrefabInstance:
|
||||
propertyPath: m_AnchoredPosition.y
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 8605971422793688558, guid: 54087b4bd46db9e4fb7da13cf7a6cc69, type: 3}
|
||||
propertyPath: m_IsActive
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 8744015103177123418, guid: 54087b4bd46db9e4fb7da13cf7a6cc69, type: 3}
|
||||
propertyPath: m_AnchorMax.y
|
||||
value: 0
|
||||
@@ -1013,6 +1048,34 @@ MonoBehaviour:
|
||||
m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
--- !u!1 &678443227 stripped
|
||||
GameObject:
|
||||
m_CorrespondingSourceObject: {fileID: 1833205063244138202, guid: 2122b1e1b36684a40978673f272f200e, type: 3}
|
||||
m_PrefabInstance: {fileID: 1305809339}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
--- !u!224 &678443228 stripped
|
||||
RectTransform:
|
||||
m_CorrespondingSourceObject: {fileID: 1384280946776679044, guid: 2122b1e1b36684a40978673f272f200e, type: 3}
|
||||
m_PrefabInstance: {fileID: 1305809339}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
--- !u!114 &678443229
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 678443227}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 892f9842e85256b47b24e0aab016820b, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier: Colosseum.Game::Colosseum.UI.BossHealthBarUI
|
||||
healthSlider: {fileID: 1425382920}
|
||||
healthText: {fileID: 378850066}
|
||||
bossNameText: {fileID: 1150905959}
|
||||
targetBoss: {fileID: 0}
|
||||
hideOnDeath: 1
|
||||
lerpSpeed: 15
|
||||
--- !u!1 &845918606
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
@@ -1335,6 +1398,17 @@ MonoBehaviour:
|
||||
m_ShadowLayerMask: 1
|
||||
m_RenderingLayers: 1
|
||||
m_ShadowRenderingLayers: 1
|
||||
--- !u!114 &1150905959 stripped
|
||||
MonoBehaviour:
|
||||
m_CorrespondingSourceObject: {fileID: 7053117599274393893, guid: 2122b1e1b36684a40978673f272f200e, type: 3}
|
||||
m_PrefabInstance: {fileID: 1305809339}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 0}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: f4688fdb7df04437aeb418b961361dc5, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
--- !u!1 &1162990048
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
@@ -1497,6 +1571,125 @@ MonoBehaviour:
|
||||
slotPrefab: {fileID: 5402880947663824673, guid: 60d898ecea82b6c429850e04d2e95b7c, type: 3}
|
||||
maxSlots: 10
|
||||
autoFindPlayer: 1
|
||||
--- !u!1001 &1305809339
|
||||
PrefabInstance:
|
||||
m_ObjectHideFlags: 0
|
||||
serializedVersion: 2
|
||||
m_Modification:
|
||||
serializedVersion: 3
|
||||
m_TransformParent: {fileID: 260528176}
|
||||
m_Modifications:
|
||||
- target: {fileID: 1384280946776679044, guid: 2122b1e1b36684a40978673f272f200e, type: 3}
|
||||
propertyPath: m_Pivot.x
|
||||
value: 0.5
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 1384280946776679044, guid: 2122b1e1b36684a40978673f272f200e, type: 3}
|
||||
propertyPath: m_Pivot.y
|
||||
value: 1
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 1384280946776679044, guid: 2122b1e1b36684a40978673f272f200e, type: 3}
|
||||
propertyPath: m_AnchorMax.x
|
||||
value: 0.5
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 1384280946776679044, guid: 2122b1e1b36684a40978673f272f200e, type: 3}
|
||||
propertyPath: m_AnchorMax.y
|
||||
value: 1
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 1384280946776679044, guid: 2122b1e1b36684a40978673f272f200e, type: 3}
|
||||
propertyPath: m_AnchorMin.x
|
||||
value: 0.5
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 1384280946776679044, guid: 2122b1e1b36684a40978673f272f200e, type: 3}
|
||||
propertyPath: m_AnchorMin.y
|
||||
value: 1
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 1384280946776679044, guid: 2122b1e1b36684a40978673f272f200e, type: 3}
|
||||
propertyPath: m_SizeDelta.x
|
||||
value: 1000
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 1384280946776679044, guid: 2122b1e1b36684a40978673f272f200e, type: 3}
|
||||
propertyPath: m_SizeDelta.y
|
||||
value: 40
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 1384280946776679044, guid: 2122b1e1b36684a40978673f272f200e, type: 3}
|
||||
propertyPath: m_LocalPosition.x
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 1384280946776679044, guid: 2122b1e1b36684a40978673f272f200e, type: 3}
|
||||
propertyPath: m_LocalPosition.y
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 1384280946776679044, guid: 2122b1e1b36684a40978673f272f200e, type: 3}
|
||||
propertyPath: m_LocalPosition.z
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 1384280946776679044, guid: 2122b1e1b36684a40978673f272f200e, type: 3}
|
||||
propertyPath: m_LocalRotation.w
|
||||
value: 1
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 1384280946776679044, guid: 2122b1e1b36684a40978673f272f200e, type: 3}
|
||||
propertyPath: m_LocalRotation.x
|
||||
value: -0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 1384280946776679044, guid: 2122b1e1b36684a40978673f272f200e, type: 3}
|
||||
propertyPath: m_LocalRotation.y
|
||||
value: -0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 1384280946776679044, guid: 2122b1e1b36684a40978673f272f200e, type: 3}
|
||||
propertyPath: m_LocalRotation.z
|
||||
value: -0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 1384280946776679044, guid: 2122b1e1b36684a40978673f272f200e, type: 3}
|
||||
propertyPath: m_AnchoredPosition.x
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 1384280946776679044, guid: 2122b1e1b36684a40978673f272f200e, type: 3}
|
||||
propertyPath: m_AnchoredPosition.y
|
||||
value: -70
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 1384280946776679044, guid: 2122b1e1b36684a40978673f272f200e, type: 3}
|
||||
propertyPath: m_LocalEulerAnglesHint.x
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 1384280946776679044, guid: 2122b1e1b36684a40978673f272f200e, type: 3}
|
||||
propertyPath: m_LocalEulerAnglesHint.y
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 1384280946776679044, guid: 2122b1e1b36684a40978673f272f200e, type: 3}
|
||||
propertyPath: m_LocalEulerAnglesHint.z
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 1833205063244138202, guid: 2122b1e1b36684a40978673f272f200e, type: 3}
|
||||
propertyPath: m_Name
|
||||
value: UI_BossHealthBar
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 2971862343577199805, guid: 2122b1e1b36684a40978673f272f200e, type: 3}
|
||||
propertyPath: m_AnchorMax.x
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 2971862343577199805, guid: 2122b1e1b36684a40978673f272f200e, type: 3}
|
||||
propertyPath: m_AnchorMax.y
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
m_RemovedComponents: []
|
||||
m_RemovedGameObjects: []
|
||||
m_AddedGameObjects: []
|
||||
m_AddedComponents:
|
||||
- targetCorrespondingSourceObject: {fileID: 1833205063244138202, guid: 2122b1e1b36684a40978673f272f200e, type: 3}
|
||||
insertIndex: -1
|
||||
addedObject: {fileID: 678443229}
|
||||
m_SourcePrefab: {fileID: 100100000, guid: 2122b1e1b36684a40978673f272f200e, type: 3}
|
||||
--- !u!114 &1425382920 stripped
|
||||
MonoBehaviour:
|
||||
m_CorrespondingSourceObject: {fileID: 1229044099469274636, guid: 2122b1e1b36684a40978673f272f200e, type: 3}
|
||||
m_PrefabInstance: {fileID: 1305809339}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 0}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 67db9e8f0e2ae9c40bc1e2b64352a6b4, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
--- !u!1 &1432187382
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
@@ -1900,7 +2093,7 @@ Camera:
|
||||
height: 1
|
||||
near clip plane: 0.3
|
||||
far clip plane: 1000
|
||||
field of view: 60
|
||||
field of view: 80
|
||||
orthographic: 0
|
||||
orthographic size: 5
|
||||
m_Depth: -1
|
||||
@@ -1927,7 +2120,7 @@ Transform:
|
||||
m_GameObject: {fileID: 2122318093}
|
||||
serializedVersion: 2
|
||||
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
|
||||
m_LocalPosition: {x: 0, y: 1, z: -10}
|
||||
m_LocalPosition: {x: 0, y: 5, z: -10}
|
||||
m_LocalScale: {x: 1, y: 1, z: 1}
|
||||
m_ConstrainProportionsScale: 0
|
||||
m_Children: []
|
||||
@@ -2036,7 +2229,7 @@ PrefabInstance:
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 5581648761285601425, guid: 56986b707b0dc09439cb35ff2f87dcc9, type: 3}
|
||||
propertyPath: m_Name
|
||||
value: SM_Chr_BR_Troll_01
|
||||
value: TestBoss
|
||||
objectReference: {fileID: 0}
|
||||
m_RemovedComponents: []
|
||||
m_RemovedGameObjects: []
|
||||
|
||||
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();
|
||||
|
||||
248
Assets/Scripts/UI/BossHealthBarUI.cs
Normal file
248
Assets/Scripts/UI/BossHealthBarUI.cs
Normal file
@@ -0,0 +1,248 @@
|
||||
using System;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
using TMPro;
|
||||
|
||||
using Colosseum.Enemy;
|
||||
|
||||
namespace Colosseum.UI
|
||||
{
|
||||
/// <summary>
|
||||
/// 보스 체력바 UI 컴포넌트.
|
||||
/// BossEnemy의 체력 변화를 자동으로 UI에 반영합니다.
|
||||
/// </summary>
|
||||
public class BossHealthBarUI : MonoBehaviour
|
||||
{
|
||||
[Header("References")]
|
||||
[Tooltip("체력 슬라이더 (없으면 자동 검색)")]
|
||||
[SerializeField] private Slider healthSlider;
|
||||
|
||||
[Tooltip("체력 텍스트 (예: '999 / 999')")]
|
||||
[SerializeField] private TMP_Text healthText;
|
||||
|
||||
[Tooltip("보스 이름 텍스트")]
|
||||
[SerializeField] private TMP_Text bossNameText;
|
||||
|
||||
[Header("Target")]
|
||||
[Tooltip("추적할 보스 (런타임에 설정 가능)")]
|
||||
[SerializeField] private BossEnemy targetBoss;
|
||||
|
||||
[Header("Settings")]
|
||||
[Tooltip("보스 사망 시 UI 숨김 여부")]
|
||||
[SerializeField] private bool hideOnDeath = true;
|
||||
|
||||
[Tooltip("슬라이더 값 변환 속도")]
|
||||
[Min(0f)] [SerializeField] private float lerpSpeed = 5f;
|
||||
|
||||
private float displayHealthRatio;
|
||||
private float targetHealthRatio;
|
||||
private bool isSubscribed;
|
||||
private bool isSubscribedToStaticEvent;
|
||||
|
||||
/// <summary>
|
||||
/// 현재 추적 중인 보스
|
||||
/// </summary>
|
||||
public BossEnemy TargetBoss => targetBoss;
|
||||
|
||||
/// <summary>
|
||||
/// 보스 수동 설정 (런타임에서 호출)
|
||||
/// </summary>
|
||||
public void SetBoss(BossEnemy boss)
|
||||
{
|
||||
// 기존 보스 이벤트 구독 해제
|
||||
UnsubscribeFromBoss();
|
||||
|
||||
targetBoss = boss;
|
||||
|
||||
// 새 보스 이벤트 구독
|
||||
SubscribeToBoss();
|
||||
|
||||
// 초기 UI 업데이트
|
||||
if (targetBoss != null)
|
||||
{
|
||||
UpdateBossName();
|
||||
UpdateHealthImmediate();
|
||||
gameObject.SetActive(true);
|
||||
}
|
||||
else
|
||||
{
|
||||
gameObject.SetActive(false);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 보스 스폰 이벤트 핸들러
|
||||
/// </summary>
|
||||
private void OnBossSpawned(BossEnemy boss)
|
||||
{
|
||||
if (boss == null)
|
||||
return;
|
||||
|
||||
SetBoss(boss);
|
||||
}
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
// 컴포넌트 자동 검색
|
||||
if (healthSlider == null)
|
||||
healthSlider = GetComponentInChildren<Slider>();
|
||||
|
||||
if (healthText == null)
|
||||
healthText = transform.Find("SliderBox/Label_HP")?.GetComponent<TMP_Text>();
|
||||
|
||||
if (bossNameText == null)
|
||||
bossNameText = transform.Find("SliderBox/Label_BossName")?.GetComponent<TMP_Text>();
|
||||
}
|
||||
|
||||
private void OnEnable()
|
||||
{
|
||||
// 정적 이벤트 구독 (보스 스폰 자동 감지)
|
||||
if (!isSubscribedToStaticEvent)
|
||||
{
|
||||
BossEnemy.OnBossSpawned += OnBossSpawned;
|
||||
isSubscribedToStaticEvent = true;
|
||||
}
|
||||
}
|
||||
private void OnDisable()
|
||||
{
|
||||
// 정적 이벤트 구독 해제
|
||||
if (isSubscribedToStaticEvent)
|
||||
{
|
||||
BossEnemy.OnBossSpawned -= OnBossSpawned;
|
||||
isSubscribedToStaticEvent = false;
|
||||
}
|
||||
}
|
||||
private void Start()
|
||||
{
|
||||
// 이미 활성화된 보스가 있으면 연결
|
||||
if (BossEnemy.ActiveBoss != null)
|
||||
{
|
||||
SetBoss(BossEnemy.ActiveBoss);
|
||||
}
|
||||
// 인스펙터에서 설정된 보스가 있으면 구독
|
||||
else if (targetBoss != null)
|
||||
{
|
||||
SubscribeToBoss();
|
||||
UpdateBossName();
|
||||
UpdateHealthImmediate();
|
||||
}
|
||||
else
|
||||
{
|
||||
// 보스가 없으면 비활성화 (이벤트 대기)
|
||||
gameObject.SetActive(false);
|
||||
}
|
||||
}
|
||||
private void Update()
|
||||
{
|
||||
// 부드러운 체력바 애니메이션
|
||||
if (!Mathf.Approximately(displayHealthRatio, targetHealthRatio))
|
||||
{
|
||||
displayHealthRatio = Mathf.Lerp(displayHealthRatio, targetHealthRatio, lerpSpeed * Time.deltaTime);
|
||||
|
||||
if (Mathf.Abs(displayHealthRatio - targetHealthRatio) < 0.01f)
|
||||
displayHealthRatio = targetHealthRatio;
|
||||
|
||||
UpdateSliderVisual();
|
||||
}
|
||||
}
|
||||
private void OnDestroy()
|
||||
{
|
||||
UnsubscribeFromBoss();
|
||||
|
||||
// 정적 이벤트 구독 해제
|
||||
if (isSubscribedToStaticEvent)
|
||||
{
|
||||
BossEnemy.OnBossSpawned -= OnBossSpawned;
|
||||
isSubscribedToStaticEvent = false;
|
||||
}
|
||||
}
|
||||
private void SubscribeToBoss()
|
||||
{
|
||||
if (targetBoss == null || isSubscribed)
|
||||
return;
|
||||
|
||||
targetBoss.OnHealthChanged += OnBossHealthChanged;
|
||||
targetBoss.OnDeath += OnBossDeath;
|
||||
isSubscribed = true;
|
||||
}
|
||||
private void UnsubscribeFromBoss()
|
||||
{
|
||||
if (targetBoss == null || !isSubscribed)
|
||||
return;
|
||||
|
||||
targetBoss.OnHealthChanged -= OnBossHealthChanged;
|
||||
targetBoss.OnDeath -= OnBossDeath;
|
||||
isSubscribed = false;
|
||||
}
|
||||
private void OnBossHealthChanged(float currentHealth, float maxHealth)
|
||||
{
|
||||
if (maxHealth <= 0f)
|
||||
return;
|
||||
|
||||
targetHealthRatio = Mathf.Clamp01(currentHealth / maxHealth);
|
||||
UpdateHealthText(currentHealth, maxHealth);
|
||||
}
|
||||
private void OnBossDeath()
|
||||
{
|
||||
if (hideOnDeath)
|
||||
{
|
||||
gameObject.SetActive(false);
|
||||
}
|
||||
}
|
||||
private void UpdateHealthImmediate()
|
||||
{
|
||||
if (targetBoss == null)
|
||||
return;
|
||||
|
||||
float currentHealth = targetBoss.CurrentHealth;
|
||||
float maxHealth = targetBoss.MaxHealth;
|
||||
|
||||
if (maxHealth <= 0f)
|
||||
return;
|
||||
|
||||
targetHealthRatio = Mathf.Clamp01(currentHealth / maxHealth);
|
||||
displayHealthRatio = targetHealthRatio;
|
||||
|
||||
UpdateSliderVisual();
|
||||
UpdateHealthText(currentHealth, maxHealth);
|
||||
}
|
||||
private void UpdateSliderVisual()
|
||||
{
|
||||
if (healthSlider != null)
|
||||
{
|
||||
healthSlider.value = displayHealthRatio;
|
||||
}
|
||||
}
|
||||
private void UpdateHealthText(float currentHealth, float maxHealth)
|
||||
{
|
||||
if (healthText != null)
|
||||
{
|
||||
healthText.text = $"{Mathf.CeilToInt(currentHealth)} / {Mathf.CeilToInt(maxHealth)}";
|
||||
}
|
||||
}
|
||||
private void UpdateBossName()
|
||||
{
|
||||
if (bossNameText == null || targetBoss == null)
|
||||
return;
|
||||
|
||||
// EnemyData에서 보스 이름 가져오기
|
||||
if (targetBoss.Data != null && !string.IsNullOrEmpty(targetBoss.Data.EnemyName))
|
||||
{
|
||||
bossNameText.text = targetBoss.Data.EnemyName;
|
||||
}
|
||||
else
|
||||
{
|
||||
// 폴백: GameObject 이름 사용
|
||||
bossNameText.text = targetBoss.name;
|
||||
}
|
||||
}
|
||||
#if UNITY_EDITOR
|
||||
private void OnValidate()
|
||||
{
|
||||
if (healthSlider == null)
|
||||
healthSlider = GetComponentInChildren<Slider>();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
2
Assets/Scripts/UI/BossHealthBarUI.cs.meta
Normal file
2
Assets/Scripts/UI/BossHealthBarUI.cs.meta
Normal file
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 892f9842e85256b47b24e0aab016820b
|
||||
@@ -18,7 +18,7 @@ MonoBehaviour:
|
||||
areaShape: 1
|
||||
targetLayers:
|
||||
serializedVersion: 2
|
||||
m_Bits: 0
|
||||
m_Bits: 4294967295
|
||||
areaRadius: 3
|
||||
fanOriginDistance: 0
|
||||
fanRadius: 3
|
||||
|
||||
@@ -23,6 +23,6 @@ MonoBehaviour:
|
||||
fanOriginDistance: 0
|
||||
fanRadius: 3
|
||||
fanHalfAngle: 45
|
||||
baseDamage: 10
|
||||
baseDamage: 1
|
||||
damageType: 0
|
||||
statScaling: 1
|
||||
|
||||
Reference in New Issue
Block a user