Unity - onClick.AddListener


onClick.AddListener

유니티에서 UI를 클릭했을때 특정 이벤트를 실행시키고 싶을 때 onclick.AddListener를 이용한다.

원래 유니티의 인스펙터 창에서 button component에 onclick 등 함수를 만들고 이를 직접 클릭으로 해당 버튼에 할당하는 방법이 있지만, 매번 이렇게 설정하기가 번거롭다.

그래서 Awake나 Start 부분에서 Button에 onClick.AddListener(메소드) 형식으로 메소드를 연결하는 방법을 사용한다.

void Awake()
    {
        // 인자가 없는경우 바로 메소드 이름으로 넘겨주면됨.
        Btn_tabNormals[0].onClick.AddListener(PressBtnClose);

        // 인자가 있는 경우 람다 식이나 델리게이트를 사용
        Btn_tabNormals[0].onClick.AddListener(() => OpenTab(0));
        Btn_tabNormals[0].onClick.AddListener(delegate { OpenTab(0); });
    }

    void PressBtnClose() // 인자가 없는 메소드
    {
    }
    
    void PressBtnTab(int tabIndex) // 인자가 있는 메소드
    {
    }

간단한데, for 문을 통해 할당시킬 경우 할당이 잘 안된다는 문제점이 있다.

for 문을 사용하여 할당했을 때 문제

문제는 아래와 같이 코드를 작성할 때 발생한다.

for (int i = 0; i < Btns.Length; i++)
{
    Btns[i].onClick.AddListener(() => PressBtnSelectGame(i));
}

이 경우 Btns의 모든 버튼들이 마지막 값으로 초기화가 된다.

Closure Problem

위의 코드에서는 람다식을 사용했다. 이 람다식은 실제 실행되기 전에는 참조형태로 가지고 있는데, for 문을 돌리면서 같은 변수 i를 계속 사용하였고, 마지막에 모두 i를 참조형태로 가지고 있다가 마지막 i의 값을 할당하므로 마지막 값으로 통일된 것이다. 이를 closure problem이라고 부른다.

따라서 아래와 같이 수정하면 된다고 한다.

for (int i = 0; i < Btns.Length; i++)
{
    int temp = i; // Closure 문제때문에 복사해서 사용한다.
    Btns[temp].onClick.AddListener(() => PressBtnSelectGame(temp));
}

출처