
유니티 Button 에 코드로 함수를 연결할 때는 onClick.AddListener 를, 버튼을 잠가 눌리지 않게 할 때는 interactable 을 false 로 둡니다. interactable 이 false 인 버튼은 기본 설정에서 회색으로 바뀌고 클릭해도 onClick 이 불리지 않습니다.
button.onClick.AddListener(() => Debug.Log("눌림")); // 코드로 연결
button.interactable = false; // 회색으로 바뀌고 눌러도 반응 없음
스테이지 선택 화면에서는 아직 깨지 않은 스테이지 버튼을 잠가 두고, 보상 받기 버튼은 여러 번 빠르게 눌러도 한 번만 받게 해야 합니다. 이 블로그의 예전 글 UI Button / Toggle에 기능 할당과 삭제 in Script 는 버튼 하나에 함수를 붙이고 떼는 데까지였습니다. 이번 글에서는 스테이지 버튼 여러 개를 반복문으로 한 번에 연결하고, 깬 스테이지까지만 열고, 보상 버튼은 누르자마자 잠갔다가 1초 뒤에 푸는 예제를 만들겠습니다. 반복문에서 람다를 쓸 때 모든 버튼이 같은 번호를 받는 실수도 함께 보겠습니다.
C# (UNITY 3D)

▲ 아래 StageButtons 의 동작을 옮긴 웹 데모입니다(Unity 화면 아님). 열림 · 잠김과 연타 결과는 Unity 에서 클릭을 보내 본 값과 같고, 1초 뒤 풀림은 코드대로 그린 것입니다.
stage_cleared 가 1 이면 스테이지 1 · 2 가 열리고 3 · 4 는 회색으로 잠깁니다. 보상 받기를 연달아 누르면 첫 번째에만 보상이 들어가고 나머지는 무시됩니다. 아래 줄은 반복문 변수 i 를 람다에 그대로 넣은 잘못된 예로, 어느 버튼을 눌러도 「스테이지 5 선택」 이 나옵니다.
1. 준비 — 스테이지 버튼 여러 개와 보상 버튼
Hierarchy 에서 UI (Canvas) > Button - TextMeshPro 로 스테이지 버튼 네 개와 보상 버튼 하나를, UI (Canvas) > Text - TextMeshPro 로 메시지 글자를 만듭니다(예전 버전 메뉴는 UI > …). StageButtons 를 Canvas 나 빈 오브젝트에 붙이고 buttons_stage 칸에 스테이지 버튼을 1번부터 순서대로, button_reward 칸에 보상 버튼을, text_message 칸에 글자를 끌어다 넣습니다. 버튼의 On Click () 칸은 비워 둡니다.
using System.Collections;
using UnityEngine;
using UnityEngine.UI;
using TMPro;
Button 을 쓰기 위해 UnityEngine.UI 를, TMP_Text 를 쓰기 위해 TMPro 를, 1초 뒤에 잠금을 푸는 코루틴(IEnumerator)을 쓰기 위해 System.Collections 를 사용합니다.
2. 스테이지 · 보상 버튼 코드
using System.Collections;
using UnityEngine;
using UnityEngine.UI;
using TMPro;
//CodeFinder 코드파인더
//From https://codefinder.janndk.com/
public class StageButtons : MonoBehaviour
{
public Button[] buttons_stage = new Button[0]; // 스테이지 버튼들(1, 2, 3 … 순서대로)
public Button button_reward; // 보상 받기 버튼
public TMP_Text text_message; // 무엇을 눌렀는지 보여 줄 글자
public int stage_cleared = 1; // 깬 스테이지 수 — 여기까지 + 다음 하나가 열림
void Start()
{
for (int i = 0; i < buttons_stage.Length; i++)
{
int index = i; // 반복문 변수는 꼭 따로 담아서
buttons_stage[i].onClick.RemoveAllListeners();
buttons_stage[i].onClick.AddListener(() => Select_Stage(index));
buttons_stage[i].interactable = i <= stage_cleared; // 아직 못 가는 스테이지는 잠금
}
if (button_reward != null)
{
button_reward.onClick.RemoveAllListeners();
button_reward.onClick.AddListener(Click_Reward);
}
}
void Select_Stage(int index)
{
Show_Message("스테이지 " + (index + 1) + " 선택");
}
void Click_Reward()
{
button_reward.interactable = false; // 누르자마자 잠가서 연타 막기
Show_Message("보상을 받았습니다");
StartCoroutine(Unlock_Later(button_reward, 1f));
}
IEnumerator Unlock_Later(Button button, float seconds)
{
yield return new WaitForSecondsRealtime(seconds); // 일시정지(timeScale 0)여도 흐름
button.interactable = true;
}
void Show_Message(string message)
{
if (text_message != null)
text_message.text = message;
}
}
Play 하면 깬 스테이지까지와 바로 다음 스테이지만 눌리고, 누른 버튼의 번호가 글자로 보입니다. 보상 받기는 누르는 순간 잠겨서 빠르게 여러 번 눌러도 두 번째부터는 onClick 이 불리지 않습니다.
이 스크립트는 Unity 6000.6.3f1 에서 컴파일 오류 없이 들어갔고, 배치 모드에서 버튼 네 개를 만들어 클릭을 직접 불러 보니 2번은 「스테이지 2 선택」, 잠긴 4번은 아무 반응이 없었습니다. 보상 버튼은 첫 클릭 뒤 interactable 이 false 가 되어 두 번째 클릭이 무시되었습니다. 1초 뒤에 풀리는 것은 배치 모드에서 시간이 흐르지 않아 확인하지 못했습니다.
변수(variable)
buttons_stage(Button[]) : 스테이지 버튼들, 1번부터 순서대로
button_reward(Button) : 보상 받기 버튼
text_message(TMP_Text) : 누른 결과를 보여 줄 글자
stage_cleared(int) : 깬 스테이지 수
함수(function)
Select_Stage : 스테이지 버튼을 누를 때 불려 몇 번을 골랐는지 보여 줍니다.
Click_Reward : 보상 버튼을 누를 때 불려 버튼을 잠그고 메시지를 보여 줍니다.
Unlock_Later : 정한 시간이 지나면 버튼 잠금을 풉니다.
Show_Message : 메시지를 글자로 보여 줍니다.
3. 반복문으로 연결할 때 — 모든 버튼이 같은 번호
for (int i = 0; i < buttons_stage.Length; i++)
buttons_stage[i].onClick.AddListener(() => Select_Stage(i)); // 어느 버튼이든 i = 버튼 개수
람다는 i 의 값이 아니라 변수 i 자체를 기억합니다. for 문의 i 는 반복 내내 하나뿐이라, 버튼을 누를 때쯤이면 반복이 끝나 i 가 버튼 개수가 되어 있습니다. 버튼 세 개를 만들어 onClick 을 불러 보니 3, 3, 3 이 나왔습니다. 2절처럼 반복 안에서 int index = i; 로 따로 담으면 0, 1, 2 가 됩니다. foreach 의 반복 변수는 반복마다 새로 만들어져서 그대로 써도 0, 1, 2 였습니다.
4. interactable 과 onClick
button.onClick.Invoke(); // interactable 이 false 여도 불림

