Input System Package로 구현하는 멀티플랫폼 Cross-Control
Unity Input System Package의 액션 중심 구조로 키보드, 게임패드, 터치 입력을 하나의 이동 제어로 묶는 방법을 정리합니다. 온스크린 컨트롤과 제어 방식 전환 시 주의할 점까지 실용적으로 다룹니다.

Cross-Control이 필요한 이유
PC에서는 WASD나 방향키, 콘솔에서는 왼쪽 스틱과 D-pad, 모바일에서는 가상 패드가 자연스럽습니다. 입력 장치마다 코드를 따로 만들면 이동 로직과 입력 분기 코드가 서로 얽히기 쉽습니다.
Input System Package는 실제 버튼이나 스틱 대신 Move, Jump, Attack처럼 의미 있는 액션을 중심으로 입력을 구성합니다. 게임 플레이 코드는 어떤 장치가 입력을 보냈는지 알 필요 없이 액션의 결과만 받으면 됩니다.
flowchart LR
K[키보드] --> A[Move 액션]
G[게임패드] --> A
T[터치 온스크린 컨트롤] --> A
A --> P[플레이어 이동 로직]
이 구조의 핵심은 장치별 입력을 액션에 연결하고 캐릭터는 Vector2 이동값만 처리하도록 분리하는 데 있습니다.
Input Actions 에셋 구성
패키지 매니저에서 Input System을 설치한 뒤 Create > Input Actions로 에셋을 만듭니다. 여기서는 에셋 이름을 PlayerInputActions라고 가정합니다.
Player 액션 맵에 다음 액션을 추가합니다.
| 액션 | 타입 | 용도 |
|---|---|---|
| Move | Value / Vector2 | 2D 이동 방향 |
| Jump | Button | 점프 또는 확인 |
| Attack | Button | 공격 |
Move에는 아래 바인딩을 추가합니다.
2D Vector Composite:W,A,S,D2D Vector Composite: 방향키<Gamepad>/leftStick<Gamepad>/dpad
키보드 복합 바인딩은 Up, Down, Left, Right 슬롯에 각각 키를 지정합니다. 대각선 입력도 자동으로 Vector2 값으로 합성됩니다.

에셋 Inspector에서 Generate C# Class를 활성화하면 액션 이름을 문자열로 다루지 않아도 됩니다. 액션 이름을 바꾸거나 삭제했을 때 컴파일 오류로 빠르게 확인할 수 있다는 장점도 있습니다.
플레이어 코드에서 액션 읽기
다음 예시는 생성된 래퍼 클래스를 직접 사용하는 방식입니다. CharacterController 이동 예제이지만 Rigidbody나 2D 캐릭터 컨트롤러에도 같은 입력 구조를 적용할 수 있습니다.
using UnityEngine;
public class PlayerMovement : MonoBehaviour
{
[SerializeField] private CharacterController controller;
[SerializeField] private float moveSpeed = 5f;
private PlayerInputActions inputActions;
private Vector2 moveInput;
private void Awake()
{
inputActions = new PlayerInputActions();
}
private void OnEnable()
{
inputActions.Player.Enable();
inputActions.Player.Move.performed += OnMove;
inputActions.Player.Move.canceled += OnMove;
}
private void OnDisable()
{
inputActions.Player.Move.performed -= OnMove;
inputActions.Player.Move.canceled -= OnMove;
inputActions.Player.Disable();
}
private void OnMove(UnityEngine.InputSystem.InputAction.CallbackContext context)
{
moveInput = context.ReadValue<Vector2>();
}
private void Update()
{
Vector3 direction = new Vector3(moveInput.x, 0f, moveInput.y);
controller.Move(direction * moveSpeed * Time.deltaTime);
}
}
performed에서 값을 저장하고 Update에서 이동을 처리하면 입력 수집과 게임 로직의 책임을 나눌 수 있습니다. canceled에도 같은 콜백을 연결해야 키를 놓거나 스틱을 중립으로 되돌렸을 때 이동값이 Vector2.zero로 갱신됩니다.
단, 대각선 이동은 수평 또는 수직 이동보다 길이가 커질 수 있습니다. 이동 속도를 일정하게 유지하려면 방향 벡터를 정규화합니다.
private void Update()
{
Vector3 direction = new Vector3(moveInput.x, 0f, moveInput.y);
if (direction.sqrMagnitude > 1f)
{
direction.Normalize();
}
controller.Move(direction * moveSpeed * Time.deltaTime);
}
모바일 가상 패드 연결하기
Input System의 On-Screen Stick과 On-Screen Button 컴포넌트는 터치 입력을 게임패드 컨트롤처럼 에뮬레이션합니다. 따라서 별도의 모바일 이동 코드를 만들지 않고 기존 Move 바인딩을 그대로 사용할 수 있습니다.
Canvas에 조이스틱 UI를 배치한 뒤 다음처럼 설정합니다.
- 조이스틱 오브젝트에
On-Screen Stick추가 Control Path를<Gamepad>/leftStick으로 설정- 점프 버튼에
On-Screen Button추가 - 점프 버튼의
Control Path를<Gamepad>/buttonSouth로 설정 Jump액션에<Gamepad>/buttonSouth바인딩 추가

