[Unity] 오픈월드 제작기(5-3)

WowNo0607·2024년 12월 28일

OpenWorldGame

목록 보기
7/9

⭐ 버프창 만들기

🔧 설계

버프창

설계 1. 아이템 사용 시, 캐릭터 정보창에 버프 관련 아이콘 띄웁니다.

설계 2. 버프 적용 시간에 따라 아이콘을 검게 물들여 남은 시간을 간접적으로 보여줍니다.

디자인


🔎 주요 요소

BuffIcon

캐릭터 상태창에 활성화되어있는 버프들을 Instantiate할 때 사용하는 prefab을 먼저 만들어 봅시다.


바깥의 하얀 Image 가 버프 관련 아이콘(sprite)로 변경 될 공간이고, 안쪽의 검은 Image가 남은 버프 시간을 간접적으로 보여주는 부분입니다.

BuffManager와 BuffIconTimer

  • BuffManager.cs
  public List<GameObject> activeBuff;              // 활성화되어있는 버프들의 리스트
  public GameObject buffIcon;                      // 캐릭터 상태창에 나타나는 버프아이콘
  1. 버프 활성화
  public void OnBuffItem(Consumable itemData, float duration)
  {
      GameObject existingBuff = CheckExistingBuff(itemData.icon);
      if (existingBuff != null)
      {
          BuffIconTimer timer = existingBuff.GetComponent<BuffIconTimer>();
          timer.StartTimer(duration);

          return;
      }
      ...
  }
	public GameObject CheckExistingBuff(Sprite buffIcon)
	{
	    foreach (var buff in activeBuff)
	    {
	        BuffIconTimer timer = buff.GetComponent<BuffIconTimer>();
	        
	        if(timer.GetComponent<Image>().sprite == buffIcon)
	        {
	            return buff;
	        }
	    }
	    return null;
	}
	public void OffBuffCallback(GameObject buffEffect)
  {
      activeBuff.Remove(buffEffect);
      SortIcons();
  }

위 두 코드의 관계를 보자면, 위 코드는 새로운 버프 요청이 들어왔을 때 실행됩니다.

요청된 버프가 현재 지속중인지 확인합니다.

  • 버프가 진행 중인 경우(Not Null) 해당 버프 시간 초기화
  • 새로운 버프인 경우(Null) 새로운 버프 아이콘을 추가(버프 효과 적용)
  • BuffIconTimer.cs
public class BuffIconTimer : MonoBehaviour
{
    private float curTime;
    private float duration;

    private Image buffStateBar;

    private void Update()
    {
        curTime += Time.deltaTime;
        if (curTime >= duration)
        {
            timerRunning = false;
            Destroy(gameObject);
        }
        UpdateBuffState();
    }

    private void UpdateBuffState()
    {
        buffStateBar.fillAmount = curTime / duration;
    }

    private void OnDestroy()
    {
        OnBuffEnd?.Invoke(gameObject);
    }
}

위 코드는 버프 아이콘에 할당되어있는 컴포넌트입니다.

buffStateBar는 처음에 설명했던 검은 바탕의 이미지를 의미합니다. 이 이미지가 시간이 지남에 따라 아래에서 위로 채워지면서 남은 시간을 간접적으로 알려줍니다.

그리고 시간이 만료되었으면 해당 아이콘(this.gamObject)를 제거하고 콜백으로 해당 버프가 종료 되었다고 BuffManager에게 알려줍니다.

  1. 버프 아이콘 정렬
  • BuffManager.cs
public void SortIcons()
{
    Vector2 startPosition = new Vector2(0f, 0f);
    Vector2 componentSize = new Vector2(40f, 40f);
    
    int padding = 5;
    int i = 0;

    foreach(var buffIcon in activeBuff)
    {
        RectTransform rectTransform = buffIcon.GetComponent<RectTransform>();
        rectTransform.sizeDelta = componentSize;
        rectTransform.localScale = Vector2.one;
        rectTransform.anchoredPosition = new Vector2(startPosition.x + i * (componentSize.x + padding), startPosition.y);
        i++;
    }
}