interactable 은 EventSystem 을 거쳐 들어오는 클릭과 Submit(키보드 · 패드 확인 버튼)을 막습니다. 코드에서 onClick.Invoke() 로 부르면 잠긴 버튼이어도 연결된 함수가 그대로 불리니, 잠금을 확인해야 하는 곳에서는 button.interactable 을 먼저 봅니다. 기본 설정의 잠긴 색은 disabledColor (200, 200, 200, 128) 이라 버튼 색에 곱해져 흰 버튼이면 반투명한 회색이 됩니다. 또 Button 은 왼쪽 클릭에만 반응해서 오른쪽 클릭으로는 onClick 이 불리지 않았습니다.
5. 버튼이 이상할 때 — 자주 하는 실수
한 번 눌렀는데 두 번 실행된다
같은 함수를 AddListener 로 두 번 붙이면 한 번 클릭에 두 번 불립니다. 연결 코드가 OnEnable 처럼 여러 번 도는 곳에 있다면 2절처럼 RemoveAllListeners 를 먼저 부릅니다. 인스펙터 On Click () 칸에 넣은 함수는 RemoveAllListeners 로 지워지지 않으니 코드로 연결할 버튼은 그 칸을 비워 둡니다.
group_menu.interactable = false; // 아래 버튼 모두 잠김
interactable 이 true 인데 눌리지 않는다
부모에 CanvasGroup 이 있고 그 interactable 이 false 이면 아래 버튼은 모두 잠깁니다. 이때 버튼의 interactable 은 true 그대로라서 값만 보면 원인을 찾기 어렵습니다. 팝업이 떠 있는 동안 뒤 메뉴 전체를 잠글 때는 이렇게 CanvasGroup 하나로 잠그는 편이 간단합니다. 실제로 눌리는지 확인할 때는 button.IsInteractable() 을 봅니다.
함께 보기: 버튼과 토글에 함수를 붙이고 떼는 기본 예제는 예전 글 UI Button / Toggle에 기능 할당과 삭제 in Script 에 있습니다.
Button 동작은 Unity 공식 문서 Button (uGUI) · CanvasGroup.interactable 과 Unity 6000.6.3f1 의 ugui 패키지 소스를 기준으로 적었습니다.
'Unity3D > C#' 카테고리의 다른 글
| 유니티 TMP InputField Enter 로 보내기 · 입력 끝 처리 (onSubmit, onEndEdit, onValueChanged 차이) (0) | 2026.10.24 |
|---|---|
| 유니티 Slider onValueChanged 로 값 표시 · 정수 단계 · 저장값 불러오기 (wholeNumbers, SetValueWithoutNotify) (0) | 2026.10.23 |
| 유니티 HEX 색상 코드 ↔ Color 변환 (Unity color from hex / to hex) (0) | 2026.10.22 |
| 유니티 토글(Toggle) 켜기/끄기 · 토글 그룹 하나만 고르기 (0) | 2026.10.21 |
| 유니티 드롭다운(TMP Dropdown) 값 읽기 · 옵션 코드로 넣기 (0) | 2026.10.20 |