상세 컨텐츠

본문 제목

[개발일지] 유니티 2.5D 디펜스게임 개발: UI NullReferenceException 트러블슈팅

카테고리 없음

by 강자이 2024. 7. 2. 21:49

본문

 

이번 포스트에서는 유니티 프로젝트에서 발생한 NullReferenceException에 대한 문제 해결 과정을 공유하고자 합니다. 이 문제는 유닛 데이터를 UI에 표시하려는 시도 중 발생했으며, 이를 해결하기 위한 몇 가지 접근 방식을 다루겠습니다.


문제 상황

유닛 데이터를 표시하는 UI_Unit 팝업 창을 열 때 NullReferenceException 오류가 발생했습니다. 구체적인 오류 메시지는 다음과 같습니다:

NullReferenceException: Object reference not set to an instance of an object
UI_Unit.UpdateUnitInfo (Unit unit) (at Assets/Scripts/UI/PopUp/UI_Unit.cs:45)
UnitTile.OnPointerDown (UnityEngine.EventSystems.PointerEventData eventData) (at Assets/Scripts/Stage/Tile/UnitTile.cs:38)
UnityEngine.EventSystems.ExecuteEvents.Execute (UnityEngine.EventSystems.IPointerDownHandler handler, UnityEngine.EventSystems.BaseEventData eventData) (at ./Library/PackageCache/com.unity.ugui@1.0.0/Runtime/EventSystem/ExecuteEvents.cs:43)
UnityEngine.EventSystems.ExecuteEvents.Execute[T] (UnityEngine.GameObject target, UnityEngine.EventSystems.BaseEventData eventData, UnityEngine.EventSystems.ExecuteEvents+EventFunction1[T1] functor) (at ./Library/PackageCache/com.unity.ugui@1.0.0/Runtime/EventSystem/ExecuteEvents.cs:272)
UnityEngine.EventSystems.EventSystem:Update() (at ./Library/PackageCache/com.unity.ugui@1.0.0/Runtime/EventSystem/EventSystem.cs:530)

 

원인 분석

오류 메시지를 통해 UI_Unit.UpdateUnitInfo 메서드에서 NullReferenceException이 발생한 것을 확인할 수 있었습니다. 이는 UI_Unit 클래스의 UI 요소가 아직 초기화되지 않은 상태에서 UpdateUnitInfo 메서드가 호출되었기 때문입니다. 구체적으로, Init() 메서드가 Start()에서 호출되면서 UI 요소의 바인딩이 늦어져서 발생한 문제였습니다.

 

해결 방법

문제를 해결하기 위해 두 가지 접근 방식을 시도했습니다:

  1. Awake에서 Init 호출: Init() 메서드를 Start() 대신 Awake()에서 호출하여 UI 요소의 초기화가 더 빨리 이루어지도록 수정했습니다.
  2. 데이터를 미리 받아오는 방식: 데이터를 미리 받아와서 Start() 메서드에서 UI 요소에 할당하는 방식으로 수정했습니다.

 


 

기존 코드

    private void Start()
    {
        Init();
    }

    public override void Init()
    {
        base.Init();

        Bind<Button>(typeof(Buttons));
        Bind<TMP_Text>(typeof(Texts));
        Bind<GameObject>(typeof(GameObjects));
        Bind<Image>(typeof(Images));

        GetButton((int)Buttons.Btn_Close).gameObject.AddUIEvent((PointerEventData data) => OnButtonClicked(Buttons.Btn_Close, data));
        GetButton((int)Buttons.Blocker).gameObject.AddUIEvent((PointerEventData data) => OnButtonClicked(Buttons.Blocker, data));
    }

    private void OnButtonClicked(Buttons buttonType, PointerEventData data)
    {
        switch (buttonType)
        {
            //UIManager.Instance.ShowPopupUI<UI_Unit>();
            case Buttons.Btn_Close:
                ClosePopupUI();
                break;
            case Buttons.Blocker:
                ClosePopupUI();
                break;
        }
    }

    public void UpdateUnitInfo(Unit unit)
    {
        GetText((int)Texts.TXT_AttackPower).text = unit.AttackRange.ToString();
        GetText((int)Texts.TXT_AttackSpeed).text = unit.MoveSpeed.ToString();
    }

 

 

코드 수정

먼저, UI_Unit 클래스를 다음과 같이 수정했습니다:

    private Unit unit;
    private void Start()
    {
        Init();
        GetText((int)Texts.TXT_AttackPower).text = unit.AttackRange.ToString();
        GetText((int)Texts.TXT_AttackSpeed).text = unit.MoveSpeed.ToString();
    }

    public override void Init()
    {
        base.Init();

        Bind<Button>(typeof(Buttons));
        Bind<TMP_Text>(typeof(Texts));
        Bind<GameObject>(typeof(GameObjects));
        Bind<Image>(typeof(Images));

        GetButton((int)Buttons.Btn_Close).gameObject.AddUIEvent((PointerEventData data) => OnButtonClicked(Buttons.Btn_Close, data));
        GetButton((int)Buttons.Blocker).gameObject.AddUIEvent((PointerEventData data) => OnButtonClicked(Buttons.Blocker, data));
    }

    private void OnButtonClicked(Buttons buttonType, PointerEventData data)
    {
        switch (buttonType)
        {
            //UIManager.Instance.ShowPopupUI<UI_Unit>();
            case Buttons.Btn_Close:                
                ClosePopupUI();
                break;
            case Buttons.Blocker:               
                ClosePopupUI();
                break;
        }
    }

    public void UpdateUnitInfo(Unit unit)
    {
        this.unit = unit;
    }

 

다음으로, UnitTile 클래스에서 OnPointerDown 이벤트에서 UI_Unit을 올바르게 호출하는 코드를 추가했습니다:

public void OnPointerDown(PointerEventData eventData)
{
    Debug.Log($"{num} 번째 타일 터치 ");

    // UI_Unit 창을 불러오는 코드
    UI_Unit unitWindow = UIManager.Instance.ShowPopupUI<UI_Unit>();
    unitWindow.UpdateUnitInfo(SpawnUnit);
}

 

 


 

결론

이번 포스트에서는 유니티에서 발생한 NullReferenceException 오류를 해결하기 위해 Awake와 Start 메서드의 호출 순서를 조정하고, 데이터를 미리 받아오는 방식을 적용하여 문제를 해결하는 과정을 공유했습니다. 유니티 프로젝트에서 UI 요소를 다룰 때 초기화 순서와 데이터 바인딩의 중요성을 잊지 말아야겠습니다.

댓글 영역