Ver2.0 Unity 입력 시스템 패키지로 모든 플랫폼 완벽 제어하기: 크로스 플랫폼 입력 관리의 모든 것

Unity 입력 시스템 패키지로 모든 플랫폼 완벽 제어하기: 크로스 플랫폼 입력 관리의 모든 것
안녕? 오늘은 게임 개발자라면 꼭 알아야 할 Unity의 새로운 입력 시스템 패키지에 대해 함께 알아볼 거야! 🎮 키보드, 마우스부터 모바일 터치, 게임패드까지 모든 입력을 하나의 시스템으로 관리하는 방법을 재미있게 설명해 줄게. 이 글을 다 읽고 나면 너도 크로스 플랫폼 입력 관리의 달인이 될 수 있을 거야! 👑
📚 목차
- Unity 입력 시스템의 진화: 왜 새 입력 시스템이 필요할까?
- 새 입력 시스템 패키지 설치 및 기본 설정
- Input Action Asset 만들기 및 설정하기
- 다양한 디바이스 입력 처리하기
- 플랫폼별 입력 최적화 전략
- 실전 예제: 크로스 플랫폼 캐릭터 컨트롤러 만들기
- 입력 시스템 디버깅 및 문제 해결
- 성능 최적화 팁과 트릭
- 마무리 및 추가 자료
1. Unity 입력 시스템의 진화: 왜 새 입력 시스템이 필요할까? 🚀
Unity로 게임을 개발해 봤다면 Input.GetKey(), Input.GetButton() 같은 함수들을 써봤을 거야. 이 함수들은 Unity의 전통적인 입력 시스템의 일부였지. 근데 이 시스템에는 몇 가지 한계가 있었어.
🔍 기존 입력 시스템의 한계
- 다양한 디바이스 지원이 제한적이었어 (특히 새로운 컨트롤러)
- 입력 리매핑(사용자 정의 키 설정)이 복잡했지
- 여러 플랫폼에서 일관된 코드 작성이 어려웠어
- 컨텍스트 기반 입력 처리가 불편했지
- 복잡한 제스처나 조합 입력 구현이 까다로웠어
새로운 입력 시스템은 이런 문제들을 해결하기 위해 탄생했어! 이제 하나의 코드로 PC, 콘솔, 모바일, VR 등 모든 플랫폼의 입력을 처리할 수 있게 됐지. 재능넷에서도 많은 개발자들이 이 새로운 시스템을 활용한 게임 개발 노하우를 공유하고 있어. 특히 다양한 플랫폼을 지원해야 하는 개발자들에게 정말 유용한 도구야! 🛠️
새 입력 시스템의 가장 큰 특징은 'Action-based' 접근 방식이야. 이게 무슨 말이냐면, 우리가 "점프하기", "발사하기" 같은 '행동'에 집중하고, 그 행동을 어떤 디바이스의 어떤 버튼이 트리거하는지는 쉽게 설정하고 변경할 수 있게 해준다는 거지. 이렇게 하면 코드는 그대로 두고 입력 매핑만 바꿔서 다양한 플랫폼에 대응할 수 있어! 😎
2. 새 입력 시스템 패키지 설치 및 기본 설정 ⚙️
자, 이제 새 입력 시스템을 설치하고 기본 설정을 해보자! 생각보다 훨씬 쉬울 거야.
📥 패키지 설치하기
- Unity 에디터에서
Window > Package Manager메뉴를 열어줘 - "+" 버튼을 누르고
Add package by name선택 com.unity.inputsystem을 입력하고 'Add' 버튼 클릭- 또는 패키지 목록에서 "Input System"을 검색해서 설치할 수도 있어
💡 참고: Unity 2019.1 이상 버전에서만 새 입력 시스템을 사용할 수 있어. 가능하면 최신 버전의 Unity를 사용하는 걸 추천할게!
패키지를 설치하면 Unity가 재시작을 요청할 거야. 이때 두 가지 옵션이 나타날 수 있어:
새 프로젝트라면 "Input System Package"를 선택하는 게 좋아. 기존 프로젝트를 마이그레이션 중이라면 "Both"를 선택해서 천천히 전환하는 것도 좋은 방법이야! 🔄
설치가 완료되면 프로젝트 설정에서 입력 시스템 관련 옵션을 확인해 볼 수 있어. Edit > Project Settings > Input System Package에서 다음과 같은 설정들을 조정할 수 있지:
⚙️ 주요 설정 옵션
- Update Mode: 입력 이벤트가 처리되는 시점 (Process Events in Fixed Update, Dynamic Update 등)
- Filter Noise on Current: 아날로그 입력의 노이즈 필터링 여부
- Compensate for Screen Orientation: 모바일 기기 회전 시 입력 방향 보정 여부
- Default Button Press Point: 버튼 입력으로 인식되는 기본 임계값
- Default Dead Zone Values: 조이스틱 데드존(무시할 미세 움직임 범위) 설정
🔥 프로 팁: 모바일 게임을 개발한다면 "Compensate for Screen Orientation" 옵션을 켜두는 것이 좋아! 이렇게 하면 기기 회전에 따라 입력 방향이 자동으로 조정되어 사용자 경험이 향상돼.
3. Input Action Asset 만들기 및 설정하기 📝
이제 본격적으로 입력 시스템을 사용해볼 차례야! 새 입력 시스템의 핵심은 Input Action Asset이라는 에셋이야. 이 에셋에서 게임의 모든 입력 액션과 바인딩을 정의하게 될 거야.
🛠️ Input Action Asset 생성하기
- 프로젝트 창에서 우클릭 >
Create > Input Actions선택 - 생성된 에셋의 이름을 의미 있게 지정해줘 (예: "GameInputs")
- 에셋을 더블클릭해서 Input Action 에디터 열기
Input Action Asset은 다음과 같은 계층 구조로 구성돼:
- Action Maps: 관련 액션들의 그룹 (예: "Player", "UI", "Vehicle")
- Actions: 게임에서 수행할 수 있는 동작들 (예: "Move", "Jump", "Shoot")
- Bindings: 액션을 트리거하는 실제 입력들 (예: WASD 키, 게임패드 스틱)
자, 이제 간단한 예시로 플레이어 캐릭터를 위한 Input Action Asset을 만들어보자:
🎮 플레이어 입력 설정 예시
- Action Map 추가: "Player"
- "Player" 맵에 다음 액션들 추가:
- "Move" (Value 타입: Vector2) - 캐릭터 이동
- "Jump" (Value 타입: Button) - 점프
- "Attack" (Value 타입: Button) - 공격
- "Interact" (Value 타입: Button) - 상호작용
- 각 액션에 적절한 바인딩 추가 (키보드/마우스 및 게임패드)
각 액션에는 다양한 Value 타입을 설정할 수 있어:
- Button: 눌림/떼짐 상태를 가진 버튼 입력 (예: 점프, 발사)
- Vector2: 2D 벡터 값 (예: 이동 방향, 마우스 위치)
- Vector3: 3D 벡터 값 (예: 3D 공간에서의 움직임)
- Quaternion: 회전 값 (예: VR 컨트롤러 회전)
- Axis: 단일 축 값 (예: 트리거 버튼 압력)
💻 C# 스크립트에서 Input Action Asset 사용하기
Input Action Asset을 설정한 후에는 C# 스크립트에서 이를 사용할 수 있어:
using UnityEngine;
using UnityEngine.InputSystem;
public class PlayerController : MonoBehaviour
{
// Input Action Asset 참조
public InputActionAsset inputActions;
// 개별 액션들
private InputAction moveAction;
private InputAction jumpAction;
private void Awake()
{
// 액션 맵 가져오기
var playerActionMap = inputActions.FindActionMap("Player");
// 개별 액션 가져오기
moveAction = playerActionMap.FindAction("Move");
jumpAction = playerActionMap.FindAction("Jump");
// 액션에 콜백 함수 등록
jumpAction.performed += OnJump;
// 액션 맵 활성화
playerActionMap.Enable();
}
private void OnJump(InputAction.CallbackContext context)
{
// 점프 로직 구현
Debug.Log("Jump!");
}
private void Update()
{
// Move 액션의 현재 값 읽기
Vector2 moveInput = moveAction.ReadValue<Vector2>();
// 이동 로직에 사용
transform.Translate(new Vector3(moveInput.x, 0, moveInput.y) * Time.deltaTime * 5f);
}
private void OnDestroy()
{
// 콜백 해제
jumpAction.performed -= OnJump;
// 액션 맵 비활성화
var playerActionMap = inputActions.FindActionMap("Player");
playerActionMap.Disable();
}
}
💡 중요 팁: Input Action Asset을 C# 클래스로 생성하면 더 편리하게 사용할 수 있어! 에셋 인스펙터에서 "Generate C# Class"를 체크하고 파일 이름을 지정하면 돼. 이렇게 하면 타입 안전성이 보장되고 코드 자동 완성 기능도 사용할 수 있지!
4. 다양한 디바이스 입력 처리하기 🎮📱🖱️
새 입력 시스템의 가장 큰 장점 중 하나는 다양한 입력 디바이스를 쉽게 지원할 수 있다는 거야. 이제 키보드, 마우스, 게임패드, 터치스크린, VR 컨트롤러까지 모두 하나의 시스템으로 관리할 수 있어!
🔌 지원하는 주요 디바이스
- 키보드 및 마우스
- Xbox, PlayStation, Nintendo Switch 컨트롤러
- 모바일 터치스크린 및 가속도계
- VR 컨트롤러 (Oculus, Vive, Windows MR 등)
- 조이스틱 및 HOTAS 컨트롤러
- 사용자 정의 디바이스
하나의 액션에 여러 디바이스의 입력을 바인딩하는 것이 새 입력 시스템의 핵심이야. 예를 들어, "Jump" 액션에 키보드의 스페이스바, Xbox 컨트롤러의 A 버튼, PS 컨트롤러의 X 버튼을 모두 바인딩할 수 있지!
🔄 디바이스 전환 자동 감지
새 입력 시스템은 사용자가 어떤 디바이스를 사용하고 있는지 자동으로 감지하고 그에 맞게 UI 프롬프트를 업데이트할 수 있어. 이건 크로스 플랫폼 게임에서 정말 유용한 기능이지!
using UnityEngine;
using UnityEngine.InputSystem;
public class DeviceDetector : MonoBehaviour
{
[SerializeField] private GameObject keyboardPrompts;
[SerializeField] private GameObject gamepadPrompts;
[SerializeField] private GameObject touchPrompts;
private void Start()
{
// 초기 디바이스 설정
UpdatePrompts();
// 디바이스 변경 이벤트 구독
InputSystem.onActionChange += OnInputDeviceChanged;
}
private void OnInputDeviceChanged(object obj, InputActionChange change)
{
if (change == InputActionChange.ActionPerformed)
{
UpdatePrompts();
}
}
private void UpdatePrompts()
{
// 마지막으로 사용된 디바이스 확인
var lastDevice = InputSystem.GetDevice<InputDevice>();
// 디바이스 타입에 따라 UI 프롬프트 업데이트
keyboardPrompts.SetActive(lastDevice is Keyboard || lastDevice is Mouse);
gamepadPrompts.SetActive(lastDevice is Gamepad);
touchPrompts.SetActive(lastDevice is Touchscreen);
}
private void OnDestroy()
{
// 이벤트 구독 해제
InputSystem.onActionChange -= OnInputDeviceChanged;
}
}
👆 터치 입력 처리하기
모바일 게임 개발자라면 터치 입력 처리가 중요할 거야. 새 입력 시스템에서는 터치 입력도 쉽게 처리할 수 있어:
// Input Action Asset에서 터치 액션 설정:
// - "PrimaryTouch" (Value 타입: Vector2, Control 타입: Touch > Position)
// - "PrimaryTap" (Value 타입: Button, Control 타입: Touch > Press)
using UnityEngine;
using UnityEngine.InputSystem;
using UnityEngine.InputSystem.Controls;
public class TouchController : MonoBehaviour
{
private InputAction touchPositionAction;
private InputAction touchPressAction;
private void Awake()
{
var inputAsset = GetComponent<PlayerInput>().actions;
var touchMap = inputAsset.FindActionMap("Touch");
touchPositionAction = touchMap.FindAction("PrimaryTouch");
touchPressAction = touchMap.FindAction("PrimaryTap");
touchPressAction.performed += OnTap;
}
private void OnTap(InputAction.CallbackContext context)
{
// 탭 위치 가져오기
Vector2 touchPos = touchPositionAction.ReadValue<Vector2>();
// 터치 위치에서 레이캐스트
Ray ray = Camera.main.ScreenPointToRay(touchPos);
if (Physics.Raycast(ray, out RaycastHit hit))
{
// 터치한 오브젝트와 상호작용
Debug.Log($"Tapped on: {hit.collider.gameObject.name}");
}
}
private void OnDestroy()
{
touchPressAction.performed -= OnTap;
}
}
🔥 프로 팁: 멀티 터치 지원이 필요하다면 EnhancedTouchSupport를 활성화하는 것이 좋아! 이렇게 하면 최대 10개의 동시 터치를 추적할 수 있어.
// 스크립트 시작 부분에 추가
using UnityEngine.InputSystem.EnhancedTouch;
private void Awake()
{
// 향상된 터치 지원 활성화
EnhancedTouchSupport.Enable();
}
5. 플랫폼별 입력 최적화 전략 🚀
다양한 플랫폼에서 게임을 출시한다면, 각 플랫폼의 특성에 맞게 입력 시스템을 최적화하는 것이 중요해. 재능넷에서도 많은 개발자들이 이런 크로스 플랫폼 최적화 노하우를 공유하고 있지. 여기서는 주요 플랫폼별 최적화 전략을 알아보자!
💻 PC 플랫폼 최적화
- 키 리매핑 지원: PC 게이머들은 자신만의 키 설정을 선호해
- 마우스 감도 설정: 다양한 DPI 설정을 지원하는 것이 좋아
- 게임패드 옵션: PC에서도 게임패드 지원은 필수!
// 키 리매핑 UI 예시 코드
public void RemapKey(string actionName, InputActionMap actionMap)
{
StartCoroutine(WaitForKeyPress(actionName, actionMap));
}
private IEnumerator WaitForKeyPress(string actionName, InputActionMap actionMap)
{
// 현재 맵 비활성화
actionMap.Disable();
// 사용자에게 키 입력 요청 UI 표시
remappingUI.SetActive(true);
// 키 입력 대기
bool keyPressed = false;
InputControl pressedKey = null;
var rebindOperation = actionMap.FindAction(actionName).PerformInteractiveRebinding()
.WithControlsExcluding("Mouse")
.OnMatchWaitForAnother(0.1f)
.OnComplete(operation => {
pressedKey = operation.selectedControl;
keyPressed = true;
operation.Dispose();
})
.Start();
while (!keyPressed)
{
yield return null;
}
// UI 업데이트
remappingUI.SetActive(false);
UpdateKeyBindingText(actionName, pressedKey.displayName);
// 설정 저장
SaveKeyBindings();
// 맵 다시 활성화
actionMap.Enable();
}
📱 모바일 플랫폼 최적화
- 터치 영역 크기: 손가락 크기를 고려한 충분한 터치 영역 제공
- 가상 조이스틱: 이동 컨트롤을 위한 최적화된 가상 조이스틱
- 제스처 인식: 스와이프, 핀치 등 다양한 제스처 지원
- 화면 방향 대응: 세로/가로 모드 전환 시 입력 처리 최적화
💡 모바일 팁: 가상 조이스틱은 처음 터치한 위치를 중심으로 생성하는 것이 좋아. 이렇게 하면 사용자가 화면의 어디를 터치하든 편하게 조작할 수 있어!
🎮 콘솔 플랫폼 최적화
- 컨트롤러 특화 기능: 진동, 모션 컨트롤, 터치패드 등 활용
- UI 네비게이션: 컨트롤러로 UI를 쉽게 탐색할 수 있도록 설계
- 플랫폼별 버튼 프롬프트: Xbox/PlayStation/Switch 버튼 아이콘 자동 전환
🔄 플랫폼 간 일관된 경험 제공하기
각 플랫폼에 최적화하면서도 게임 경험은 일관되게 유지하는 것이 중요해. 이를 위한 몇 가지 전략을 소개할게:
- 입력 추상화: 디바이스 특정 코드를 피하고 액션 기반으로 설계
- 자동 감지 및 전환: 사용 중인 디바이스를 감지하고 UI 자동 업데이트
- 플랫폼별 기본값 설정: 각 플랫폼에 맞는 기본 설정 제공
- 공통 액션 맵 사용: 모든 플랫폼에서 동일한 액션 맵 구조 유지
💻 플랫폼 감지 및 설정 최적화 예시
using UnityEngine;
using UnityEngine.InputSystem;
public class PlatformOptimizer : MonoBehaviour
{
[SerializeField] private float pcMouseSensitivity = 1.0f;
[SerializeField] private float consoleLookSensitivity = 0.5f;
[SerializeField] private float mobileSwipeSensitivity = 0.3f;
[SerializeField] private GameObject pcUI;
[SerializeField] private GameObject consoleUI;
[SerializeField] private GameObject mobileUI;
private void Start()
{
// 현재 플랫폼 감지 및 최적화
OptimizeForCurrentPlatform();
// 디바이스 변경 감지
InputSystem.onDeviceChange += (device, change) => {
if (change == InputDeviceChange.Added || change == InputDeviceChange.Removed)
{
UpdateUIForCurrentDevice();
}
};
}
private void OptimizeForCurrentPlatform()
{
// 플랫폼별 설정 최적화
#if UNITY_STANDALONE
// PC 설정
SetSensitivity(pcMouseSensitivity);
ShowUI(pcUI);
#elif UNITY_IOS || UNITY_ANDROID
// 모바일 설정
SetSensitivity(mobileSwipeSensitivity);
ShowUI(mobileUI);
#elif UNITY_PS4 || UNITY_PS5 || UNITY_XBOX_ONE || UNITY_SWITCH
// 콘솔 설정
SetSensitivity(consoleLookSensitivity);
ShowUI(consoleUI);
#endif
}
private void UpdateUIForCurrentDevice()
{
var lastDevice = InputSystem.GetDevice<InputDevice>();
if (lastDevice is Keyboard || lastDevice is Mouse)
{
ShowUI(pcUI);
}
else if (lastDevice is Gamepad)
{
ShowUI(consoleUI);
}
else if (lastDevice is Touchscreen)
{
ShowUI(mobileUI);
}
}
private void SetSensitivity(float value)
{
// 감도 설정 적용
PlayerPrefs.SetFloat("LookSensitivity", value);
}
private void ShowUI(GameObject uiToShow)
{
pcUI.SetActive(uiToShow == pcUI);
consoleUI.SetActive(uiToShow == consoleUI);
mobileUI.SetActive(uiToShow == mobileUI);
}
}
🔥 프로 팁: 플랫폼별 설정을 ScriptableObject로 관리하면 에디터에서 쉽게 조정하고 테스트할 수 있어! 이렇게 하면 코드 변경 없이도 각 플랫폼의 설정을 최적화할 수 있지.
6. 실전 예제: 크로스 플랫폼 캐릭터 컨트롤러 만들기 🏃♂️
이제 배운 내용을 바탕으로 실제로 작동하는 크로스 플랫폼 캐릭터 컨트롤러를 만들어보자! 이 컨트롤러는 PC, 모바일, 콘솔 등 모든 플랫폼에서 잘 작동할 거야.
🛠️ 1단계: Input Action Asset 설정
먼저 캐릭터 컨트롤을 위한 Input Action Asset을 만들자:
- 프로젝트 창에서 우클릭 >
Create > Input Actions선택 - 생성된 에셋의 이름을 "PlayerControls"로 지정
- 에셋을 더블클릭해서 Input Action 에디터 열기
- "Player" Action Map 추가
- 다음 액션들 추가:
- "Move" (Vector2) - 캐릭터 이동
- "Look" (Vector2) - 카메라 회전
- "Jump" (Button) - 점프
- "Sprint" (Button) - 달리기
- "Interact" (Button) - 상호작용
- 각 액션에 키보드/마우스, 게임패드, 터치 입력 바인딩 추가
- 에셋 인스펙터에서 "Generate C# Class" 체크하고 이름을 "PlayerControls"로 설정
🛠️ 2단계: 캐릭터 컨트롤러 스크립트 작성
이제 생성된 Input Action Asset을 사용하는 캐릭터 컨트롤러 스크립트를 작성해보자:
using UnityEngine;
using UnityEngine.InputSystem;
[RequireComponent(typeof(CharacterController))]
public class CrossPlatformController : MonoBehaviour
{
// 참조
private CharacterController controller;
private PlayerControls playerControls;
private Camera playerCamera;
// 이동 관련 변수
private Vector2 moveInput;
private Vector2 lookInput;
private bool jumpInput;
private bool sprintInput;
// 설정
[Header("Movement Settings")]
[SerializeField] private float walkSpeed = 5f;
[SerializeField] private float sprintSpeed = 8f;
[SerializeField] private float jumpForce = 5f;
[SerializeField] private float gravity = -9.81f;
[Header("Look Settings")]
[SerializeField] private float lookSensitivity = 1f;
[SerializeField] private float lookXLimit = 80f;
// 상태 변수
private Vector3 moveDirection;
private Vector3 verticalVelocity;
private float rotationX = 0;
private bool isGrounded;
private void Awake()
{
// 컴포넌트 참조 가져오기
controller = GetComponent<CharacterController>();
playerCamera = Camera.main;
// 입력 액션 초기화
playerControls = new PlayerControls();
// 입력 콜백 등록
playerControls.Player.Move.performed += ctx => moveInput = ctx.ReadValue<Vector2>();
playerControls.Player.Move.canceled += ctx => moveInput = Vector2.zero;
playerControls.Player.Look.performed += ctx => lookInput = ctx.ReadValue<Vector2>();
playerControls.Player.Look.canceled += ctx => lookInput = Vector2.zero;
playerControls.Player.Jump.performed += ctx => jumpInput = true;
playerControls.Player.Jump.canceled += ctx => jumpInput = false;
playerControls.Player.Sprint.performed += ctx => sprintInput = true;
playerControls.Player.Sprint.canceled += ctx => sprintInput = false;
// 마우스 커서 잠금 (PC)
#if UNITY_STANDALONE
Cursor.lockState = CursorLockMode.Locked;
Cursor.visible = false;
#endif
}
private void OnEnable()
{
playerControls.Enable();
}
private void OnDisable()
{
playerControls.Disable();
}
private void Update()
{
// 접지 상태 확인
isGrounded = controller.isGrounded;
// 이동 처리
HandleMovement();
// 카메라 회전 처리
HandleLook();
// 점프 처리
HandleJump();
}
private void HandleMovement()
{
if (isGrounded && verticalVelocity.y < 0)
{
verticalVelocity.y = -2f; // 약간의 중력 적용
}
// 현재 속도 결정 (달리기 여부에 따라)
float currentSpeed = sprintInput ? sprintSpeed : walkSpeed;
// 이동 방향 계산 (카메라 기준)
Vector3 forward = playerCamera.transform.forward;
Vector3 right = playerCamera.transform.right;
forward.y = 0;
right.y = 0;
forward.Normalize();
right.Normalize();
moveDirection = (forward * moveInput.y + right * moveInput.x).normalized;
// 캐릭터 이동
controller.Move(moveDirection * currentSpeed * Time.deltaTime);
// 중력 적용
verticalVelocity.y += gravity * Time.deltaTime;
controller.Move(verticalVelocity * Time.deltaTime);
}
private void HandleLook()
{
// 플랫폼에 따라 감도 조정
#if UNITY_STANDALONE
float sensitivityMultiplier = 1.0f;
#elif UNITY_IOS || UNITY_ANDROID
float sensitivityMultiplier = 0.5f;
#else
float sensitivityMultiplier = 0.7f;
#endif
// 좌우 회전 (캐릭터 전체)
transform.Rotate(0, lookInput.x * lookSensitivity * sensitivityMultiplier, 0);
// 상하 회전 (카메라만)
rotationX -= lookInput.y * lookSensitivity * sensitivityMultiplier;
rotationX = Mathf.Clamp(rotationX, -lookXLimit, lookXLimit);
playerCamera.transform.localRotation = Quaternion.Euler(rotationX, 0, 0);
}
private void HandleJump()
{
if (jumpInput && isGrounded)
{
verticalVelocity.y = Mathf.Sqrt(jumpForce * -2f * gravity);
jumpInput = false;
}
}
}
🛠️ 3단계: 모바일 터치 컨트롤 추가
모바일 플랫폼을 위한 가상 조이스틱을 추가해보자:
using UnityEngine;
using UnityEngine.InputSystem;
using UnityEngine.InputSystem.OnScreen;
using UnityEngine.UI;
public class MobileControls : MonoBehaviour
{
[SerializeField] private GameObject touchControlsCanvas;
[SerializeField] private OnScreenStick moveStick;
[SerializeField] private OnScreenStick lookStick;
[SerializeField] private Button jumpButton;
[SerializeField] private Button sprintButton;
private PlayerControls playerControls;
private PlayerInput playerInput;
private void Awake()
{
playerInput = GetComponent<PlayerInput>();
playerControls = new PlayerControls();
// 플랫폼 감지 및 컨트롤 활성화
#if UNITY_IOS || UNITY_ANDROID
touchControlsCanvas.SetActive(true);
SetupTouchControls();
#else
touchControlsCanvas.SetActive(false);
#endif
}
private void SetupTouchControls()
{
// 이동 스틱 설정
moveStick.controlPath = "<Gamepad>/leftStick";
// 시점 스틱 설정
lookStick.controlPath = "<Gamepad>/rightStick";
// 점프 버튼 설정
jumpButton.onClick.AddListener(() => {
playerControls.Player.Jump.performed?.Invoke(new InputAction.CallbackContext());
Invoke("ReleaseJump", 0.1f);
});
// 달리기 버튼 설정
sprintButton.onClick.AddListener(() => {
if (sprintButton.GetComponent<ButtonHoldDetector>().isHolding)
{
playerControls.Player.Sprint.performed?.Invoke(new InputAction.CallbackContext());
}
else
{
playerControls.Player.Sprint.canceled?.Invoke(new InputAction.CallbackContext());
}
});
}
private void ReleaseJump()
{
playerControls.Player.Jump.canceled?.Invoke(new InputAction.CallbackContext());
}
private void OnEnable()
{
playerControls.Enable();
}
private void OnDisable()
{
playerControls.Disable();
}
}
// 버튼 홀드 감지 헬퍼 클래스
public class ButtonHoldDetector : MonoBehaviour
{
public bool isHolding { get; private set; }
public void OnPointerDown()
{
isHolding = true;
}
public void OnPointerUp()
{
isHolding = false;
}
}
🔥 프로 팁: PlayerInput 컴포넌트를 사용하면 위 코드를 더 간단하게 만들 수 있어! 이 컴포넌트는 Input Action Asset을 직접 참조하고, 이벤트를 자동으로 처리해 줘.
🛠️ 4단계: 디바이스 감지 및 UI 업데이트
사용 중인 디바이스에 따라 UI 프롬프트를 업데이트하는 스크립트를 추가하자:
using UnityEngine;
using UnityEngine.InputSystem;
using UnityEngine.UI;
public class ControlPromptManager : MonoBehaviour
{
[System.Serializable]
public class PromptSet
{
public string actionName;
public Sprite keyboardPrompt;
public Sprite xboxPrompt;
public Sprite playstationPrompt;
public Sprite switchPrompt;
public Sprite touchPrompt;
public Image targetImage;
}
[SerializeField] private PromptSet[] prompts;
private void Start()
{
// 초기 프롬프트 설정
UpdatePrompts();
// 디바이스 변경 감지
InputSystem.onDeviceChange += (device, change) => {
if (change == InputDeviceChange.Added || change == InputDeviceChange.Removed)
{
UpdatePrompts();
}
};
}
private void UpdatePrompts()
{
var lastDevice = InputSystem.GetDevice<InputDevice>();
foreach (var prompt in prompts)
{
if (lastDevice is Keyboard || lastDevice is Mouse)
{
prompt.targetImage.sprite = prompt.keyboardPrompt;
}
else if (lastDevice is Gamepad gamepad)
{
// 게임패드 제조사 확인
if (gamepad.displayName.Contains("Xbox") || gamepad.displayName.Contains("XInput"))
{
prompt.targetImage.sprite = prompt.xboxPrompt;
}
else if (gamepad.displayName.Contains("PlayStation") || gamepad.displayName.Contains("PS"))
{
prompt.targetImage.sprite = prompt.playstationPrompt;
}
else if (gamepad.displayName.Contains("Switch") || gamepad.displayName.Contains("Nintendo"))
{
prompt.targetImage.sprite = prompt.switchPrompt;
}
else
{
// 기본값
prompt.targetImage.sprite = prompt.xboxPrompt;
}
}
else if (lastDevice is Touchscreen)
{
prompt.targetImage.sprite = prompt.touchPrompt;
}
}
}
private void OnDestroy()
{
InputSystem.onDeviceChange -= (device, change) => { };
}
}
이렇게 하면 모든 플랫폼에서 잘 작동하는 캐릭터 컨트롤러가 완성돼! 이 컨트롤러는 PC에서는 키보드와 마우스, 콘솔에서는 게임패드, 모바일에서는 터치 컨트롤을 자동으로 지원하게 될 거야. 재능넷에서도 이런 크로스 플랫폼 개발 노하우가 많이 공유되고 있으니 참고해봐! 😊
7. 입력 시스템 디버깅 및 문제 해결 🔍
입력 시스템을 구현하다 보면 여러 가지 문제가 발생할 수 있어. 특히 여러 플랫폼을 지원하는 경우에는 더욱 그렇지. 여기서는 효과적인 디버깅 방법과 자주 발생하는 문제들의 해결책을 알아보자!
🛠️ 디버깅 도구
- Input Debugger 창:
Window > Analysis > Input Debugger에서 실시간으로 입력 상태 확인 가능 - 입력 이벤트 로깅: 콜백 함수에 로그를 추가해 입력 이벤트 추적
- 시각적 피드백: 입력에 따른 시각적 피드백을 UI에 표시
- InputSystem.GetDevice(): 현재 활성화된 디바이스 확인
💻 입력 디버깅 헬퍼 클래스
다음은 입력 시스템 디버깅에 유용한 헬퍼 클래스야:
using UnityEngine;
using UnityEngine.InputSystem;
using System.Text;
using System.Collections.Generic;
public class InputDebugger : MonoBehaviour
{
[SerializeField] private InputActionAsset inputActions;
[SerializeField] private bool logToConsole = true;
[SerializeField] private TMPro.TextMeshProUGUI debugText;
private Dictionary<string, string> actionStates = new Dictionary<string, string>();
private void Start()
{
// 모든 액션 맵과 액션에 디버그 콜백 추가
foreach (var actionMap in inputActions.actionMaps)
{
foreach (var action in actionMap.actions)
{
// 초기 상태 기록
actionStates[action.name] = "Waiting...";
// 콜백 등록
action.performed += ctx => OnActionEvent(action.name, "Performed", ctx);
action.started += ctx => OnActionEvent(action.name, "Started", ctx);
action.canceled += ctx => OnActionEvent(action.name, "Canceled", ctx);
}
// 액션 맵 활성화
actionMap.Enable();
}
}
private void OnActionEvent(string actionName, string phase, InputAction.CallbackContext context)
{
// 값 읽기 시도
string valueStr = "N/A";
try
{
if (context.valueType == typeof(Vector2))
{
Vector2 value = context.ReadValue<Vector2>();
valueStr = $"({value.x:F2}, {value.y:F2})";
}
else if (context.valueType == typeof(float))
{
float value = context.ReadValue<float>();
valueStr = value.ToString("F2");
}
else if (context.valueType == typeof(bool) || context.valueType == typeof(void))
{
valueStr = phase;
}
}
catch
{
valueStr = "Error reading value";
}
// 상태 업데이트
actionStates[actionName] = $"{phase}: {valueStr}";
// 콘솔에 로그
if (logToConsole)
{
Debug.Log($"[Input] {actionName} - {phase} - Value: {valueStr} - Device: {context.control?.device.name}");
}
// UI 업데이트
UpdateDebugText();
}
private void UpdateDebugText()
{
if (debugText != null)
{
StringBuilder sb = new StringBuilder();
sb.AppendLine("INPUT DEBUG:");
sb.AppendLine("------------");
// 현재 활성 디바이스
var currentDevice = InputSystem.GetDevice<InputDevice>();
sb.AppendLine($"Active Device: {currentDevice?.displayName ?? "None"}");
sb.AppendLine();
// 모든 액션 상태
foreach (var entry in actionStates)
{
sb.AppendLine($"{entry.Key}: {entry.Value}");
}
debugText.text = sb.ToString();
}
}
private void Update()
{
// 매 프레임 디버그 텍스트 업데이트 (디바이스 변경 감지를 위해)
UpdateDebugText();
}
private void OnDestroy()
{
// 콜백 해제
foreach (var actionMap in inputActions.actionMaps)
{
foreach (var action in actionMap.actions)
{
action.performed -= ctx => { };
action.started -= ctx => { };
action.canceled -= ctx => { };
}
}
}
}
🚫 자주 발생하는 문제와 해결책
-
입력이 전혀 작동하지 않음
해결책:
- Action Map이 활성화되었는지 확인 (
actionMap.Enable()) - 올바른 Action Map을 참조하고 있는지 확인
- Input Action Asset이 프로젝트에 제대로 포함되었는지 확인
- Action Map이 활성화되었는지 확인 (
-
특정 디바이스에서만 입력이 작동하지 않음
해결책:
- 해당 디바이스에 대한 바인딩이 올바르게 설정되었는지 확인
- 디바이스가 Unity에서 인식되는지 Input Debugger로 확인
- 플랫폼별 입력 설정 확인 (특히 모바일)
-
입력 지연 문제
해결책:
- Update Mode 설정 확인 (Project Settings > Input System Package)
- 프레임 드롭이 발생하는지 확인 (성능 문제일 수 있음)
- 입력 처리 코드가 무거운 작업을 수행하지 않는지 확인
-
여러 디바이스 간 전환 시 문제
해결책:
- InputSystem.onDeviceChange 이벤트를 사용해 디바이스 변경 감지
- 디바이스 변경 시 UI 및 컨트롤 설정 업데이트
- 여러 디바이스의 동시 입력 처리 로직 확인
-
Action 값이 예상과 다름
해결책:
- Processor 설정 확인 (데드존, 노멀라이제이션 등)
- 올바른 타입으로 값을 읽고 있는지 확인 (
ReadValue<T>()) - Composite 바인딩 설정이 올바른지 확인
🔥 프로 팁: 디버그 빌드에 입력 시각화 도구를 포함시키는 것이 좋아! 이렇게 하면 QA 테스터나 베타 테스터들이 문제를 발견했을 때 정확한 입력 상태를 보고할 수 있어. 개발 빌드에만 포함되도록 전처리기 지시문을 사용하면 돼!
#if DEVELOPMENT_BUILD || UNITY_EDITOR
// 디버그 도구 활성화
inputDebugger.gameObject.SetActive(true);
#else
// 릴리스 빌드에서는 비활성화
inputDebugger.gameObject.SetActive(false);
#endif
8. 성능 최적화 팁과 트릭 ⚡
입력 시스템은 게임의 반응성에 직접적인 영향을 미치기 때문에 성능 최적화가 매우 중요해. 특히 모바일 기기나 저사양 PC에서는 더욱 그렇지. 여기서는 Unity 입력 시스템의 성능을 최적화하는 방법을 알아보자!
⚡ 일반적인 최적화 팁
- 불필요한 액션 비활성화: 현재 사용하지 않는 Action Map은 비활성화하여 처리 오버헤드 줄이기
- 이벤트 기반 접근 방식 사용: 매 프레임 폴링보다 이벤트 콜백 사용하기
- 입력 처리 빈도 조절: Update Mode 설정으로 입력 처리 빈도 최적화
- 가비지 생성 최소화: 콜백에서 불필요한 객체 생성 피하기
- 복잡한 조합 입력 제한: 저사양 기기에서는 복잡한 조합 입력 제한하기
💻 성능 최적화 코드 예시
다음은 입력 시스템 성능을 최적화하는 몇 가지 코드 예시야:
using UnityEngine;
using UnityEngine.InputSystem;
public class OptimizedInputManager : MonoBehaviour
{
[SerializeField] private InputActionAsset inputActions;
// 자주 사용하는 액션 맵 캐싱
private InputActionMap gameplayMap;
private InputActionMap menuMap;
// 자주 사용하는 액션 캐싱
private InputAction moveAction;
private InputAction lookAction;
// 재사용 가능한 벡터 (가비지 생성 방지)
private Vector2 moveInput;
private Vector2 lookInput;
private void Awake()
{
// 액션 맵 캐싱
gameplayMap = inputActions.FindActionMap("Gameplay");
menuMap = inputActions.FindActionMap("Menu");
// 자주 사용하는 액션 캐싱
moveAction = gameplayMap.FindAction("Move");
lookAction = gameplayMap.FindAction("Look");
// 이벤트 기반 콜백 등록 (매 프레임 폴링 대신)
moveAction.performed += OnMovePerformed;
moveAction.canceled += OnMoveCanceled;
lookAction.performed += OnLookPerformed;
lookAction.canceled += OnLookCanceled;
}
// 게임 상태에 따라 필요한 액션 맵만 활성화
public void EnterGameplay()
{
menuMap.Disable(); // 사용하지 않는 맵 비활성화
gameplayMap.Enable();
}
public void EnterMenu()
{
gameplayMap.Disable(); // 사용하지 않는 맵 비활성화
menuMap.Enable();
}
// 이벤트 기반 콜백 (Update에서 매 프레임 폴링하지 않음)
private void OnMovePerformed(InputAction.CallbackContext context)
{
// 재사용 가능한 벡터에 값 저장 (new Vector2 사용하지 않음)
moveInput = context.ReadValue<Vector2>();
// 이동 로직 처리
HandleMovement(moveInput);
}
private void OnMoveCanceled(InputAction.CallbackContext context)
{
moveInput = Vector2.zero;
HandleMovement(moveInput);
}
private void OnLookPerformed(InputAction.CallbackContext context)
{
lookInput = context.ReadValue<Vector2>();
HandleLook(lookInput);
}
private void OnLookCanceled(InputAction.CallbackContext context)
{
lookInput = Vector2.zero;
HandleLook(lookInput);
}
// 실제 이동 처리 로직
private void HandleMovement(Vector2 input)
{
// 이동 로직 구현
}
// 실제 시점 처리 로직
private void HandleLook(Vector2 input)
{
// 시점 로직 구현
}
private void OnDestroy()
{
// 콜백 해제
if (moveAction != null)
{
moveAction.performed -= OnMovePerformed;
moveAction.canceled -= OnMoveCanceled;
}
if (lookAction != null)
{
lookAction.performed -= OnLookPerformed;
lookAction.canceled -= OnLookCanceled;
}
}
}
📱 플랫폼별 성능 최적화
모바일 최적화
- 터치 입력 샘플링 레이트 조절: 모든 터치 이벤트를 처리하지 않고 필요한 만큼만 샘플링
- 가속도계/자이로스코프 사용 제한: 필요할 때만 활성화
- 터치 영역 최적화: 복잡한 제스처 인식보다 간단한 터치 영역 사용
// 모바일 터치 입력 최적화 예시
#if UNITY_IOS || UNITY_ANDROID
// 터치 샘플링 레이트 조절
[SerializeField] private float touchSampleRate = 0.05f; // 50ms마다 샘플링
private float lastTouchSampleTime;
private void Update()
{
// 일정 간격으로만 터치 처리
if (Time.time - lastTouchSampleTime >= touchSampleRate)
{
ProcessTouchInput();
lastTouchSampleTime = Time.time;
}
}
private void ProcessTouchInput()
{
// 터치 입력 처리 로직
}
#endif
콘솔/PC 최적화
- 입력 버퍼링: 빠른 연속 입력을 버퍼링하여 처리
- 프레임 독립적 입력: 프레임 레이트에 관계없이 일관된 입력 처리
- 하드웨어 특화 기능 최적화: 특정 컨트롤러의 고유 기능 최적화
🔍 고급 최적화 팁
-
프로파일링 활용: Unity Profiler를 사용해 입력 시스템의 성능 병목 지점 찾기
입력 처리 코드에
Profiler.BeginSample("Input Processing")와Profiler.EndSample()을 사용해 정확한 성능 측정 -
입력 우선순위 설정: 중요한 입력은 높은 우선순위로 처리
예: 일시정지 버튼은 항상 최우선으로 처리, 덜 중요한 입력은 낮은 빈도로 처리
-
멀티스레딩 고려: 복잡한 입력 처리는 별도 스레드에서 처리
주의: Input System의 대부분 API는 메인 스레드에서만 안전하게 호출 가능
-
메모리 풀링: 입력 이벤트 처리에 필요한 객체를 미리 풀링하여 가비지 컬렉션 최소화
특히 모바일에서 중요한 최적화 기법
🔥 프로 팁: 입력 시스템의 Update Mode를 "Process Events in Dynamic Update"로 설정하면 대부분의 경우 최상의 반응성과 성능 밸런스를 얻을 수 있어! 특히 60fps 이상을 목표로 하는 게임에 적합해. 재능넷에서도 많은 개발자들이 이 설정을 추천하고 있어!
9. 마무리 및 추가 자료 📚
지금까지 Unity의 새로운 입력 시스템 패키지를 사용해 크로스 플랫폼 입력을 관리하는 방법에 대해 알아봤어! 이 강력한 시스템을 마스터하면 어떤 플랫폼에서든 일관된 게임 경험을 제공할 수 있을 거야. 😊
📝 요약
- Unity의 새 입력 시스템은 다양한 디바이스와 플랫폼을 쉽게 지원할 수 있게 해줘
- Action-based 접근 방식으로 입력 로직과 디바이스 바인딩을 분리할 수 있어
- Input Action Asset을 통해 에디터에서 시각적으로 입력을 구성할 수 있지
- 플랫폼별 최적화를 통해 각 플랫폼에서 최상의 경험을 제공할 수 있어
- 디버깅 도구와 성능 최적화 기법으로 안정적인 입력 시스템을 구축할 수 있지
새로운 입력 시스템은 처음에는 약간 복잡해 보일 수 있지만, 익숙해지면 기존 시스템보다 훨씬 강력하고 유연하다는 것을 알게 될 거야. 특히 여러 플랫폼을 지원해야 하는 게임 개발자에게는 정말 큰 도움이 돼!
📚 추가 학습 자료
- 공식 문서: Unity 입력 시스템 패키지 공식 문서
- 튜토리얼: Unity Learn 플랫폼의 입력 시스템 튜토리얼
- 샘플 프로젝트: GitHub에서 제공하는 입력 시스템 샘플
- 포럼: Unity 포럼의 입력 시스템 섹션
- 재능넷: 재능넷에서 Unity 입력 시스템 관련 노하우와 팁을 공유하는 전문가들의 글
Unity의 새 입력 시스템은 계속 발전하고 있어. 앞으로는 더 많은 디바이스와 입력 방식을 지원하게 될 거야. VR/AR 컨트롤러, 모션 캡처, 음성 인식, 심지어 뇌-컴퓨터 인터페이스까지! 게임 개발의 미래는 더욱 다양한 입력 방식을 통합하는 방향으로 나아가고 있어.
이 글이 Unity 입력 시스템을 이해하고 활용하는 데 도움이 됐으면 좋겠어. 만약 더 깊이 있는 내용이나 특정 주제에 대한 질문이 있다면, 재능넷의 개발자 커뮤니티에서 도움을 받을 수 있을 거야. 많은 전문가들이 자신의 경험과 노하우를 공유하고 있으니까! 🚀
🎮 직접 시도해보세요!
이제 배운 내용을 바탕으로 직접 크로스 플랫폼 게임을 개발해보세요! Unity의 새 입력 시스템을 활용하면 PC, 콘솔, 모바일, VR 등 모든 플랫폼에서 최적의 게임 경험을 제공할 수 있습니다.
더 많은 게임 개발 팁과 노하우는 재능넷에서 찾아보세요!
관련 키워드
댓글 작성
이 글에 대한 여러분의 생각을 들려주세요
로그인이 필요합니다
댓글을 작성하려면 먼저 로그인해주세요.
댓글 0
지식인의 숲 - 지적 재산권 보호 고지
지적 재산권 보호 고지
- 저작권 및 소유권: 본 컨텐츠는 재능넷의 독점 AI 기술로 생성되었으며, 대한민국 저작권법 및 국제 저작권 협약에 의해 보호됩니다.
- AI 생성 컨텐츠의 법적 지위: 본 AI 생성 컨텐츠는 재능넷의 지적 창작물로 인정되며, 관련 법규에 따라 저작권 보호를 받습니다.
- 사용 제한: 재능넷의 명시적 서면 동의 없이 본 컨텐츠를 복제, 수정, 배포, 또는 상업적으로 활용하는 행위는 엄격히 금지됩니다.
- 데이터 수집 금지: 본 컨텐츠에 대한 무단 스크래핑, 크롤링, 및 자동화된 데이터 수집은 법적 제재의 대상이 됩니다.
- AI 학습 제한: 재능넷의 AI 생성 컨텐츠를 타 AI 모델 학습에 무단 사용하는 행위는 금지되며, 이는 지적 재산권 침해로 간주됩니다.
아직 댓글이 없습니다.