71.내일배움캠프 63일차 TIL <Unity Unity 2D 팀프로젝트- MartialGod:Reborn - 12일차> 07/08

정광훈(Unity_9기)·2025년 7월 8일

TIL (Today I Learned)

목록 보기
72/110
post-thumbnail

오늘 한 일

왠지 모르게 한 오브젝트에 여러 개의 스크립트가 붙으면
안 될 것 같은 생각이 들었는지 모르겠다.

스크립트만 분리하면 되는데 엄연히 따지면
다른 오브젝트에 붙여야 할 이유가 있었던 것도 아니었다.

SaveNodeUI.cs에 UI 온오프나 페이드 같은 효과,
UI에서 메뉴 선택같은 기능들이 여기에 다 있었다.

(이건 수정 후 사진)

그래서 코드를 서로 다른 스크립트에 분리시키고 SaveNodeUIController.cs를
Panel의 자식인 텍스트들을 모아둔 Text오브젝트에 컴포넌트로 추가 시켰다.

SaveNodeUI오브젝트에 SaveNodeUI.cs가 있고 거기서 연출하는 과정에서
세이브 노드 UI를 꺼지면서 잠시 게임이 멈춘다는 느낌을 주고 싶었다.

Panel의 자식인 Text 오브젝트가 꺼지면서 SaveNodeUIController.cs도
비활성화가 되어 코루틴 작동 중에 그대로 멈춰버린 것.

지금 생각해도 왜 그랬는지 모르겠다.

그래서 SaveNodeUIController.cs를 SaveNodeUI오브젝트에 추가하였다.

다른 방법으로는 Invoke()를 사용하는 방법이 있고,
Invoke()를 사용하는 방법은

public class InvokeExample : MonoBehaviour
{
    void Start()
    {
        // 3초 후에 "SpawnObject" 메서드를 한 번 호출합니다.
        Invoke("SpawnObject", 3f);
    }

    void SpawnObject()
    {
        Debug.Log("오브젝트가 생성되었습니다!");
        // 여기에 실제 오브젝트 생성 코드를 넣을 수 있어요.
    }
}

이런 식으로 잘 이용해서 구현하는 방법이 있다.

캔버스 그룹의 알파를 조절하는 방식으로 패널같은 것을
안 보이게 하여 없어진 것 처럼 보이게 할 수 있다.
이 방법을 SaveNodeUI오브젝트를 서서히 사라지는 효과를 줌.

DoTween도 코루틴과 같이 비동기 방식이다.

fadeImage.DOFade(1, 2f).SetEase(Ease.OutQuad);
이 코드는 실행하자마자 다음 코드로 넘어가는 동시에 Fade효과가 진행된다.
코드가 다 끝나야 다음 코드로 진행하는 것이 아님.
일반 메서드, 코드 같은 경우는 끝나야 다음으로 넘어감.


<오늘 작성한 코드>

using UnityEngine;

public class SaveNode : MonoBehaviour, IInteraction
{
    [SerializeField] private GameObject saveNodeUI;
    [SerializeField] private GameObject interactionText;

    public PlayerStatus status;
    
    const string SaveNodeUIString = "SaveNodeUI";
    const string InteractionString = "Canvas - Interaction";

    private void Reset()
    {
        saveNodeUI = GameObject.Find(SaveNodeUIString);
        interactionText = this.TryFindChild(InteractionString);
    }

    public void Interaction()
    {
        if (saveNodeUI != null)
        {
            saveNodeUI.SetActive(true);
        }

        UIManager.Instance.Close<BattleUI>();
        
        if (interactionText != null)
        {
            interactionText.SetActive(false);
        }
    }

    private void OnTriggerEnter2D(Collider2D other)
    {
        if (other.gameObject.layer == LayerHelper.OriginLayerValue(LayerHelper.PlayerLayer))
        {
            interactionText.SetActive(true);
        }
    }

    private void OnTriggerExit2D(Collider2D other)
    {
        if (other.gameObject.layer == LayerHelper.OriginLayerValue(LayerHelper.PlayerLayer))
        {
            interactionText.SetActive(false);
        }
    }

    public void Rest()
    {
        // 휴식하기 선택 시 체력/스테미나 회복, 리페어 프로토콜 횟수를 충전시킨다.
        Player.Instance.OnPlayerHealthChange?.Invoke(status.playerCurrentHealth, status.playerHealth);
        
    }