온스크린 컨트롤은 게임패드 입력 경로를 발생시키므로 PC에서 실제 게임패드를 연결했을 때도 동일한 액션이 동작합니다. UI의 표시 여부만 플랫폼 또는 현재 입력 장치에 맞춰 제어하면 됩니다.
using UnityEngine;
using UnityEngine.InputSystem;
public class MobileControlVisibility : MonoBehaviour
{
[SerializeField] private GameObject mobileControls;
private void Awake()
{
mobileControls.SetActive(Application.isMobilePlatform);
}
}
에디터에서 터치 UI를 시험해야 한다면 이 코드를 임시로 끄거나 별도의 디버그 옵션을 두는 편이 편리합니다.
PlayerInput 컴포넌트를 사용할 때
생성 코드 대신 PlayerInput 컴포넌트를 이용해도 됩니다. 플레이어 오브젝트에 컴포넌트를 추가하고 Actions에 Input Actions 에셋을 지정한 뒤 Default Map을 Player로 설정합니다.
이 방식은 로컬 멀티플레이어, 기기별 액션 맵 전환, UI 입력 모듈 연동이 필요한 프로젝트에서 특히 유용합니다. 다만 Send Messages 방식은 메서드 이름이 문자열 규칙에 묶이므로 규모가 커질수록 Invoke Unity Events 또는 생성된 C# 클래스 방식이 추적하기 쉽습니다.
구현 시 확인할 점
액션 타입을 맞춘다
이동은 Value와 Vector2, 점프와 공격은 Button으로 설정합니다. Move를 Button으로 만들면 스틱 값을 올바르게 읽을 수 없습니다.
액션 맵은 상태에 따라 전환한다
게임 플레이 중에는 Player, 메뉴에서는 UI 액션 맵만 활성화하는 식으로 구성합니다. 메뉴가 열린 상태에서도 이동 액션이 켜져 있으면 버튼 조작이 캐릭터 이동으로 이어질 수 있습니다.
inputActions.Player.Disable();
inputActions.UI.Enable();
입력 장치와 UI 표시를 분리한다
모바일 UI를 숨겼다고 해서 게임패드 바인딩까지 제거할 필요는 없습니다. 입력 바인딩은 유지하고 화면에 보이는 조작 UI만 상황에 맞게 바꾸는 편이 유지 보수에 유리합니다.
리바인딩은 액션 기준으로 저장한다
키 설정 화면을 만든다면 키 코드 자체보다 바인딩 오버라이드를 저장해야 합니다. 그러면 키보드와 게임패드의 기본 바인딩을 유지하면서 사용자가 바꾼 입력만 덮어쓸 수 있습니다.
마무리
Cross-Control의 목표는 모든 플랫폼에 같은 조작 UI를 강제하는 것이 아닙니다. 각 플랫폼에 자연스러운 입력을 제공하면서도 게임 코드에서는 하나의 액션만 다루게 만드는 것입니다.
Move 같은 공통 액션을 먼저 설계하고 키보드, 게임패드, 온스크린 컨트롤을 같은 액션에 바인딩하면 플랫폼이 늘어나도 플레이어 로직을 복제하지 않고 확장할 수 있습니다.



