콘텐츠로 이동

Unity Input System 런타임 키 리바인딩

Unity New Input System은 InputAction.PerformInteractiveRebinding()으로 플레이어가 원하는 키로 조작을 재매핑할 수 있습니다. 리바인딩 결과를 JSON으로 직렬화해 PlayerPrefs에 저장하면 세션 간 설정이 유지됩니다.


using UnityEngine;
using UnityEngine.InputSystem;
using TMPro;
public class RebindUI : MonoBehaviour
{
[SerializeField] InputActionAsset inputActions;
[SerializeField] TMP_Text bindingText;
[SerializeField] GameObject waitingOverlay;
private InputActionRebindingExtensions.RebindingOperation _rebindOp;
void Start()
{
// 현재 바인딩 텍스트 표시
var action = inputActions.FindAction("Player/Jump");
bindingText.text = action.GetBindingDisplayString();
}
public void StartRebind()
{
var action = inputActions.FindAction("Player/Jump");
action.Disable(); // 리바인딩 중 입력 비활성화
waitingOverlay.SetActive(true);
_rebindOp = action.PerformInteractiveRebinding()
.WithControlsExcluding("<Mouse>/position")
.WithControlsExcluding("<Mouse>/delta")
.OnMatchWaitForAnother(0.1f) // 중복 입력 방지 대기
.OnComplete(op => OnRebindComplete(action, op))
.OnCancel(op => OnRebindCancel(action, op))
.Start();
}
void OnRebindComplete(InputAction action,
InputActionRebindingExtensions.RebindingOperation op)
{
op.Dispose();
action.Enable();
waitingOverlay.SetActive(false);
bindingText.text = action.GetBindingDisplayString();
SaveBindings();
}
void OnRebindCancel(InputAction action,
InputActionRebindingExtensions.RebindingOperation op)
{
op.Dispose();
action.Enable();
waitingOverlay.SetActive(false);
}
void OnDestroy() => _rebindOp?.Dispose();
}

2. 특정 바인딩 인덱스 리바인딩

섹션 제목: “2. 특정 바인딩 인덱스 리바인딩”

액션에 키보드/게임패드 두 바인딩이 있을 때 각각 독립적으로 리바인딩합니다.

public void StartRebindAtIndex(string actionName, int bindingIndex)
{
var action = inputActions.FindAction(actionName);
action.Disable();
_rebindOp = action
.PerformInteractiveRebinding(bindingIndex)
.WithControlsExcluding("<Mouse>/position")
.OnComplete(op =>
{
op.Dispose();
action.Enable();
SaveBindings();
})
.Start();
}

const string BindingsSaveKey = "InputBindings";
void SaveBindings()
{
string json = inputActions.SaveBindingOverridesAsJson();
PlayerPrefs.SetString(BindingsSaveKey, json);
PlayerPrefs.Save();
}
void LoadBindings()
{
if (PlayerPrefs.HasKey(BindingsSaveKey))
{
string json = PlayerPrefs.GetString(BindingsSaveKey);
inputActions.LoadBindingOverridesFromJson(json);
}
}
public void ResetToDefaults()
{
inputActions.RemoveAllBindingOverrides();
PlayerPrefs.DeleteKey(BindingsSaveKey);
RefreshAllDisplayTexts();
}

4. 복합 바인딩(Composite) 리바인딩

섹션 제목: “4. 복합 바인딩(Composite) 리바인딩”

WASD 같은 2D Vector Composite의 각 방향을 개별 리바인딩합니다.

public void RebindCompositepart(string actionName, string partName)
{
var action = inputActions.FindAction(actionName);
// Composite 파트 인덱스 찾기
int bindingIndex = -1;
for (int i = 0; i < action.bindings.Count; i++)
{
if (action.bindings[i].isPartOfComposite &&
action.bindings[i].name == partName)
{
bindingIndex = i;
break;
}
}
if (bindingIndex >= 0)
StartRebindAtIndex(actionName, bindingIndex);
}
// 사용: "Move" 액션의 "up" 파트 리바인딩
RebindCompositepart("Player/Move", "up");

public void RefreshAllDisplayTexts()
{
foreach (var entry in bindingEntries)
{
var action = inputActions.FindAction(entry.ActionName);
entry.Text.text = InputControlPath.ToHumanReadableString(
action.bindings[entry.BindingIndex].effectivePath,
InputControlPath.HumanReadableStringOptions.OmitDevice);
}
}

6. 게임패드/키보드 전용 필터링

섹션 제목: “6. 게임패드/키보드 전용 필터링”
// 키보드만 허용
_rebindOp = action.PerformInteractiveRebinding()
.WithBindingGroup("Keyboard")
.WithControlsHavingToMatchPath("<Keyboard>")
.Start();
// 게임패드만 허용
_rebindOp = action.PerformInteractiveRebinding()
.WithControlsHavingToMatchPath("<Gamepad>")
.Start();

PerformInteractiveRebinding의 핵심은 OnComplete에서 Dispose를 호출하고 액션을 다시 Enable하는 것입니다. SaveBindingOverridesAsJson / LoadBindingOverridesFromJson으로 재매핑 상태를 직렬화하면 게임 재시작 후에도 설정이 유지됩니다. WithControlsExcluding으로 마우스 이동축 같은 의도치 않은 입력을 제외하는 것도 필수입니다.