    public void OpenUpgradeUI()
    {
        // 전술 코어 업그레이드 UI를 출력한다.
        // 전술 코어 업그레이드 UI가 활성화 되었을 때 세이브 노드 UI는 전술 코어 UI와 겹치지 않게 한다.
        
        LogHelper.Log("업그레이드UI 표시");
    }
}
using System.Collections;
using DG.Tweening;
using UnityEngine;
using UnityEngine.InputSystem;
using UnityEngine.UI;



// - 세이브 노드 UI가 닫힌 후 1초 동안은 무적시간이다.
// 단, 이때는 플레이어는 입력 무시이며 UI 닫힌 후 1초 후에 입력을 받을 수 있다.


public class SaveNodeUI : UIBase
{
    [SerializeField] private CanvasGroup saveNodeUICanvasGroup; // 페이드 효과를 주기 위한 캔버스 그룹
    [SerializeField] private Image fadeImage; // 페이드 효과에 사용할 이미지
    [SerializeField] private GameObject saveNodePanel;
    [SerializeField] private SaveNodeUIController saveNodeUIController;
    
    const string SaveNodePanelString = "Panel";
    const string FadeImageString = "Image - Fade";

    private void Reset()
    {
        saveNodeUICanvasGroup = this.TryGetChildComponent<CanvasGroup>();
        fadeImage = this.TryGetChildComponent<Image>(FadeImageString);
        saveNodePanel = this.TryFindChild(SaveNodePanelString);
        saveNodeUIController = FindObjectOfType<SaveNodeUIController>();
    }

    private void OnEnable()
    {
        Init();
    }
    
    private void Start()
    {
        UIManager.Instance.Add<SaveNodeUI>(this);
    }
    
    public override void Init() // 초기화
    {
        saveNodeUICanvasGroup.alpha = 1f; 

        SetPlayerInputEnabled(false); // 플레이어 인풋 비활성화
    }

    public override void OffUI() // 세이브 노드 UI 종료
    {
        saveNodeUICanvasGroup.DOKill();
        UIManager.Instance.Open<BattleUI>(); // 배틀 UI 다시 활성화
        // 0.5초에 걸쳐 점점 사라짐
        saveNodeUICanvasGroup.DOFade(0, 0.5f).SetEase(Ease.OutQuad)
            .OnComplete(() => { StartCoroutine(PlayerInputEnableCoroutine()); });
    }

    private void FadeOut() // 휴식하기 누르면 연출되는 페이드 아웃
    {
        fadeImage.DOKill();
        saveNodePanel.gameObject.SetActive(false);
        // 2초에 걸쳐 점점 어두워짐
        fadeImage.DOFade(1, 2f).SetEase(Ease.OutQuad);
    }
    
    // 휴식하기 눌러 페이드 아웃 후 연출되는 페이드 인, 2초에 걸쳐 점점 밝아짐
    private void FadeIn() 
    {
        fadeImage.DOKill();
        fadeImage.DOFade(0, 2f).SetEase(Ease.InQuad)
            .OnComplete(() => { saveNodePanel.gameObject.SetActive(true); });
    }

    public IEnumerator FadeCoroutine() // 페이드 코루틴
    {
        saveNodeUIController.SetMenuInputEnabled(false);
        
        FadeOut();
        yield return CoroutineHelper.WaitTime(3f); // 1초 뒤에 FadeIn()발동
        FadeIn();
        yield return CoroutineHelper.WaitTime(3f); // FadeIn() 다음에 1초 후 입력 가능
        
        saveNodeUIController.SetMenuInputEnabled(true);
    }

    private void SetPlayerInputEnabled(bool enabled) // 플레이어 인풋을 활성화 / 비활성화 시킴
    {
        if (Player.Instance != null && Player.Instance.TryGetComponent<PlayerInput>(out PlayerInput playerInput))
        {
            if (enabled)
            {
                playerInput.ActivateInput();
                LogHelper.Log("플레이어 인풋 활성화");
            }
            else
            {
                playerInput.DeactivateInput();
                LogHelper.Log("플레이어 인풋 비활성화");
            }
        }
    }

    private IEnumerator PlayerInputEnableCoroutine() // 플레이어 인풋 활성화 코루틴
    {
        yield return new WaitForSeconds(1f); // 1초뒤에 플레이어가 움직일 수 있게 해야함
        SetPlayerInputEnabled(true);
        gameObject.SetActive(false);
    }
}
using System.Collections.Generic;
using TMPro;
using UnityEngine;

