무적 유예 시간을 추가했습니다.
public class BasePlayerHPController : IPlayerHPController
{
private readonly PlayerType playerType;
private readonly PlayerModel model;
private readonly ISpriteRendererView spriteRenderer;
private int hp;
private bool isInvincible;
private UnityEvent<int> onHPChanged = new();
public BasePlayerHPController(PlayerType playerType, PlayerModel model, ISpriteRendererView spriteRenderer)
{
this.playerType = playerType;
this.model = model;
this.spriteRenderer = spriteRenderer;
SetHP(model.so.HP.MaxHP);
}
public void SetHP(int value)
{
hp = value;
onHPChanged?.Invoke(hp);
}
public void DamageHP(int damage)
{
PlayInvincible(model.so.HP.InvincibleDuration).Forget();//여기서 무적 적용
hp = Mathf.Max(0, hp - damage);
onHPChanged?.Invoke(hp);
if (hp <= 0)
OnHPZero();
}
public void RestoreHP(int value)
{
hp = Mathf.Min(model.maxHP, hp + value);
onHPChanged?.Invoke(hp);
}
public void SubscribeOnHPChanged(UnityAction<int> onHPChanged)
{
this.onHPChanged.AddListener(onHPChanged);
}
public void UnsubscribeOnHPChanged(UnityAction<int> onHPChanged)
{
this.onHPChanged.RemoveListener(onHPChanged);
}
private void OnHPZero()
{
IStageController stageController = LocalManager.instance.StageManager;
switch (playerType)
{
case PlayerType.Left:
stageController.OnLeftFailed();
break;
case PlayerType.Right:
stageController.OnRightFailed();
break;
}
}
public void Dispose()
{
}
public bool IsInvincible()
=> isInvincible;
public async UniTask PlayInvincible(float duration, UnityAction onFinished = null, CancellationToken token = default)
{
isInvincible = true;
try
{
var time = 0.0f;
var interval = 0.2f;
while (time < duration)
{
token.ThrowIfCancellationRequested();
var blinkCount =(int)(time / interval);//interval 기준으로 무적 시간의
if (blinkCount % 2 == 0) //짝수, 홀수를 판단해 투명도 조정으로 깜빡이기
spriteRenderer.SetAlpha(model.so.HP.InvincibleBlinkAlphaMax);
else
spriteRenderer.SetAlpha(model.so.HP.InvincibleBlinkAlphaMin);
time += Time.deltaTime;
await UniTask.Yield(PlayerLoopTiming.Update);
}
}
catch (OperationCanceledException) { }
finally
{
spriteRenderer.SetAlpha(1.0f);
isInvincible = false;
}
}
}
기존 BasePlayerHPController에 코드 약간 추가한 것 외에는 별로 작업이랄건 없던 것 같습니다.
private async void OnSpikeEnter(Collider2D collider2D)
{
if (collider2D.gameObject.TryGetComponent<IPlayerView>(out var playerView))
{
var playerType = playerView.GetPlayerType();
var playerPresenter = await LocalManager.instance.StageManager.GetPresenterAsync(playerType);
IPlayerHPController hpcontroller = playerPresenter;
if(hpcontroller.IsInvincible() == false) //무적으로 인한 피격 무시와
hpcontroller.DamageHP(1); //bounce되는 것은 별개로
//Damage에서 무적 여부를 판단할까도 했는데
var bounceDirection = (collider2D.transform.position - view.transform.position).normalized;
IPlayerReactionController reactionController = playerPresenter;
reactionController.Bounce(model.bounceData, bounceDirection);
} //아무래도 역할 분리를 시키는게 맞을 것 같아서 이대로
}
무적 판단은 외부 DamageHP 호출 부분에서 판단하도록 했습니다.
피격 후 무적이 적용되는 시스템은 변경되지 않을 것 같아 DamageHP 내부에서 즉시 실행하도록 했습니다.
public interface ISpriteRendererView
{
public void SetSprite(Sprite sprite);
public void SetColor(Color color);
public void SetAlpha(float alpha);
}
public class BaseSpriteRendererView : MonoBehaviour, ISpriteRendererView
{
private SpriteRenderer spriteRenderer;
private void OnEnable()
{
spriteRenderer = GetComponent<SpriteRenderer>();
}
public void SetAlpha(float alpha)
{
var color = spriteRenderer.color;
if (color.a != alpha)
{
color.a = alpha;
spriteRenderer.color = color;
}
}
public void SetColor(Color color)
{
if(spriteRenderer.color != color)
spriteRenderer.color = color;
}
public void SetSprite(Sprite sprite)
=>spriteRenderer.sprite = sprite;
}
ISpriteRendererView는 위와 같이 구현했습니다.
다음 목표:
좌/우 인풋으로 작동하는 UI 만들기
이건 기획에 시간 좀 걸리겠네요.
'무제_LR' 카테고리의 다른 글
| 마우스 없이 키보드로만 조작하는 Progress 기반 UI 제작 (0) | 2025.12.03 |
|---|---|
| UI Indicator 만들기 만들기(Selctable, Navigation) (0) | 2025.11.26 |
| 플레이어에 FSM 적용에 FSM 적용 (0) | 2025.11.21 |
| 간단한 연출용 UI(Animator 사용) (0) | 2025.11.18 |
| ScriptableObject는 항상 동일한 인스턴스가 아니다?!?!?!?!?!?!?!?!?!?!?!? (0) | 2025.11.13 |