Input System 씬 전환 후 입력 먹통 문제 해결기 (ft. GameManager)

순한양·2025년 7월 1일
0

[Unity] Input System 씬 전환 후 입력 먹통 문제 해결기 (ft. GameManager)

Unity Logo
C# Logo

🤔 문제 현상: 씬만 넘어가면 키가 안 먹혀요!

팀 프로젝트를 진행하던 중, 이상한 버그를 만났습니다.
분명히 IntroScene에서는 잘 되던 키 변경(리매핑)과 Esc 키를 이용한 일시정지 기능이, GameScene으로만 넘어가면 먹통이 되는 현상이었습니다.

더 답답한 것은, 콘솔 창에 특별한 오류 메시지가 뜨지 않아 원인을 짐작하기 어려웠다는 점입니다. Debug.Log를 통해 확인해보니, InputReader는 입력을 감지하고 있었고 GameManager도 상태 변경 신호를 잘 받고 있었습니다. 모든 것이 정상적으로 흐르는 것처럼 보였지만, 결과적으로는 아무 일도 일어나지 않았습니다.

💥 원인 분석: "두 개의 다른 조종기"

끈질긴 디버깅 끝에, 문제의 원인은 우리가 Unity의 새로운 Input System을 잘못된 방식으로 사용하고 있었다는 것을 깨달았습니다.

비유하자면, GameManager는 '마스터 조종기'를 들고 "이제부터 점프는 X 버튼이야!" 라고 열심히 설정을 바꾸고 있었습니다. 하지만, 정작 비행기(플레이어)는 자기가 따로 챙겨온 '개인용 조종기'를 보고 조종하고 있었습니다. '마스터 조종기'의 설정을 아무리 바꿔도, 플레이어는 자기 조종기만 보기 때문에 설정이 전혀 적용되지 않는 것이었죠.

기술적으로 말해, GameManagerInputReader가 각각 new Controls()를 통해 별개의 컨트롤러 인스턴스를 생성하고 있었던 것이 문제입니다.

  • GameManagerPlayerPrefs에서 불러온 키 변경사항을 자기가 가진 'A' 인스턴스에 적용했습니다.
  • InputReader는 사용자의 실제 키 입력을 자기가 가진 'B' 인스턴스를 통해 감지하고 있었습니다.

A 인스턴스와 B 인스턴스는 서로 다른 메모리 공간에 있는 별개의 객체이므로, A의 변경사항이 B에 전혀 영향을 주지 못했던 것입니다.

✅ 해결책: "하나의 조종기"를 모두가 공유하는 중앙집권적 구조

해결책은 명확했습니다. 게임 전체에서 단 하나의 Controls 인스턴스만 존재하도록 보장하고, 이 인스턴스를 GameManager가 소유하고 관리하도록 구조를 변경하는 것입니다.

1. GameManager.cs 수정

GameManager가 유일한 Controls 인스턴스를 생성하고, 활성화하고, 키 설정을 불러오는 모든 책임을 집니다.

using UnityEngine;
using UnityEngine.InputSystem;

public class GameManager : MonoBehaviour
{
    [SerializeField] private InputActionAsset _inputActions;
    public Controls PlayerControls { get; private set; }
    
    public static GameManager Instance { get; private set; }

    private void Awake()
    {
        if (Instance == null)
        {
            Instance = this;
            DontDestroyOnLoad(gameObject);
            
            // 1. 유일한 컨트롤러 인스턴스를 생성합니다.
            PlayerControls = new Controls();
            // 2. 저장된 키 바인딩을 불러옵니다.
            LoadAllKeybindings();
        }
        else
        {
            Destroy(gameObject);
        }
    }

    private void OnEnable()
    {
        if (Instance != this) return;
        // 3. 컨트롤러를 활성화합니다.
        PlayerControls?.Enable();
    }

    private void OnDisable()
    {
        if (Instance != this) return;
        PlayerControls?.Disable();
    }
    
    private void LoadAllKeybindings()
    {
        string rebinds = PlayerPrefs.GetString("AllKeyRebinds", string.Empty);
        if (string.IsNullOrEmpty(rebinds)) return;
        PlayerControls.LoadBindingOverridesFromJson(rebinds);
    }
    // ...
}

2. InputReader.cs 수정

InputReader는 더 이상 Controls를 직접 생성하지 않고, GameManager가 만들어준 '마스터 조종기'를 받아와 사용만 하도록 변경합니다.

using System;
using UnityEngine;
using UnityEngine.InputSystem;

public class InputReader : MonoBehaviour, Controls.IPlayerActions
{
    // private Controls _controls; // 이 라인을 삭제!
    
    public event Action OnPausePerformed;
    // ... 다른 이벤트들 ...

    private void Start()
    {
        if (GameManager.Instance?.PlayerControls != null)
        {
            // GameManager의 공유 인스턴스에 자신의 함수들을 콜백으로 등록합니다.
            GameManager.Instance.PlayerControls.Player.SetCallbacks(this);
        }
    }

    // OnJump, OnPause 등 콜백 함수들은 그대로 유지...
    public void OnPause(InputAction.CallbackContext context)
    {
        if (context.performed) OnPausePerformed?.Invoke();
    }
}

🚀 결론 및 교훈

이 수정을 통해, 게임이 시작될 때 GameManager가 단 하나의 Controls 인스턴스를 만들고 모든 키 설정을 적용합니다. 그 후 모든 스크립트(InputReader, KeyRemappingManager 등)는 GameManager.Instance.PlayerControls를 통해 이 공유된 단일 인스턴스에만 접근합니다.

Unity의 새로운 Input System에서 씬을 넘나드는 데이터를 관리할 때는, 어떤 인스턴스가 '진짜(Single Source of Truth)'인지 명확히 하고, 모든 시스템이 그 단일 인스턴스를 참조하도록 만드는 것이 중요하다는 것을 배웠습니다. 이 구조 변경으로 모든 입력 관련 버그가 해결되었습니다.

profile
개발 입문자

0개의 댓글