플레이어 HPBar
This commit is contained in:
BIN
Assets/01_Scenes/GameScene.unity
LFS
BIN
Assets/01_Scenes/GameScene.unity
LFS
Binary file not shown.
@@ -35,13 +35,38 @@ public class ComboTransition
|
|||||||
public float ForwardStepDuration = 0.1f;// 전진 동작 시간
|
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 파일로 관리.
|
// 콤보 트리의 노드. .asset 파일로 관리.
|
||||||
[CreateAssetMenu(fileName = "ComboNode", menuName = "Combat/ComboNode")]
|
[CreateAssetMenu(fileName = "ComboNode", menuName = "Combat/ComboNode")]
|
||||||
public class ComboNode : ScriptableObject
|
public class ComboNode : ScriptableObject
|
||||||
{
|
{
|
||||||
public string NodeName; // Inspector 식별용
|
public string NodeName; // Inspector 식별용
|
||||||
[FormerlySerializedAs("Attack")]
|
[FormerlySerializedAs("Attack")]
|
||||||
public ActionData Action; // 이 노드에 진입했을 때 수행할 액션
|
public ActionData Action; // 기본(Neutral) 액션 — 방향키 입력이 없을 때 수행
|
||||||
|
// 방향키를 누른 채 공격하면 이 목록에서 일치하는 방향의 액션으로 대체된다.
|
||||||
|
// 비어 있거나 일치하는 방향이 없으면 Action(기본)으로 폴백.
|
||||||
|
public DirectionalAction[] DirectionalVariants;
|
||||||
public float ComboWindow = 0.8f; // 이 노드에서 다음 입력 받을 수 있는 시간
|
public float ComboWindow = 0.8f; // 이 노드에서 다음 입력 받을 수 있는 시간
|
||||||
public ComboTransition[] Transitions; // 다음 노드들 (입력별로 분기)
|
public ComboTransition[] Transitions; // 다음 노드들 (입력별로 분기)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -119,6 +119,7 @@ public class PlayerController : MonoBehaviour,IDamageable
|
|||||||
private Rigidbody2D _rb;
|
private Rigidbody2D _rb;
|
||||||
private Animator _anim;
|
private Animator _anim;
|
||||||
private SpriteRenderer _spriteRenderer; // 좌우 반전 + flipX 페이싱용
|
private SpriteRenderer _spriteRenderer; // 좌우 반전 + flipX 페이싱용
|
||||||
|
private Health _health;
|
||||||
|
|
||||||
// ─── 무기 시스템 ────────────────────────────────────────────────────
|
// ─── 무기 시스템 ────────────────────────────────────────────────────
|
||||||
// PlayerWeaponInventory가 현재 장착 무기 관리. 무기 교체 시 OnWeaponChanged 이벤트 발화.
|
// PlayerWeaponInventory가 현재 장착 무기 관리. 무기 교체 시 OnWeaponChanged 이벤트 발화.
|
||||||
@@ -131,6 +132,7 @@ private void Awake()
|
|||||||
_rb = GetComponent<Rigidbody2D>();
|
_rb = GetComponent<Rigidbody2D>();
|
||||||
_anim = GetComponent<Animator>();
|
_anim = GetComponent<Animator>();
|
||||||
_spriteRenderer = GetComponentInChildren<SpriteRenderer>();
|
_spriteRenderer = GetComponentInChildren<SpriteRenderer>();
|
||||||
|
_health = GetComponent<Health>();
|
||||||
ResolveBodyColliderReference();
|
ResolveBodyColliderReference();
|
||||||
EnsureAttackHitbox();
|
EnsureAttackHitbox();
|
||||||
// 공격이 enemy를 hit하면 _lastHitEnemy를 기억 → 다음 잡기에서 그 enemy를 우선 타겟팅.
|
// 공격이 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)
|
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})");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
160
Assets/02_Scripts/UI/PlayerHpBarUI.cs
Normal file
160
Assets/02_Scripts/UI/PlayerHpBarUI.cs
Normal file
@@ -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<PlayerController>();
|
||||||
|
if (player != null)
|
||||||
|
_health = player.GetComponent<Health>();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ResolveFillImage()
|
||||||
|
{
|
||||||
|
if (_fillImage != null || !_autoFindFillImageInChildren) return;
|
||||||
|
|
||||||
|
Transform fill = FindChildByName(transform, "Fill");
|
||||||
|
if (fill != null)
|
||||||
|
_fillImage = fill.GetComponent<Image>();
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
2
Assets/02_Scripts/UI/PlayerHpBarUI.cs.meta
Normal file
2
Assets/02_Scripts/UI/PlayerHpBarUI.cs.meta
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 0ce8dd9c4b8941e2a69bbe4864891425
|
||||||
Binary file not shown.
Reference in New Issue
Block a user