From 57333a853e2573eac150e44232f94c584d1c7d5b Mon Sep 17 00:00:00 2001 From: "DESKTOP-VVOCIJO\\PC" Date: Wed, 20 May 2026 13:05:49 +0900 Subject: [PATCH] =?UTF-8?q?=ED=94=8C=EB=A0=88=EC=9D=B4=EC=96=B4=20HPBar?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Assets/01_Scenes/GameScene.unity | 4 +- Assets/02_Scripts/Combat/ComboNode.cs | 27 +++- Assets/02_Scripts/Player/PlayerController.cs | 6 + Assets/02_Scripts/UI/PlayerHpBarUI.cs | 160 +++++++++++++++++++ Assets/02_Scripts/UI/PlayerHpBarUI.cs.meta | 2 + Assets/05_Data/Attack/GunFire.asset | 4 +- 6 files changed, 198 insertions(+), 5 deletions(-) create mode 100644 Assets/02_Scripts/UI/PlayerHpBarUI.cs create mode 100644 Assets/02_Scripts/UI/PlayerHpBarUI.cs.meta diff --git a/Assets/01_Scenes/GameScene.unity b/Assets/01_Scenes/GameScene.unity index 32690d9..ebd6f21 100644 --- a/Assets/01_Scenes/GameScene.unity +++ b/Assets/01_Scenes/GameScene.unity @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1526bd6b02015358905505ed05ff0ed253cd5c6d68abd3784b1fdf153c3629f8 -size 64083 +oid sha256:2d3656c07d27be100355d7cefa18109c52754839004e07647000b72aa122e7a2 +size 76524 diff --git a/Assets/02_Scripts/Combat/ComboNode.cs b/Assets/02_Scripts/Combat/ComboNode.cs index b5119ef..64b346f 100644 --- a/Assets/02_Scripts/Combat/ComboNode.cs +++ b/Assets/02_Scripts/Combat/ComboNode.cs @@ -35,13 +35,38 @@ public class ComboTransition public float ForwardStepDuration = 0.1f;// 전진 동작 시간 } +// 공격 방향. 방향키 입력에 따라 같은 노드라도 다른 액션을 낼 수 있다. +// Neutral = 방향키 없음(제자리) / Forward = 바라보는 방향 / Back = 반대 방향 +// Up = 위 입력 / Down = 아래 입력 +// 대각선 입력은 위/아래를 우선한다 (PlayerController.GetAttackDirection 참고). +public enum AttackDirection +{ + Neutral, + Forward, + Back, + Up, + Down, +} + +// 한 노드의 방향별 액션 변형. ComboNode.DirectionalVariants 배열의 항목. +// 예: 같은 펀치 노드라도 Forward면 전진 펀치, Up이면 어퍼컷. +[Serializable] +public class DirectionalAction +{ + public AttackDirection Direction; // 이 변형이 발동할 입력 방향 + public ActionData Action; // 해당 방향일 때 수행할 액션 +} + // 콤보 트리의 노드. .asset 파일로 관리. [CreateAssetMenu(fileName = "ComboNode", menuName = "Combat/ComboNode")] public class ComboNode : ScriptableObject { public string NodeName; // Inspector 식별용 [FormerlySerializedAs("Attack")] - public ActionData Action; // 이 노드에 진입했을 때 수행할 액션 + public ActionData Action; // 기본(Neutral) 액션 — 방향키 입력이 없을 때 수행 + // 방향키를 누른 채 공격하면 이 목록에서 일치하는 방향의 액션으로 대체된다. + // 비어 있거나 일치하는 방향이 없으면 Action(기본)으로 폴백. + public DirectionalAction[] DirectionalVariants; public float ComboWindow = 0.8f; // 이 노드에서 다음 입력 받을 수 있는 시간 public ComboTransition[] Transitions; // 다음 노드들 (입력별로 분기) } diff --git a/Assets/02_Scripts/Player/PlayerController.cs b/Assets/02_Scripts/Player/PlayerController.cs index f8f88c2..29b0f1a 100644 --- a/Assets/02_Scripts/Player/PlayerController.cs +++ b/Assets/02_Scripts/Player/PlayerController.cs @@ -119,6 +119,7 @@ public class PlayerController : MonoBehaviour,IDamageable private Rigidbody2D _rb; private Animator _anim; private SpriteRenderer _spriteRenderer; // 좌우 반전 + flipX 페이싱용 + private Health _health; // ─── 무기 시스템 ──────────────────────────────────────────────────── // PlayerWeaponInventory가 현재 장착 무기 관리. 무기 교체 시 OnWeaponChanged 이벤트 발화. @@ -131,6 +132,7 @@ private void Awake() _rb = GetComponent(); _anim = GetComponent(); _spriteRenderer = GetComponentInChildren(); + _health = GetComponent(); ResolveBodyColliderReference(); EnsureAttackHitbox(); // 공격이 enemy를 hit하면 _lastHitEnemy를 기억 → 다음 잡기에서 그 enemy를 우선 타겟팅. @@ -1578,5 +1580,9 @@ private void DrawTimelineBar(ActionData data, float elapsed) public void TakeDamage(int amount, Vector2 hitVelocity = default, string hitReactionAnimationState = null, Vector2? hitTargetPosition = null, bool correctHitTargetY = false, int hitPositionSolidMask = 0, float hitPositionCorrectionDuration = 0) { + if (_health == null || _health.IsDead) return; + + _health.TakeDamage(amount); + Debug.Log($"{name} 피격: -{amount} (HP: {_health.CurrentHealth}/{_health.MaxHealth})"); } } diff --git a/Assets/02_Scripts/UI/PlayerHpBarUI.cs b/Assets/02_Scripts/UI/PlayerHpBarUI.cs new file mode 100644 index 0000000..18c8241 --- /dev/null +++ b/Assets/02_Scripts/UI/PlayerHpBarUI.cs @@ -0,0 +1,160 @@ +using UnityEngine; +using UnityEngine.UI; + +// Screen-space HP bar for the player HUD. +// Enemy HP bars can keep using the SpriteRenderer-based HpBar. +public class PlayerHpBarUI : MonoBehaviour +{ + [SerializeField] private Health _health; + [SerializeField] private Image _fillImage; + [SerializeField] private bool _autoFindPlayerHealth = true; + [SerializeField] private bool _autoFindFillImageInChildren = true; + [SerializeField] private bool _configureAsFilledImage = true; + [SerializeField] private float _smoothSpeed = 0f; + + [Header("Color Thresholds")] + [SerializeField] private Color _highColor = new Color(0.2f, 0.9f, 0.3f, 1f); + [SerializeField] private Color _midColor = new Color(1f, 0.85f, 0.2f, 1f); + [SerializeField] private Color _lowColor = new Color(0.95f, 0.25f, 0.25f, 1f); + [SerializeField, Range(0f, 1f)] private float _midThreshold = 0.5f; + [SerializeField, Range(0f, 1f)] private float _lowThreshold = 0.2f; + + private float _currentRatio = 1f; + private float _targetRatio = 1f; + + private void Awake() + { + ResolveFillImage(); + ResolveHealth(); + ConfigureFillImage(); + } + + private void OnEnable() + { + ResolveFillImage(); + ResolveHealth(); + ConfigureFillImage(); + + if (_health == null) return; + + _health.OnHealthChanged += HandleHealthChanged; + HandleHealthChanged(_health.CurrentHealth, _health.MaxHealth); + } + + private void OnDisable() + { + if (_health != null) + _health.OnHealthChanged -= HandleHealthChanged; + } + + private void Start() + { + ResolveFillImage(); + ResolveHealth(); + ConfigureFillImage(); + + if (_health != null) + HandleHealthChanged(_health.CurrentHealth, _health.MaxHealth); + } + + private void Update() + { + if (_fillImage == null || _smoothSpeed <= 0f) return; + if (Mathf.Approximately(_currentRatio, _targetRatio)) return; + + _currentRatio = Mathf.MoveTowards(_currentRatio, _targetRatio, _smoothSpeed * Time.deltaTime); + ApplyFill(); + } + + public void Bind(Health health) + { + if (_health == health) return; + + if (isActiveAndEnabled && _health != null) + _health.OnHealthChanged -= HandleHealthChanged; + + _health = health; + + if (isActiveAndEnabled && _health != null) + { + _health.OnHealthChanged += HandleHealthChanged; + HandleHealthChanged(_health.CurrentHealth, _health.MaxHealth); + } + } + + private void HandleHealthChanged(int current, int max) + { + _targetRatio = max > 0 ? Mathf.Clamp01((float)current / max) : 0f; + + if (_smoothSpeed <= 0f) + { + _currentRatio = _targetRatio; + ApplyFill(); + } + + ApplyColor(_targetRatio); + } + + private void ApplyFill() + { + if (_fillImage == null) return; + _fillImage.fillAmount = Mathf.Clamp01(_currentRatio); + } + + private void ApplyColor(float ratio) + { + if (_fillImage == null) return; + + if (ratio <= _lowThreshold) + _fillImage.color = _lowColor; + else if (ratio <= _midThreshold) + _fillImage.color = _midColor; + else + _fillImage.color = _highColor; + } + + private void ResolveHealth() + { + if (_health != null || !_autoFindPlayerHealth) return; + + PlayerController player = FindFirstObjectByType(); + if (player != null) + _health = player.GetComponent(); + } + + private void ResolveFillImage() + { + if (_fillImage != null || !_autoFindFillImageInChildren) return; + + Transform fill = FindChildByName(transform, "Fill"); + if (fill != null) + _fillImage = fill.GetComponent(); + } + + private Transform FindChildByName(Transform parent, string childName) + { + if (parent == null) return null; + + for (int i = 0; i < parent.childCount; i++) + { + Transform child = parent.GetChild(i); + if (child.name == childName) + return child; + + Transform nested = FindChildByName(child, childName); + if (nested != null) + return nested; + } + + return null; + } + + private void ConfigureFillImage() + { + if (_fillImage == null || !_configureAsFilledImage) return; + + _fillImage.type = Image.Type.Filled; + _fillImage.fillMethod = Image.FillMethod.Horizontal; + _fillImage.fillOrigin = (int)Image.OriginHorizontal.Left; + } +} diff --git a/Assets/02_Scripts/UI/PlayerHpBarUI.cs.meta b/Assets/02_Scripts/UI/PlayerHpBarUI.cs.meta new file mode 100644 index 0000000..80c9d0d --- /dev/null +++ b/Assets/02_Scripts/UI/PlayerHpBarUI.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 0ce8dd9c4b8941e2a69bbe4864891425 diff --git a/Assets/05_Data/Attack/GunFire.asset b/Assets/05_Data/Attack/GunFire.asset index 9aab21a..93a1b0f 100644 --- a/Assets/05_Data/Attack/GunFire.asset +++ b/Assets/05_Data/Attack/GunFire.asset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5859e13f692adf16f8df99bb15b9e1df44d876d2dc52eef75b9642510d82c0f1 -size 3197 +oid sha256:c3b24f0e87eff47b9dfc2d89faaaf70e8490300b7b3034feb3e8ba08e14c4dc9 +size 3198