본문 바로가기

무제_LR

피격 후 무적 시간 적용

 

무적 유예 시간을 추가했습니다.

 

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 만들기

이건 기획에 시간 좀 걸리겠네요.