입력 시스템 중앙화: 키보드·마우스와 패드 입력을 유연하게 전환하는 구조 설계
게임 개발 시 키보드·마우스와 게임패드 입력을 추상화하고 입력 기기 전환에 따라 조작 안내 UI를 자동으로 연동하는 중앙화 입력 아키텍처를 소개합니다.

게임 개발 초기에 키보드와 마우스 입력만 기준으로 코드를 구현하면 개발 중반에 콘솔 게임패드 지원을 추가할 때 구조적 한계에 부딪히게 됩니다. 개별 캐릭터나 UI 로직 내부에서 KeyCode.Space 같은 하드웨어 키를 직접 조회하는 방식은 입력 기기 전환이나 키 리바인딩 처리를 매우 복잡하게 만듭니다.
이 글에서는 다양한 입력 장치의 신호를 하나의 액션(Action) 단위로 추상화하고 플레이어가 사용하는 기기 변화를 실시간으로 감지하여 UI까지 자동으로 변경해 주는 중앙화 입력 시스템 구조를 설명합니다.
입력 중앙화가 필요한 이유
하드웨어 입력 신호를 게임플레이 로직에서 직접 참조하면 여러 문제가 발생합니다.
- 코드 중복 및 강한 결합: 이동이나 공격 로직마다 키보드 처리 코드와 패드 조이스틱 처리 코드가 중복 작성됩니다.
- 입력 기기 전환 감지의 난해함: 플레이어가 키보드를 누르다가 게임패드를 조작할 때, 화면에 뜨는 조작 안내(Prompt) 아이콘을 즉시 변경하기 어렵습니다.
- 키 리바인딩 확장성 저하: 키 변경 기능을 구현할 때 모든 개별 조작 스크립트를 수정하거나 예외 코드를 붙여야 합니다.

입력 시스템을 중앙화하면 게임 로직은 어떤 하드웨어 기기가 눌렸는지 직접 파악할 필요 없이 ‘이동’이나 ‘점프’라는 입력 사건이 발생했다는 사실에만 반응할 수 있게 됩니다.
중앙화 입력 아키텍처 설계
중앙화 시스템은 하드웨어 장치에서 오는 신호를 중간에서 수신하고 이를 의미 있는 액션으로 변환한 뒤 게임 시스템과 UI 매니저로 전달하는 중계자 역할을 담당합니다.
flowchart TD
SubDevice1[키보드 / 마우스] --> InputMap[Input Action Map]
SubDevice2[게임패드] --> InputMap
InputMap --> InputManager[Input Manager Hub]
InputManager -->|이동 및 공격 이벤트| PlayerController[Player Controller]
InputManager -->|기기 변경 이벤트| UIManager[UI Prompt Manager]
이 구조의 핵심 흐름은 다음과 같습니다.
- Input Action Mapping:
Jump,Attack,Move처럼 의미 중심의 액션을 정의하고 각 기기별 키 파인딩을 매핑합니다. - Input Hub (Manager): 입력 신호를 구독하여 최신 입력 상태를 유지하며 현재 활성화된 입력 기기를 추적합니다.
- Event Broadcasting: 캐릭터 조작 로직에는 액션 이벤트를 전달하고 UI 시스템에는 기기 변경 이벤트를 발행합니다.
실전 구현: 유니티 Unity Input System 활용 예시
유니티의 신규 입력 시스템(Input System)을 활용하면 이 아키텍처를 효과적으로 구축할 수 있습니다. 입력 액션 자산을 만들고 이를 처리하는 InputManager 클래스를 작성합니다.
using System;
using UnityEngine;
using UnityEngine.InputSystem;
public enum InputDeviceType
{
KeyboardMouse,
Gamepad
}
public class InputManager : MonoBehaviour
{
public static InputManager Instance { get; private set; }
private PlayerInputActions inputActions;
public event Action<Vector2> OnMoveInput;
public event Action OnJumpInput;
public event Action<InputDeviceType> OnDeviceChanged;
public InputDeviceType CurrentDevice { get; private set; } = InputDeviceType.KeyboardMouse;
private void Awake()
{
if (Instance != null && Instance != this)
{
Destroy(gameObject);
return;
}
Instance = this;
DontDestroyOnLoad(gameObject);
inputActions = new PlayerInputActions();
}
private void OnEnable()
{
if (Instance != this || inputActions == null) return;
inputActions.Player.Move.performed += HandleMovePerformed;
inputActions.Player.Move.canceled += HandleMoveCanceled;
inputActions.Player.Jump.performed += HandleJumpPerformed;
inputActions.Player.Enable();
InputSystem.onActionChange += HandleActionChange;
}
private void OnDisable()
{
if (Instance != this || inputActions == null) return;
InputSystem.onActionChange -= HandleActionChange;
inputActions.Player.Move.performed -= HandleMovePerformed;
inputActions.Player.Move.canceled -= HandleMoveCanceled;
inputActions.Player.Jump.performed -= HandleJumpPerformed;
inputActions.Player.Disable();
}
private void OnDestroy()
{
if (Instance == this)
Instance = null;
inputActions?.Dispose();
}
private void HandleMovePerformed(UnityEngine.InputSystem.InputAction.CallbackContext context)
{
OnMoveInput?.Invoke(context.ReadValue<Vector2>());
}
private void HandleMoveCanceled(UnityEngine.InputSystem.InputAction.CallbackContext context)
{
OnMoveInput?.Invoke(Vector2.zero);
}
private void HandleJumpPerformed(UnityEngine.InputSystem.InputAction.CallbackContext context)
{
OnJumpInput?.Invoke();
}
private void HandleActionChange(object obj, InputActionChange change)
{
if (change != InputActionChange.ActionPerformed) return;
var action = obj as InputAction;
if (action == null || inputActions == null || !inputActions.Contains(action)) return;
var lastControl = action?.activeControl;
if (lastControl == null) return;
InputDeviceType detectedDevice = lastControl.device is Gamepad
? InputDeviceType.Gamepad
: InputDeviceType.KeyboardMouse;
if (detectedDevice != CurrentDevice)
{
CurrentDevice = detectedDevice;
OnDeviceChanged?.Invoke(CurrentDevice);
}
}
}
이 구현체는 입력 이벤트를 분산 발생시킴과 동시에 InputSystem.onActionChange를 관찰하여 플레이어가 마지막으로 조작한 기기 종류를 감지하고 이벤트를 전송합니다.
입력 기기 자동 전환과 UI 프롬프트 연동
입력 기기가 전환될 때 조작 가이드 UI 아이콘도 실시간으로 변경되어야 뛰어난 사용자 경험을 제공할 수 있습니다. 키보드 상태에서는 [E] 키 아이콘을 노출하고 게임패드가 조작되면 즉시 [X] 버튼 아이콘으로 변경하는 방식입니다.

