스택(Stack) 기반으로 화면 전환과 팝업을 관리하는 Unity UI 프레임워크입니다.
UIManager와 UIView를 중심으로, 다음과 같은 요구사항을 해결하기 위한 구조입니다.
- 씬 안의 여러 화면(뷰)을 일관된 규칙으로 전환하고 싶을 때
- 전체 화면 전환과 팝업을 동일한 스택 위에서 다루고 싶을 때
- 진입/퇴장 애니메이션을 프로필 기반으로 관리하고 싶을 때
- UI GameObject의 활성/비활성/파괴까지 포함한 라이프사이클을 제어하고 싶을 때
UIFramework/
Components/
UIManager.cs // 스택 기반 UI 흐름 관리
UIView.cs // 개별 화면/팝업 단위 + 애니메이션/라이프사이클
Core/
ButtonAttribute.cs // 인스펙터에서 메서드를 버튼으로 실행하기 위한 Attribute/Editor
UIAnimationHelper.cs // Slide / Zoom / Fade 코루틴 유틸
UIEnums.cs // UISlideDir, UIAnimType, UILifecycleMode 정의
UITransitionProfile.cs // 전환 설정 ScriptableObject
Example/
11.prefab // 샘플 UIView 구성이 들어 있는 예제 프리팹
UIViewTimingDebug.cs // UIView 이벤트 타이밍 디버그용 스크립트
Profiles/
Default_UITransitionProfile.asset
NoEnter.asset
RightRight.asset
UpDown.asset
Zoom.asset
UIManager는 내부에 Stack<UIView>를 가지고 있고,
현재 화면/팝업들의 상태를 “접시 쌓기”처럼 관리합니다.
Push(UIView view)- 일반 화면 전환
- 현재 화면을
Hide()한 뒤, 새 UIView를 스택에 Push +Show()
ShowPopup(UIView view)- 팝업 전환
- 아래 화면은 유지한 채, 새 UIView를 스택에 Push +
Show()
Pop()- 최상단 UIView를 Pop +
Hide() - 그 아래 UIView가 있다면 다시
Show()를 호출해 복귀 연출
- 최상단 UIView를 Pop +
ClosePopup()- “팝업만 닫는” 용도
- 최상단 UIView를 Pop +
Hide()만 하고, 아래 UIView에는Show()를 다시 호출하지 않음 - 팝업 닫을 때 기존 전체 화면 진입 애니메이션이 다시 도는 버그를 방지
PopAll()- 스택에 쌓인 모든 UIView를 Pop 하면서 정리 (
Hide()/ForceHide()조합)
- 스택에 쌓인 모든 UIView를 Pop 하면서 정리 (
PopTo(UIView target)- 특정 UIView가 스택 어딘가에 있을 때, 그 지점까지 위에 있는 것들을 전부 Pop
- 중간에 제거되는 뷰들은
ForceHide()로 즉시 정리하고, 타겟 뷰는 다시Show()
요약하면:
- Push = 새 화면을 위에 쌓는 동작
- Pop / ClosePopup / PopTo / PopAll = 스택에서 내려가거나 정리하는 동작
으로 UI 흐름 전체를 하나의 스택 모델로 정리한 구조입니다.
UIView는 Canvas 아래에 존재하는 하나의 화면/팝업 단위입니다.
[RequireComponent(typeof(RectTransform))]
[RequireComponent(typeof(CanvasGroup))]
[DisallowMultipleComponent]
public class UIView : MonoBehaviour
{
[Header("Transition Profile (옵션)")]
public bool overrideTransition = false;
public UITransitionProfile profile;
[Header("Override (overrideTransition=true 일 때만 유효)")]
public UIAnimType entryAnim = UIAnimType.Slide;
public UISlideDir entryDir = UISlideDir.Right;
public float entrySpeed = 2f;
public UIAnimType exitAnim = UIAnimType.Slide;
public UISlideDir exitDir = UISlideDir.Left;
public float exitSpeed = 2f;
[Header("Lifecycle")]
public UILifecycleMode lifecycle = UILifecycleMode.DeactivateOnHide;
[Header("Events")]
public UnityEvent onPreShow;
public UnityEvent onPostShow;
public UnityEvent onPreHide;
public UnityEvent onPostHide;
}주요 역할:
RectTransform/CanvasGroup캐싱 및 제어Show()/Hide()/ForceHide()로 진입/퇴장 애니메이션과 라이프사이클 처리onPreShow,onPostShow,onPreHide,onPostHide이벤트 훅 제공UILifecycleMode값에 따라 Hide 이후 행동 결정KeepActive: GameObject 활성 상태 유지DeactivateOnHide:SetActive(false)DestroyOnHide:Destroy(gameObject)
-
Show()- GameObject를 활성화 (
SetActive(true)) - 최상단으로 올림 (
transform.SetAsLastSibling()) - Transition 설정(프로필/오버라이드)을 해석한 뒤,
UIAnimationHelper를 통해- SlideIn / ZoomIn / FadeIn 중 하나를 코루틴으로 실행
- CanvasGroup의
interactable,blocksRaycasts,alpha를 적절히 세팅 - onPreShow → 애니메이션 → onPostShow 순서로 이벤트 호출
- GameObject를 활성화 (
-
Hide()- Transition 설정에 따라 SlideOut / ZoomOut / FadeOut 실행
- CanvasGroup의 상호작용을 끄고(onPreHide) → 애니메이션 → onPostHide
- 마지막에
ApplyLifecycle()로 GameObject 비활성/파괴 여부 결정
-
ForceHide()- 현재 진행 중인 애니메이션 코루틴을 즉시 중지
- CanvasGroup을 바로 꺼주고(alpha=0 포함)
ApplyLifecycle()을 바로 적용해 애니 없이 즉시 정리
PopTo나 PopAll처럼 “중간 화면들을 애니 없이 정리해야 하는 경우”에
ForceHide()가 사용됩니다.
Core/UITransitionProfile.cs는 전환 설정을 ScriptableObject로 분리한 타입입니다.
[CreateAssetMenu(fileName = "UITransitionProfile", menuName = "UI/Transition Profile")]
public class UITransitionProfile : ScriptableObject
{
[Header("Entry (Show)")]
public UIAnimType entryAnim = UIAnimType.Slide;
public UISlideDir entryDir = UISlideDir.Right;
public float entrySpeed = 2f;
[Header("Exit (Hide)")]
public UIAnimType exitAnim = UIAnimType.Slide;
public UISlideDir exitDir = UISlideDir.Left;
public float exitSpeed = 2f;
}UIAnimType:None,Slide,Zoom,FadeUISlideDir:Left,Right,Up,Down,None
UIView.overrideTransition == false 일 때는 프로필 값을 사용하고,
overrideTransition == true일 때는 UIView 인스펙터에서 직접 지정한 값(entry/exit*)을 사용합니다.
Profiles/Transitions 폴더에는 샘플 프로필이 포함되어 있습니다.
Default_UITransitionProfile.assetNoEnter.assetRightRight.assetUpDown.assetZoom.asset
프로젝트 스타일에 맞는 프로필을 복사/수정해서 사용하면 됩니다.
UIAnimationHelper는 실제 애니메이션을 수행하는 정적 유틸 클래스입니다.
주요 메서드(일부):
IEnumerator SlideIn(RectTransform rect, UISlideDir dir, float speed, UnityAction onEnd)IEnumerator SlideOut(RectTransform rect, UISlideDir dir, float speed, UnityAction onEnd)IEnumerator ZoomIn(RectTransform rect, float speed, UnityAction onEnd)IEnumerator ZoomOut(RectTransform rect, float speed, UnityAction onEnd)IEnumerator FadeIn(CanvasGroup group, float speed, UnityAction onEnd)IEnumerator FadeOut(CanvasGroup group, float speed, UnityAction onEnd)
UIView는 내부에서 이 코루틴들을 사용해 전환 애니메이션을 처리합니다.
Core/ButtonAttribute.cs는 에디터 전용 Attribute입니다.
- 메서드 위에
[Button("라벨명")]을 붙이면,
인스펙터에서 해당 메서드를 버튼 클릭으로 실행할 수 있습니다. UIManager/UITransitionProfile에 예제가 포함되어 있습니다.UIManager.Editor_PrintViewStack()UIManager.Editor_ResetToDefaults()UITransitionProfile.Editor_ResetToDefaults()
게임 실행 중에도 인스펙터에서 바로 호출할 수 있어, 디버깅/초기화에 유용합니다.
Example/UIViewTimingDebug.cs는 UIView 이벤트 타이밍을 로그로 보는 샘플입니다.
DebugTime1()→ OnPreShow에 연결DebugTime2()→ OnPostShow에 연결DebugTime3()→ OnPreHide에 연결DebugTime4()→ OnPostHide에 연결
이 스크립트를 UIView 하위 오브젝트에 붙이고,
UIView의 UnityEvent에 연결하면 애니메이션 진행 순서를 쉽게 확인할 수 있습니다.
- Canvas 아래에 빈 GameObject를 만들고
UIManager컴포넌트를 붙입니다. uiRoot에는 UIView들이 붙을 기준 RectTransform을 지정합니다.
(보통 Canvas의 메인 패널)- 처음에 표시할 UIView를
initialView에 할당합니다. showInitialOnStart를 켜두면,Start()에서 자동으로ResetAndPush(initialView)가 호출됩니다.
- Canvas 아래에 Panel UI를 하나 만들고, 그 루트에
UIView를 붙입니다. CanvasGroup이 자동으로 요구되므로, 없으면 추가합니다.- 레이아웃/버튼/텍스트 등 화면 구성을 하고,
- 전환 방식을 정합니다.
- 공통 프로필 사용:
overrideTransition = false,profile지정 - 개별 설정 사용:
overrideTransition = true, entry/exit 관련 필드 직접 설정
- 공통 프로필 사용:
- Hide 이후 라이프사이클을 선택합니다.
- 자주 다시 보여줄 화면 →
DeactivateOnHide - 일회성 팝업 →
DestroyOnHide등
- 자주 다시 보여줄 화면 →
using UnityEngine;
public class SampleUIEntry : MonoBehaviour
{
[SerializeField] private UIManager uiManager;
[SerializeField] private UIView lobbyView;
[SerializeField] private UIView popupView;
public void OnClick_GoLobby()
{
uiManager.ResetAndPush(lobbyView);
}
public void OnClick_OpenPopup()
{
uiManager.ShowPopup(popupView);
}
public void OnClick_Back()
{
uiManager.Pop();
}
public void OnClick_ClosePopup()
{
uiManager.ClosePopup();
}
}버튼의 OnClick 이벤트에 위 메서드들을 연결해서 사용하면 됩니다.
(혹은, 버튼 OnClick에서 직접 UIManager GameObject를 참조해 Push/ShowPopup/Pop/ClosePopup을 호출해도 됩니다.)
UIView의 entry 애니메이션을UIAnimType.None으로 설정하거나,NoEnter같은 프로필(asset)을 만들어 entryAnim을 None으로 지정한 뒤,
초기 뷰에 해당 프로필을 연결하면 됩니다.
이렇게 하면 첫 진입 시에는 애니 없이 바로 그려지고,
이후 다른 화면에서 다시 Push될 때는 원하는 전환 애니메이션을 쓸 수 있습니다.
이 UI 프레임워크는 다음을 목표로 합니다.
- 스택 기반으로 화면 전환과 팝업을 단일 규칙으로 관리
- UIView 단위로 전환 애니메이션 + 라이프사이클 책임 분리
- ScriptableObject 프로필을 통한 전환 설정 재사용
- ButtonAttribute/Debug 스크립트를 통한 에디터 친화적인 디버깅 지원
필요에 따라 Addressables, ViewId 매핑, 상태 저장(탭/스크롤 위치) 등을 추가해
자기 프로젝트 스타일에 맞는 UI 시스템으로 확장해서 사용할 수 있습니다.