public class SaveNodeUIController : MonoBehaviour
{
    [SerializeField] private SaveNode saveNode;
    [SerializeField] private SaveNodeUI saveNodeUI;

    private TextMeshProUGUI restText;
    private TextMeshProUGUI coreUpgradeText;
    private TextMeshProUGUI backText;

    public List<TMP_Text> selectMenuList;

    public float changeFontSize = 1.2f;

    private List<float> originalFontSize = new List<float>();

    private int currentIndex;
    private bool isMenuInputDisabled = false;

    const string RestTextString = "Text - Rest";
    const string CoreUpgradeTextString = "Text - CoreUpgrade";
    const string BackTextString = "Text - Back";

    private void Reset()
    {
        saveNode = FindObjectOfType<SaveNode>();
        saveNodeUI = FindObjectOfType<SaveNodeUI>();

        if (selectMenuList == null)
        {
            selectMenuList = new List<TMP_Text>();
        }
        else
        {
            selectMenuList.Clear();
        }

        restText = this.TryFindChild(RestTextString).GetComponent<TextMeshProUGUI>();
        coreUpgradeText = this.TryFindChild(CoreUpgradeTextString).GetComponent<TextMeshProUGUI>();
        backText = this.TryFindChild(BackTextString).GetComponent<TextMeshProUGUI>();

        selectMenuList.Add(restText);
        selectMenuList.Add(coreUpgradeText);
        selectMenuList.Add(backText);
    }

    private void OnEnable()
    {
        InitMenu();
    }

    public void InitMenu()
    {
        isMenuInputDisabled = false;
        currentIndex = 0;

        foreach (TMP_Text menu in selectMenuList) // 메뉴 텍스트 기존 폰트 사이즈 추가
        {
            originalFontSize.Add(menu.fontSize);
        }

        UpdateSelectFont();
    }

    private void Update()
    {
        if (isMenuInputDisabled) return;

        SelectMenu();
    }

    private void SelectMenu() // 메뉴 선택
    {
        if (Input.GetKeyDown(KeyCode.W)) // 위로 이동
        {
            currentIndex--;
            // 첫 인덱스에서 위로 이동하면 마지막 인덱스로 이동
            if (currentIndex < 0)
            {
                currentIndex = selectMenuList.Count - 1;
            }

            UpdateSelectFont();
        }
        else if (Input.GetKeyDown(KeyCode.S)) // 아래로 이동
        {
            currentIndex++;
            // 마지막 인덱스에서 아래로 이동하면 첫 인덱스로 이동
            if (currentIndex >= selectMenuList.Count)
            {
                currentIndex = 0;
            }

            UpdateSelectFont();
        }
        else if (Input.GetKeyDown(KeyCode.Escape))
        {
            if (saveNodeUI != null)
            {
                saveNodeUI.OffUI(); // 세이브노드UI 비활성화
            }
        }
        else if (Input.GetKeyDown(KeyCode.Return) || Input.GetKeyDown(KeyCode.G))
        {
            EnterMenu(currentIndex); // 해당 메뉴 기능 실행
        }
    }

    private void UpdateSelectFont() // 선택 중인 메뉴 폰트 업데이트
    {
        for (int i = 0; i < selectMenuList.Count; i++)
        {
            if (i == currentIndex) // 현재 인덱스의 글자 크기를 키움
            {
                selectMenuList[i].fontSize = originalFontSize[i] * changeFontSize;
            }
            else // 선택중인 인덱스가 아니면 기존 글자 크기로 돌아옴
            {
                selectMenuList[i].fontSize = originalFontSize[i];
            }
        }
    }

    private void EnterMenu(int index) // 메뉴 선택
    {
        switch (index)
        {
            case 0:
                if (saveNodeUI != null)
                {
                    saveNode.Rest(); // 휴식기능
                    StartCoroutine(saveNodeUI.FadeCoroutine()); // 페이드 효과
                }

                break;
            case 1:
                if (saveNode != null)
                {
                    saveNode.OpenUpgradeUI(); // 업그레이드 UI 활성화
                }

                break;
            case 2:
                if (saveNodeUI != null)
                {
                    saveNodeUI.OffUI(); // 세이브 노드UI 종료
                }

                break;
        }
    }

    // 휴식하기 중에 다른 입력이 되지 않도록 방지
    // enabled가 false면 입력되지 않음, true면 입력 가능
    public void SetMenuInputEnabled(bool enabled)
    {
        isMenuInputDisabled = !enabled;
    }
}

0개의 댓글