6. 그림으로 보는 설정 단계
CGS를 여러분의 애셋에 연결하는 선택적 레시피 — 오디오 믹서, 입력 액션, IL2CPP link.xml, Addressables.
이 장의 어떤 것도 프레임워크 사용을 시작하는 데 필수는 아닙니다 — 모든 것이 그대로 바로 동작합니다. 이 단계들은 프레임워크를 여러분의 애셋에 연결합니다: 여러분의 오디오 믹서, 입력 바인딩, 빌드 설정, 그리고 (선택적으로) Addressables. 각 단계는 처음부터 끝까지 따라 할 수 있는 완전한 레시피입니다.
6.1 자체 오디오 믹서 사용하기
오디오 서비스는 번들 믹서와 함께 배포되므로 사운드가 즉시 동작합니다. 자체 Unity AudioMixer 애셋을 쓸 준비가 되면 두 가지가 필요합니다:
- Master 아래, 정확히
Music,Sfx,Voice라는 이름의 그룹 세 개. 오디오 서비스는 각 채널을 이름이 일치하는 그룹으로 라우팅합니다. - 볼륨용으로 노출된 float 파라미터 네 개, 이름은
MasterVolume,MusicVolume,SfxVolume,VoiceVolume. 파라미터를 노출하려면 그룹을 선택하고 Inspector에서 Volume 필드를 우클릭한 뒤 "Expose ... to script"를 선택하세요. 이름은 믹서의 Exposed Parameters 드롭다운에서 바꿉니다.

Master 아래 필수 그룹 세 개(Music, Sfx, Voice)가 있는 Audio Mixer 창. Exposed Parameters 드롭다운(오른쪽 위)에는 볼륨 파라미터 네 개가 나열되어야 합니다: MasterVolume, MusicVolume, SfxVolume, VoiceVolume.
그다음 첫 씬에 넣은 작은 스크립트로 믹서를 프레임워크에 건네주세요. 여러분의 믹서를 중심으로 새 오디오 서비스를 만들어 교체해 넣습니다:
using CommonGameSystem.Core;
using UnityEngine;
using UnityEngine.Audio;
[DefaultExecutionOrder(-100)] // swap before other scripts cache the audio service
public sealed class AudioSetup : MonoBehaviour
{
[SerializeField] private AudioMixer gameMixer; // assign your mixer in the Inspector
private void Awake()
{
var options = AudioOptions.Default;
options.Mixer = gameMixer;
// Used different parameter names? Point the service at them:
// options.masterParam = "MyMasterVol";
var previous = ServiceLocator.Resolve<IAudioService>() as System.IDisposable;
ServiceLocator.Replace<IAudioService>(new AudioService(
ServiceLocator.Resolve<IConfiguration>(),
ServiceLocator.Resolve<IEventBus>(),
ServiceLocator.Resolve<IObjectPoolService>(),
ServiceLocator.Resolve<ITimeService>(),
options));
previous?.Dispose(); // removes the old service's hidden helper object
}
}
옵션 메뉴의 볼륨 슬라이더는 믹서가 아니라 설정 서비스와 대화합니다. Get<AudioSettings>()로 현재 그룹을 읽고, 필드를 바꾼 뒤, Set(...)을 호출하고 이어서 FlushPending<AudioSettings>()를 호출하세요. 플러시는 변경을 저장하고 새 볼륨을 즉시 믹서로 밀어 넣습니다. 전체 레퍼런스: Audio와 Configuration.
6.2 자체 입력 액션 연결하기
입력 서비스는 여러분이 만드는 Unity Input System 애셋(.inputactions 파일)을 읽습니다 — 프레임워크에는 빈 자리 표시용 애셋만 들어 있습니다. 여러분의 애셋이 액션 맵("Gameplay", "Menu")과 그 안의 바인딩을 정의합니다.
- 애셋 만들기: Project 창에서 우클릭한 뒤 Create > Input Actions. 이름은
GameInput같은 것으로 지으세요. - 열어서 액션 맵과 액션을 추가하세요 — 예를 들어 "Move"와 "Jump"가 있는 "Gameplay" 맵.
- 자동 입력 전환 기능이 있는 프레임워크의 UI 패널 스택을 쓴다면, 정확히
ui.panel과ui.modal이라는 이름의 맵 두 개도 추가하세요. 메뉴가 열려 있는 동안 패널 스택이 이 맵들을 활성화합니다. 이름은 대소문자를 구분합니다.

