Quick Comparison
활성화 API는 같은 "끄기"가 아닙니다. SetActive는 GameObject의 전체 실행 참여를, enabled는 해당 Component 종류의 기능을, activeSelf·activeInHierarchy는 서로 다른 활성 상태 정보를 다룹니다.
| 바꾸거나 읽는 대상 | API/값 | 그대로 남는 것 | 대표 사용처 |
|---|---|---|---|
| GameObject와 hierarchy | SetActive(bool) | object 자체와 serialized 값 | pooled object, 패널, 적 전체 비활성 |
| MonoBehaviour | behaviour.enabled | 다른 Component·GameObject 활성 | 특정 script의 Update/OnEnable 흐름 중지 |
| Renderer | renderer.enabled | script·Collider·GameObject 활성 | 보이지 않게만 하기 |
| Collider | collider.enabled | Renderer·script 활성 | 충돌/trigger query 참여만 끄기 |
| 자신이 요청한 active flag | activeSelf | 부모 때문에 막힌 실제 상태 | 의도된 local flag 점검 |
| 실제 hierarchy 활성 | activeInHierarchy | 없음 | 현재 동작 가능한지 판단 |
gameObject.SetActive(false); // 이 GameObject와 active child hierarchy를 비활성화
renderer.enabled = false; // 화면 렌더링만 끔
collider.enabled = false; // 이 Collider의 physics 참여만 끔GameObject 활성 상태
SetActive(false)는 해당 GameObject를 hierarchy에서 비활성으로 만들고, 실제 활성 상태였던 child의 activeInHierarchy도 false로 만듭니다. 활성 Component의 OnDisable이 호출되고 update message, coroutine, rendering, Collider/Rigidbody의 scene 참여도 이 GameObject가 다시 활성화될 때까지 중단됩니다. child의 activeSelf 값 자체를 전부 false로 덮어쓰는 것은 아닙니다.
parent.SetActive(false);
Debug.Log(child.activeSelf); // child가 스스로 켜져 있었다면 true일 수 있음
Debug.Log(child.activeInHierarchy); // 부모 영향 때문에 false그래서 "이 child를 내가 켜 두었는가"는 activeSelf, "지금 Update·rendering에 참여할 수 있는가"는 activeInHierarchy로 봅니다. parent가 inactive인 상태에서 child에 SetActive(true)를 호출해도 parent를 켜기 전까지 실제 활성으로 돌아오지 않습니다.
Component별 enabled
Behaviour.enabled는 MonoBehaviour, Animator, AudioSource처럼 Behaviour를 상속하는 Component의 실행 enabled flag입니다. GameObject가 active이고 이 flag가 true일 때 isActiveAndEnabled가 true가 됩니다. Renderer.enabled와 Collider.enabled는 같은 property 이름을 쓰지만 끄는 기능이 다르므로, "enabled=false면 모든 것이 멈춘다"고 일반화하지 않습니다.
| Component | enabled = false 결과 | 언제 적합한가 | 흔한 오해 |
|---|---|---|---|
| MonoBehaviour | 해당 script의 Update 계열과 enabled lifecycle이 중지 | 기능 script 하나만 잠시 끌 때 | GameObject의 다른 script도 멈춘다고 생각 |
| Renderer | 해당 Renderer만 그리지 않음 | 로직은 유지하며 시각 표현만 숨길 때 | Collider·Update도 멈춘다고 생각 |
| Collider | 해당 Collider가 collision/trigger/query 대상에서 빠짐 | 무적 판정, sensor off | Rigidbody 또는 Renderer까지 꺼진다고 생각 |
| AudioSource | source playback·update 동작이 disabled 상태가 됨 | 개별 audio component 정지 | GameObject의 다른 audio·script까지 멈춘다고 생각 |
[SerializeField] private Renderer targetRenderer;
[SerializeField] private Collider hitCollider;
public void SetInvisibleButInteractive(bool invisible)
{
targetRenderer.enabled = !invisible;
hitCollider.enabled = true;
}UI를 숨길 때도 같은 구분이 필요합니다. CanvasGroup.alpha = 0은 보이는 정도를 바꾸는 방법이고, input 차단은 blocksRaycasts, update·layout·활성은 별도 문제입니다. 전체 UI lifecycle을 끌지, 그림과 input만 바꿀지를 UI 카드의 선택 기준으로 나눕니다.
lifecycle과 pool 경계
SetActive를 pool의 대여/반환에 쓰면 OnEnable과 OnDisable이 여러 번 호출됩니다. event listener, coroutine, particle state를 그 메시지 함수에 둘 때는 매 대여마다 중복 등록되지 않는지와 반환 때 정리되는지를 함께 확인합니다. 한 MonoBehaviour만 enabled=false로 만들었다고 그 object에서 시작한 모든 외부 작업이 자동으로 정리되는 것은 아닙니다.
Destroy는 active 상태 변경이 아니라 object 수명 종료입니다. 다음 frame에 제거될 수 있고 Unity object reference는 null처럼 동작하므로, pool로 되돌릴 object에 Destroy와 SetActive(false)를 혼용하지 않습니다.
자주 틀리는 부분
| 증상 | 원인 | 수정 |
|---|---|---|
| child를 켰는데 화면에 안 보임 | parent가 inactive | activeInHierarchy와 parent chain 확인 |
| object를 숨겼는데 공격 판정은 계속 남음 | Renderer만 disabled | Collider 또는 GameObject lifecycle 중 의도한 범위를 선택 |
| 한 script를 껐는데 다른 script도 멈출 것으로 기대 | Behaviour.enabled 범위를 오해 | 의존 script와 GameObject active를 별도로 제어 |
| pool 재사용 때 listener가 두 번 호출 | OnEnable마다 등록하고 OnDisable에서 해제하지 않음 | 등록/해제를 대칭으로 만들고 idempotence 확인 |
| inactive object를 Destroy와 혼용 | 수명 종료와 재사용 정책이 충돌 | pool object는 return, 일회성 object는 destroy로 소유자 정책 분리 |
참고 링크
4 sources