부트캠프/본캠프

[내일배움캠프_2025JUL30] Input System 키 바인딩 변경 Setting

Young_A 2025. 7. 30. 21:54

목차

    Input System 키 바인딩 변경 Setting

    팀프로젝트의 2일차가 되었다.

     

    중간에 기획 사전 OT 같은 느낌으로 진행된 게 있어서 귀동냥하면서 작업했다.

    기획도 자료가 다 올라오긴 한다고 하니 한번씩 체크해보는 것도 나쁘지 않을 것 같다.


    InputManager

    Settings 메뉴에서 기본 키 설정 변경 가능한 기능은 필수 구현이 아니라 도전 과제이다.

    하지만 내가 맡은 파트는 다른 파트들이 어느정도 구현이 되어야 진행할 수 있는 부분들이 많아서 빈 시간동안 미리 진행했다.

     

    게임 플레이 로직들과는 관련이 없는 부분이라 Input Manager를 따로 만들고 싱글톤으로 진행했다.

    하지만 구현 방향에 따라, 싱글톤 여부는 변경할 수도 있을 것 같다.

     

    Input 키 바인딩 저장을 PlayerPrefs에서 할지, 따로 Data를 만들어야 할지 고민이 된다.

    일단 키 값이 세 개 밖에 없기도 하고, 필수 기능 구현을 마치지 않앗으므로 PlayerPrefs에 저장하는 것으로 진행했다.

     

    어제 배운 ScriptatbleObject를 사용해보려고, defaultBindingSO를 만들었었으나,

    현재와 같이 키보드 입력만을 기본으로 하고 있는 경우에는 필요하지 않을 것 같기도 하고...

    어차피 InputSystem에서 이미 바인딩 설정한 것과 같은 값을 defaultBindingSO에 저장하는 것임으로 의미가 없을 것 같아 도로 삭제했다.

    다만 플랫폼이 여러개라거나, 키보드 시스템, 컨트롤러 시스템 등등이 도입된다면? 활용할 수 있지 않을까 생각중이다.

     

    +현재 Debug.log를 통해서 구현하는 바람에 놓친 부분인데,

    추후 UI 기반으로 변경하려면 PlayerPrefs에 저장하거나, rebind.Start()와 같은 부분은, 저장하기 버튼을 클릭했을 때 진행하도록 변경해야할 것 같다.

    그를 위해서는 temp 변수를 만들고, 이를 통해 user에게 display해주는 방식으로 진행해야한다.

    InputActionAsset.LoadBindingOverridesFromJson()

    if (PlayerPrefs.HasKey(inputBindingKey))
    {
        inputActions.LoadBindingOverridesFromJson(PlayerPrefs.GetString(inputBindingKey));
    }

    리바인드를 저장되어 있는 JSON으로부터 복원한다.

     

    내 경우에는 PlayerPrefs json 형태로 저장하고 있으므로 이를 불러오면 간단하다.

    InputActionAsset.RemoveAllBindingOverrides()

    inputActions.RemoveAllBindingOverrides();
    PlayerPrefs.DeleteKey(inputBindingKey);

    해당 InputActionAsset에 오버라이드된 바인딩을 전부 제거한다.

     

    내 경우에는 PlayerPrefs에 저장되어 있는 key값도 지워줘야한다!


    InputAction.PerformInteractiveRebinding(int bindingIndex)

    참고로 리바인딩을 하는 동안에는 InputActionAsset을 Disable() 해주어야 에러가 뜨지 않는다.

    끝난 뒤 Enable()하는 것도 잊으면 안된다.

    InputAction targetAction = inputActions.FindAction(actionName);
    /*
    	...예외처리들 중략
    */
    targetAction.Disable(); //리바인딩 하는 동안 disable
    var rebind = targetAction.PerformInteractiveRebinding(bindingIndex).WithControlsExcluding("Mouse"); //마우스 제외한 입력장치만 받게 함. (디버그로그 테스트하는데 마우스 스크롤로 바뀌어서 변경함)
    
    Debug.Log($"새로운 키 바인딩을 시작합니다: {actionName}");
    Debug.Log("원하는 키를 입력해주세요.");
    
    rebind.OnComplete(
        operation =>
        {
            PlayerPrefs.SetString(inputBindingKey, inputActions.SaveBindingOverridesAsJson()); //PlayerPrefs에 변경사항 저장
            PlayerPrefs.Save();
            targetAction.Enable();
    
            Debug.Log($"Rebound '{targetAction}' to '{operation.selectedControl.displayName}'");    //변경 로그
            operation.Dispose();
        });
    rebind.Start(); //리바인딩 시작

    OnComplete(Action<InputActionRebindingExtensions.RebindingOperation>)

    리바인딩이 성공적으로 완료되면 호출되는 델리게이트다.

    이게 호출 될 쯤이면 리바인딩이 완전히 적용되고, OnApplyBinding이 실행된다.

    Start()

    리바인딩을 시작하므로 리바인딩 작업이 완전히 구성된 후에 호출되어야 한다.

    InputActionAsset.WithControlsExcluding("Mouse")

    마우스, 키보드 인풋시스템을 가리지 않고 받기 때문에, 디버그로그를 확인하기 위해 콘솔창에서 마우스 스크롤 하는 것까지 포함해서 입력을 받더라...

    그래서 마우스를 제외한 키 입력을 받도록 WithControlsExcluding("Mouse")를 추가해주었다.

     

    더보기

    중복 바인딩 처리

    이미 슬라이딩에 w를 넣은 상태에서 점프 키를 w로 바꾸려고 하면 점프까지 w키와 바인딩 되버리는 문제가 발생했다.

    이 경우를 방지하기 위해 중복 바인딩을 확인하는 IsBindingConflict라는 bool을 반환하는 메서드를 구현했다.

    bool IsBindingConflict(string actionName, string newBindingPath)
    {
        foreach (var action in inputActions)
        {
            if (action.name == actionName)
                continue;
            foreach (var binding in action.bindings)
            {
                if (binding.effectivePath == newBindingPath)
                    return true;    //이미 사용중임.
            }
        }
        return false;
    }

    InputBinding.effectivePath

    이미 바인딩 된 키들의 값을 string으로 받을 수 있다.

    다만 위와 같은 문제가 생겼다.

    effectivePath는 <Keyboard>/s 와 같이 출력된다.

    하지만 operation.selectedControl.path;로 넘기니 /Keyboard/s 가 출력되었다.

    따라서 둘은 ==로 비교했을 때 true가 나올 수 없다!

    if (!newBindingPath.StartsWith('<')) //만약 받은 path가 <로 시작하지 않으면 binding.effectivePath와 형식이 맞지 않음으로 포맷팅 새로함.
    {
        newBindingPath = newBindingPath.Replace("/Keyboard/", "<Keyboard>/");
    }

    위와 같이 간단히 string 값의 일부를 Replace 해준 뒤 중첩 foreach문으로 InputAction들과 각각의 Binding들을 비교했주었다.

     


    느낀점

    오늘 조금 집중을 못했다.

    때문에 유데미 강의도 못들었다!ㅠㅠ

     

    8시에 저녁 스크럼을 통해서 각자 한 부분을 공유하는 것은 좋은 선택 같다.

    마지막에 단체로 튜터님한테 가서 질문까지 하고 애매한 부분들을 확실하게 할 수 있었던 것 같다.


    내일 학습 할 것은 무엇인지

    내일 플레이어와 장애물 진행하신 분들의 협업이 끝난 뒤에 한 씬에 붙일 수 있을 것 같다.

    우선 완료되어있는 UI를 코드상으로 잇는 작업을 해야할 것 같고,

    내가 제작한 Input Setting Panel도 팀 네이밍 컨벤션에 맞게 UI<를 붙여서 작업해야할 것 같다.