위 코드는 캐릭터 상태창의 버프창에 아이콘을 정렬하는 역할을 수행합니다.

앞서 수행한 버프가 종료되면 뒤 버프들을 차례로 앞으로 당겨 정렬을 수행합니다.

  1. PlayerController 수정
public class State{
		public void UesConsumable(Consumable itemData)
		{
		    switch (itemData.subType)
		    {
						...
		        case ConsumableType.SpeedUp:
	          {
	              speed += itemData.value;
	              float duration = 10f;
	              PlayerController.myBuffManager.OnBuffItem(itemData, duration);
	              break;
	          }
		    }
		    ...
		}
}

Test Scene

위처럼 노란색 포션을 사용하여 버프가 활성화 되는 것을 볼 수 있습니다.


Total Code

  • BuffManager

public class BuffManager : MonoBehaviour
{
    public List<GameObject> activeBuff;

    public GameObject testBuffPrefab;

    public Action OnOffBuffChanged;

    private void Start()
    {
        SortIcons();
    }

    public void SortIcons()
    {
        Vector2 startPosition = new Vector2(0f, 0f);
        Vector2 componentSize = new Vector2(40f, 40f);
        
        int padding = 5;
        int i = 0;

        foreach(var buffIcon in activeBuff)
        {
            RectTransform rectTransform = buffIcon.GetComponent<RectTransform>();
            rectTransform.sizeDelta = componentSize;
            rectTransform.localScale = Vector2.one;
            rectTransform.anchoredPosition = new Vector2(startPosition.x + i * (componentSize.x + padding), startPosition.y);
            i++;
        }
    }

    public void OnBuffItem(Consumable itemData, float duration)
    {
        GameObject existingBuff = CheckExistingBuff(itemData.icon);
        if (existingBuff != null)
        {
            BuffIconTimer timer = existingBuff.GetComponent<BuffIconTimer>();
            timer.StartTimer(duration);

            return;
        }

        GameObject newBuff = Instantiate(testBuffPrefab);
        newBuff.GetComponent<Image>().sprite = itemData.icon;
        newBuff.transform.SetParent(transform);
        activeBuff.Add(newBuff);

        BuffIconTimer newTimer = newBuff.GetComponent<BuffIconTimer>();
        if (newTimer != null)
        {
            newTimer.OnBuffEnd = OffBuffCallback;
        }

        newTimer.StartTimer(duration);

        SortIcons();
    }

    public void OffBuffCallback(GameObject buffEffect)
    {
        activeBuff.Remove(buffEffect);
        SortIcons();
    }

    public GameObject CheckExistingBuff(Sprite buffIcon)
    {
        foreach (var buff in activeBuff)
        {
            BuffIconTimer timer = buff.GetComponent<BuffIconTimer>();
            
            if(timer.GetComponent<Image>().sprite == buffIcon)
            {
                return buff;
            }
        }
        return null;
    }
}
  • BuffIconTimer
public class BuffIconTimer : MonoBehaviour
{
    private float curTime;
    private float duration;
    private bool timerRunning;

    public GameObject buffTimerImg;
    private Image buffStateBar;

    public Action<GameObject> OnBuffEnd;        // 버프 종료 시 실행할 콜백

    private void Start()
    {
        buffStateBar = buffTimerImg.GetComponent<Image>();
    }

    public void StartTimer(float duration)
    {
        timerRunning = true;
        this.duration = duration;
        curTime = 0;
    }

    private void Update()
    {
        curTime += Time.deltaTime;
        if (curTime >= duration)
        {
            timerRunning = false;
            Destroy(gameObject);
        }
        UpdateBuffState();
    }

    private void UpdateBuffState()
    {
        buffStateBar.fillAmount = curTime / duration;
    }

    private void OnDestroy()
    {
        OnBuffEnd?.Invoke(gameObject);
    }
}
profile
안녕하세요

0개의 댓글