"Gameplay" 맵과 패널 스택 맵 두 개 ui.panel, ui.modal(강조 표시)이 있는 애셋을 보여주는 Input Actions 에디터 — 목표로 삼을 최종 상태입니다. 프레임워크는 이 애셋을 절대 수정하지 않습니다 — 데드존, 홀드, 바인딩은 온전히 여러분의 통제 아래에 있습니다.
첫 씬의 스크립트 하나로 애셋을 연결하세요. 여러분의 애셋을 중심으로 입력 서비스를 다시 만들고, 메뉴 내비게이션도 이를 쓰도록 패널 스택을 다시 만듭니다:
using CommonGameSystem.Core;
using UnityEngine;
using UnityEngine.InputSystem;
[DefaultExecutionOrder(-100)] // swap before other scripts cache these services
public sealed class InputSetup : MonoBehaviour
{
[SerializeField] private InputActionAsset actions; // your .inputactions asset
private void Awake()
{
var source = new HardcodedInputKeyMapSource(actions);
var bus = ServiceLocator.Resolve<IEventBus>();
var time = ServiceLocator.Resolve<ITimeService>();
var oldInput = ServiceLocator.Resolve<IInputService>() as System.IDisposable;
var oldUi = ServiceLocator.Resolve<IPanelStack>() as System.IDisposable;
ServiceLocator.Replace<IInputKeyMapSource>(source);
var input = new DefaultInputService(source, bus);
ServiceLocator.Replace<IInputService>(input);
ServiceLocator.Replace<IPanelStack>(
new PanelStack(bus, input, time, PanelStackOptions.Default));
oldInput?.Dispose();
oldUi?.Dispose();
}
}
패널 스택을 쓰지 않는다면 IPanelStack 줄 두 개와 oldUi 줄들을 지우세요. 여러분의 스크립트가 자체 어셈블리 정의 안에 있다면 이 스크립트에는 CommonGameSystem.Input, CommonGameSystem.UI, Unity.InputSystem 참조가 필요합니다 — 7.1장의 표를 보세요.
재시작해도 유지되는 리바인딩. 플레이어가 컨트롤을 다시 매핑하면(서비스의 대화형 리바인드가 새 키 누르기를 안내합니다), 그 후에 SaveBindingOverrides()를 호출하세요. 오버라이드는 Unity의 PlayerPrefs에 저장됩니다. 시작 시 LoadBindingOverrides()를 한 번 호출해 복원하고, "기본값 복원" 버튼에는 ResetBindingOverrides()를 쓰세요. 전체 레퍼런스: Input.
6.3 IL2CPP 빌드: 세이브 클래스 살려 두기 (link.xml)
IL2CPP 빌드는 게임을 작게 만들기 위해 사용되지 않아 보이는 코드를 스트리핑합니다. 직렬화를 통해서만 생성되는 클래스 — 세이브 데이터, 커스텀 설정 그룹, 직렬화하는 이벤트 클래스 — 는 스트리퍼에게 사용되지 않는 것처럼 보일 수 있습니다. 그 결과: 에디터에서는 저장이 되다가 빌드된 게임에서 조용히 깨집니다.
해결책은 Unity에 "이것들은 절대 스트리핑하지 마"라고 알려주는 link.xml 파일입니다. Assets/ 아래 아무 곳에나(폴더 루트면 충분합니다) 정확히 link.xml이라는 이름의 파일을 만들고 여러분의 클래스를 나열하세요:
<linker>
<assembly fullname="Assembly-CSharp">
<type fullname="MyGame.PlayerSave" preserve="all"/>
<type fullname="MyGame.OptionsSave" preserve="all"/>
</assembly>
</linker>
이 파일은 어디에 두나요?
Assets/link.xml로 저장하세요. 각<type>줄은 여러분의 클래스 하나를 전체 네임스페이스와 함께 지정합니다. "Assembly-CSharp"은 프로젝트 스크립트의 기본 어셈블리입니다 — 코드가 어셈블리 정의를 쓴다면 그 어셈블리 이름을 대신 쓰세요.
경험 법칙:
Save<T>(...)에 넘기거나 설정 그룹으로 저장하는 클래스마다<type>줄을 하나씩 추가하세요.fullname은 네임스페이스를 포함한 클래스 이름입니다. 여기서의 오타는 조용히 실패하므로 코드에서 복사해 오세요.- 프레임워크 자체 타입은 이미 보호되어 있습니다. 여러분의 클래스만 나열하면 됩니다.
- 이것은 IL2CPP 빌드에만 영향을 줍니다. 에디터와 Mono 빌드는 스트리핑을 하지 않으며, 버그가 실제 빌드 전까지 숨어 있는 이유가 바로 이것입니다.
6.4 Addressables 설정 (주소로 애셋을 로드할 때만)
두 서비스가 Unity의 Addressables 패키지를 필요로 합니다: 애셋 프로바이더(프리팹, 스프라이트, 오디오를 텍스트 주소로 로드하며 자동 참조 카운팅 제공)와 어드레서블 씬 서비스(Addressables의 애디티브 씬). 둘 다 쓰지 않는다면 이 절은 건너뛰세요 — 패키지 없이도 프레임워크는 잘 돌아갑니다.
- Window > Package Manager의 Unity Registry 탭에서 Addressables(
com.unity.addressables)를 설치합니다. - Window > Asset Management > Addressables > Groups를 열고 Create Addressables Settings를 한 번 클릭합니다.
- 런타임에 로드하려는 애셋을 선택하고 Inspector 상단의 Addressable을 체크합니다. 체크박스 옆 텍스트 필드가 그 애셋의 주소입니다.
- 그 주소 문자열이 프레임워크에 넘기는 키입니다:
await assets.LoadAsync<GameObject>("characters/player").
에디터에서는 Play 모드가 어드레서블 애셋을 프로젝트에서 직접 로드하므로 자유롭게 반복 작업할 수 있습니다. 플레이어 빌드를 출시하기 전에는 Groups 창에서 Addressables 콘텐츠를 한 번 빌드하세요(Build > New Build > Default Build Script). 패키지가 설치되어 있으면 두 서비스 모두 시작 시 자동으로 등록됩니다 — 추가 프레임워크 설정은 없습니다.