UI 스크립트는 InputManager의 기기 변경 이벤트를 구독하여 아이콘을 전환합니다.
using UnityEngine;
using UnityEngine.UI;
public class InteractionPromptUI : MonoBehaviour
{
[SerializeField] private Image promptIconImage;
[SerializeField] private Sprite keyboardIcon;
[SerializeField] private Sprite gamepadIcon;
private void Start()
{
if (InputManager.Instance != null)
{
InputManager.Instance.OnDeviceChanged += UpdatePromptIcon;
UpdatePromptIcon(InputManager.Instance.CurrentDevice);
}
}
private void OnDestroy()
{
if (InputManager.Instance != null)
{
InputManager.Instance.OnDeviceChanged -= UpdatePromptIcon;
}
}
private void UpdatePromptIcon(InputDeviceType deviceType)
{
promptIconImage.sprite = (deviceType == InputDeviceType.Gamepad)
? gamepadIcon
: keyboardIcon;
}
}
중앙화 시스템 구현 시 유의점
안정적인 시스템 구축을 위해 다음 세 가지 요소를 함께 고려해야 합니다.
- 데드존(Deadzone) 예외 처리: 패드의 아날로그 스틱 미세 쏠림 현상 때문에 유저가 조작하지 않아도 기기 변경 이벤트가 잘못 실행될 수 있습니다. 중앙 입력 장치에서 적절한 데드존 임계값을 설정해야 합니다.
- 마우스 커서 가시성 제어: 키보드·마우스 모드에서는 마우스 커서를 보여주고 패드 입력 모드 전환 시 커서를 숨기며 UI 포커스를 첫 번째 조작 버튼으로 이동시켜야 합니다.
- 상태별 Action Map 분리: UI 메뉴 창이나 컷씬 진행 중에는 게임 플레이용 입력 맵을 비활성화하고 UI 전용 입력 맵만 활성화하여 의도치 않은 조작을 방지합니다.
입력 구조를 중앙화하고 이벤트를 분리하면 콘솔 포팅이나 다중 플랫폼 출시 때 조작 관련 유지보수 부담을 줄일 수 있습니다.



