Logo wizaman's blog (legacy)

InputManager向けボタン名とKeyCodeの相互変換

October 14, 2017
21 min read
Table of Contents

UnityのInputManagerで指定できるボタン名とKeyCodeの相互変換を試みます。

その前にいくらか前提を説明します。

Inputは設定が面倒

Unityでユーザの操作入力を取り扱うにはInputクラスを使います。

押しているかどうかboolで判断するButton系の取得と、どれぐらいの強さで入力されているか[-1, 1]範囲のfloatを返すAxis系があります。

ここで入力値を得るために指定する名前は、入力設定(InputManager)で設定した仮想軸の名前になります。デフォルトではHorizontal、Vertical、Fire1、Fire2、Fire3、Jump、Submit、Cancelなどが設定されているはずです。定義した仮想軸はButtonとしてもAxisとしても使用できます。良い感じに補間するための設定がInputManagerにあります。

任意の名前をつけた仮想軸を定義できるのは良いのですが、ゲーム中に設定を差し替えることができなかったり、割り当てるボタンを指定するフォーマットに統一性がなかったりと、そのまま使おうとするのは無理があると思います。どうしてこうなった。

Inputにラッパーかぶせるとかするにしても、InputManagerの設定は無視できないところです。しかし、**InputManagerをスクリプトから操作するAPIが公開されていません。**うわぁ、めんどくせぇ。

とはいえ、これを手で設定するのはあり得ないでしょう。

どうせYAMLで書き出せばテキスト処理でどうとでもなると思い、昔はYAMLを書き出すスクリプトを書いたこともありましたが、もっと良い方法がありました。

上の記事では、SerializedPropertyを通してInputManager.assetを編集する試みをしています。フォーマットがYAMLかどうかにも依存しないし、一番まともっぽいアプローチ。

ただ、割り当てるボタンを文字列で指定するところが面倒です。これ結局KeyCodeを指定することと同義なのですが、ドロップダウンリストで指定するわけでもなく、独自フォーマットの名前を直接入力しないといけません。

ボタン名の大まかなルールは上記ドキュメントにまとまっていますが、正しい名前を手で打ち込むのもバカらしいですし、そもそも正解がよくわかりません。

というわけで、前置きが長くなりましたが、KeyCodeとボタン名の相互変換を試みます。このボタン名は仮想軸の割り当てに使ってもらってもいいし、Input.GetKey()に直接指定しても使えます。後者の場合、普通にKeyCodeを指定する方が当然便利です。

InputManagerの自動生成については先に紹介した記事を参考にしてください。私はもう少し都合よく整理していますが、基礎は同じなので割愛。

実装

KeyCodeとして定義されたenumの名前変換テーブルを作ってやればOKです。

以前に書いたキャメルケースの分割を利用します(今回のために実装した副産物を別の記事にしただけ)。

public static class KeyCodeUtil
{
    private static readonly Dictionary<KeyCode, string> specifiedNames = new Dictionary<KeyCode, string> {
        { KeyCode.Exclaim, "!" },
        { KeyCode.DoubleQuote, "\"" },
        { KeyCode.Hash, "#" },
        { KeyCode.Dollar, "$" },
        { KeyCode.Ampersand, "&" },
        { KeyCode.Quote, "'" },
        { KeyCode.LeftParen, "(" },
        { KeyCode.RightParen, ")" },
        { KeyCode.Asterisk, "*" },
        { KeyCode.Plus, "+" },
        { KeyCode.Comma, "," },
        { KeyCode.Minus, "-" },
        { KeyCode.Period, "." },
        { KeyCode.Slash, "/" },
        { KeyCode.Colon, ":" },
        { KeyCode.Semicolon, ";" },
        { KeyCode.Less, "<" },
        { KeyCode.Equals, "=" },
        { KeyCode.Greater, ">" },
        { KeyCode.Question, "?" },
        { KeyCode.At, "@" },
        { KeyCode.LeftBracket, "[" },
        { KeyCode.Backslash, "\\" },
        { KeyCode.RightBracket, "]" },
        { KeyCode.Caret, "^" },
        { KeyCode.Underscore, "_" },
        { KeyCode.BackQuote, "`" },
        { KeyCode.KeypadPeriod, "[.]" },
        { KeyCode.KeypadDivide, "[/]" },
        { KeyCode.KeypadMultiply, "[*]" },
        { KeyCode.KeypadMinus, "[-]" },
        { KeyCode.KeypadPlus, "[+]" },
        { KeyCode.KeypadEnter, "enter" },
        { KeyCode.KeypadEquals, "equals" },
        { KeyCode.Print, "print screen" },
    };
 
    private static readonly HashSet<string> ignoredNames = new HashSet<string> {
        "LeftApple",
        "RightApple",
    };
 
    private static readonly HashSet<KeyCode> ignoredCodes = new HashSet<KeyCode> {
        KeyCode.None,
    };
 
    public static KeyCode toCode(string name)
    {
        KeyCode code;
        if(_str2code.TryGetValue(name, out code)) {
            return code;
        }
        return KeyCode.None;
    }
 
    public static string toName(this KeyCode code)
    {
        string name;
        if(_code2str.TryGetValue(code, out name)) {
            return name;
        }
        return "";
    }
 
    public static KeyCode mouse(int button)
    {
        if(button < 0 || button >= 7) return KeyCode.None;
 
        var code = KeyCode.Mouse0 + button;
        return code;
    }
 
    public static KeyCode joystick(int button)
    {
        if(button < 0 || button >= 20) return KeyCode.None;
 
        var code = KeyCode.JoystickButton0 + button;
        return code;
    }
 
    public static KeyCode joystick(int num, int button)
    {
        if(button < 0 || button >= 20) return KeyCode.None;
 
        var code = KeyCode.None;
        switch(num) {
        case 1:
            code = KeyCode.Joystick1Button0;
            break;
        case 2:
            code = KeyCode.Joystick2Button0;
            break;
        case 3:
            code = KeyCode.Joystick3Button0;
            break;
        case 4:
            code = KeyCode.Joystick4Button0;
            break;
        default:
            return KeyCode.None;
        }
 
        code += button;
        return code;
    }
 
    static KeyCodeUtil()
    {
        foreach(var pair in specifiedNames) {
            assign(pair.Key, pair.Value);
        }
 
        foreach(var name in Enum.GetNames(typeof(KeyCode))) {
            assign(name);
        }
    }
 
    private static void assign(string name)
    {
        if(ignoredNames.Contains(name)) return;
 
        var code = (KeyCode)Enum.Parse(typeof(KeyCode), name);
        if(ignoredCodes.Contains(code)) return;
 
        if(_code2str.ContainsKey(code)) return;
 
        var parts = splitName(name);
 
        switch(parts[0]) {
        case "alpha":
            assign(code, parts[1]);
            return;
        case "f":
            assign(code, name.ToLowerInvariant());
            return;
        case "keypad":
            parts = parts.slice(1);
            name = "[{0}]".format(string.Join(" ", parts.ToArray()));
            assign(code, name);
            return;
        }
 
        switch(parts.last()) {
        case "arrow":
            parts.RemoveAt(parts.Count - 1);
            break;
        case "control":
            parts[parts.Count - 1] = "ctrl";
            break;
        case "command":
            parts[parts.Count - 1] = "cmd";
            break;
        case "super":
            parts[parts.Count - 1] = "super";
            break;
        }
 
        assign(code, string.Join(" ", parts.ToArray()));
    }
 
    private static void assign(KeyCode code, string name)
    {
        _str2code[name] = code;
        _code2str[code] = name;
    }
 
    private static List<string> splitName(string name)
    {
        var parts = name.splitCamelCase();
        for(var i = 0; i < parts.Count; ++i) {
            parts[i] = parts[i].ToLowerInvariant();
        }
        return parts;
    }
 
    private static readonly Dictionary<string, KeyCode> _str2code = new Dictionary<string, KeyCode>();
    private static readonly Dictionary<KeyCode, string> _code2str = new Dictionary<KeyCode, string>();
}

一応、ほぼすべてのKeyCodeが正しく指定できているか確認したつもりです。名前が不正だと、強制的に空欄にされるので、念のため確認してみてください。

=equalsとか、どっちがキーパッドだよって感じですが、イコールキーの存在するキーボード持ってなかったので、動作確認はしていなくて、調査して出てきた結果を採用しています。キーパッドなら他と命名規則を統一して[equals]あるいは[=]のようにしてくれれば混乱しないのに。

Windowsキーはsuper系で名前がまとめられています。その名称をLinux文化に合わせるなら、KeyCodeの方もSuperにしておいてほしい・・・未定義なのかと思った・・・。

面倒なことにLeftApple、RightAppleはそれぞれLeftCommand、RightCommandと同じ値が割り当てられていて、区別不可能です(enumの内部表現は整数しか持ちません)。名前指定の存在するCommandキーとしてAppleキーをまとめるため、Enum.GetNames()からEnum.Parse()するという回りくどい対応になっています。そうしないとAppleキーの方で名前取得してしまうことがあるためです。良い子のみんなはenumの重複定義はやめようね。

対応表

せっかくなので、KeyCodeとボタン名の対応表も作りました。公式はこの表を用意してくれれば良かったんだ。

先にも述べたとおり、AppleキーはCommandキーと区別つかないので、同じボタン名が割り当てられています。

KeyCodeボタン名
None未定義
Backspacebackspace
Tabtab
Clearclear
Returnreturn
Pausepause
Escapeescape
Spacespace
Exclaim!
DoubleQuote
Hash#
Dollar$
Ampersand&
Quote
LeftParen(
RightParen)
Asterisk*
Plus+
Comma,
Minus-
Period.
Slash/
Alpha00
Alpha11
Alpha22
Alpha33
Alpha44
Alpha55
Alpha66
Alpha77
Alpha88
Alpha99
Colon:
Semicolon;
Less<
Equals=
Greater>
Question?
At@
LeftBracket[
Backslash\
RightBracket]
Caret^
Underscore_
BackQuote`
Aa
Bb
Cc
Dd
Ee
Ff
Gg
Hh
Ii
Jj
Kk
Ll
Mm
Nn
Oo
Pp
Qq
Rr
Ss
Tt
Uu
Vv
Ww
Xx
Yy
Zz
Deletedelete
Keypad0[0]
Keypad1[1]
Keypad2[2]
Keypad3[3]
Keypad4[4]
Keypad5[5]
Keypad6[6]
Keypad7[7]
Keypad8[8]
Keypad9[9]
KeypadPeriod[.]
KeypadDivide[/]
KeypadMultiply[*]
KeypadMinus[-]
KeypadPlus[+]
KeypadEnterenter
KeypadEqualsequals
UpArrowup
DownArrowdown
RightArrowright
LeftArrowleft
Insertinsert
Homehome
Endend
PageUppage up
PageDownpage down
F1f1
F2f2
F3f3
F4f4
F5f5
F6f6
F7f7
F8f8
F9f9
F10f10
F11f11
F12f12
F13f13
F14f14
F15f15
Numlocknumlock
CapsLockcaps lock
ScrollLockscroll lock
RightShiftright shift
LeftShiftleft shift
RightControlright ctrl
LeftControlleft ctrl
RightAltright alt
LeftAltleft alt
RightAppleright cmd
RightCommandright cmd
LeftCommandleft cmd
LeftAppleleft cmd
LeftWindowsleft super
RightWindowsright super
AltGralt gr
Helphelp
Printprint screen
SysReqsys req
Breakbreak
Menumenu
Mouse0mouse 0
Mouse1mouse 1
Mouse2mouse 2
Mouse3mouse 3
Mouse4mouse 4
Mouse5mouse 5
Mouse6mouse 6
JoystickButton0joystick button 0
JoystickButton1joystick button 1
JoystickButton2joystick button 2
JoystickButton3joystick button 3
JoystickButton4joystick button 4
JoystickButton5joystick button 5
JoystickButton6joystick button 6
JoystickButton7joystick button 7
JoystickButton8joystick button 8
JoystickButton9joystick button 9
JoystickButton10joystick button 10
JoystickButton11joystick button 11
JoystickButton12joystick button 12
JoystickButton13joystick button 13
JoystickButton14joystick button 14
JoystickButton15joystick button 15
JoystickButton16joystick button 16
JoystickButton17joystick button 17
JoystickButton18joystick button 18
JoystickButton19joystick button 19
Joystick1Button0joystick 1 button 0
Joystick1Button1joystick 1 button 1
Joystick1Button2joystick 1 button 2
Joystick1Button3joystick 1 button 3
Joystick1Button4joystick 1 button 4
Joystick1Button5joystick 1 button 5
Joystick1Button6joystick 1 button 6
Joystick1Button7joystick 1 button 7
Joystick1Button8joystick 1 button 8
Joystick1Button9joystick 1 button 9
Joystick1Button10joystick 1 button 10
Joystick1Button11joystick 1 button 11
Joystick1Button12joystick 1 button 12
Joystick1Button13joystick 1 button 13
Joystick1Button14joystick 1 button 14
Joystick1Button15joystick 1 button 15
Joystick1Button16joystick 1 button 16
Joystick1Button17joystick 1 button 17
Joystick1Button18joystick 1 button 18
Joystick1Button19joystick 1 button 19
Joystick2Button0joystick 2 button 0
Joystick2Button1joystick 2 button 1
Joystick2Button2joystick 2 button 2
Joystick2Button3joystick 2 button 3
Joystick2Button4joystick 2 button 4
Joystick2Button5joystick 2 button 5
Joystick2Button6joystick 2 button 6
Joystick2Button7joystick 2 button 7
Joystick2Button8joystick 2 button 8
Joystick2Button9joystick 2 button 9
Joystick2Button10joystick 2 button 10
Joystick2Button11joystick 2 button 11
Joystick2Button12joystick 2 button 12
Joystick2Button13joystick 2 button 13
Joystick2Button14joystick 2 button 14
Joystick2Button15joystick 2 button 15
Joystick2Button16joystick 2 button 16
Joystick2Button17joystick 2 button 17
Joystick2Button18joystick 2 button 18
Joystick2Button19joystick 2 button 19
Joystick3Button0joystick 3 button 0
Joystick3Button1joystick 3 button 1
Joystick3Button2joystick 3 button 2
Joystick3Button3joystick 3 button 3
Joystick3Button4joystick 3 button 4
Joystick3Button5joystick 3 button 5
Joystick3Button6joystick 3 button 6
Joystick3Button7joystick 3 button 7
Joystick3Button8joystick 3 button 8
Joystick3Button9joystick 3 button 9
Joystick3Button10joystick 3 button 10
Joystick3Button11joystick 3 button 11
Joystick3Button12joystick 3 button 12
Joystick3Button13joystick 3 button 13
Joystick3Button14joystick 3 button 14
Joystick3Button15joystick 3 button 15
Joystick3Button16joystick 3 button 16
Joystick3Button17joystick 3 button 17
Joystick3Button18joystick 3 button 18
Joystick3Button19joystick 3 button 19
Joystick4Button0joystick 4 button 0
Joystick4Button1joystick 4 button 1
Joystick4Button2joystick 4 button 2
Joystick4Button3joystick 4 button 3
Joystick4Button4joystick 4 button 4
Joystick4Button5joystick 4 button 5
Joystick4Button6joystick 4 button 6
Joystick4Button7joystick 4 button 7
Joystick4Button8joystick 4 button 8
Joystick4Button9joystick 4 button 9
Joystick4Button10joystick 4 button 10
Joystick4Button11joystick 4 button 11
Joystick4Button12joystick 4 button 12
Joystick4Button13joystick 4 button 13
Joystick4Button14joystick 4 button 14
Joystick4Button15joystick 4 button 15
Joystick4Button16joystick 4 button 16
Joystick4Button17joystick 4 button 17
Joystick4Button18joystick 4 button 18
Joystick4Button19joystick 4 button 19
Joystick5Button0joystick 5 button 0
Joystick5Button1joystick 5 button 1
Joystick5Button2joystick 5 button 2
Joystick5Button3joystick 5 button 3
Joystick5Button4joystick 5 button 4
Joystick5Button5joystick 5 button 5
Joystick5Button6joystick 5 button 6
Joystick5Button7joystick 5 button 7
Joystick5Button8joystick 5 button 8
Joystick5Button9joystick 5 button 9
Joystick5Button10joystick 5 button 10
Joystick5Button11joystick 5 button 11
Joystick5Button12joystick 5 button 12
Joystick5Button13joystick 5 button 13
Joystick5Button14joystick 5 button 14
Joystick5Button15joystick 5 button 15
Joystick5Button16joystick 5 button 16
Joystick5Button17joystick 5 button 17
Joystick5Button18joystick 5 button 18
Joystick5Button19joystick 5 button 19
Joystick6Button0joystick 6 button 0
Joystick6Button1joystick 6 button 1
Joystick6Button2joystick 6 button 2
Joystick6Button3joystick 6 button 3
Joystick6Button4joystick 6 button 4
Joystick6Button5joystick 6 button 5
Joystick6Button6joystick 6 button 6
Joystick6Button7joystick 6 button 7
Joystick6Button8joystick 6 button 8
Joystick6Button9joystick 6 button 9
Joystick6Button10joystick 6 button 10
Joystick6Button11joystick 6 button 11
Joystick6Button12joystick 6 button 12
Joystick6Button13joystick 6 button 13
Joystick6Button14joystick 6 button 14
Joystick6Button15joystick 6 button 15
Joystick6Button16joystick 6 button 16
Joystick6Button17joystick 6 button 17
Joystick6Button18joystick 6 button 18
Joystick6Button19joystick 6 button 19
Joystick7Button0joystick 7 button 0
Joystick7Button1joystick 7 button 1
Joystick7Button2joystick 7 button 2
Joystick7Button3joystick 7 button 3
Joystick7Button4joystick 7 button 4
Joystick7Button5joystick 7 button 5
Joystick7Button6joystick 7 button 6
Joystick7Button7joystick 7 button 7
Joystick7Button8joystick 7 button 8
Joystick7Button9joystick 7 button 9
Joystick7Button10joystick 7 button 10
Joystick7Button11joystick 7 button 11
Joystick7Button12joystick 7 button 12
Joystick7Button13joystick 7 button 13
Joystick7Button14joystick 7 button 14
Joystick7Button15joystick 7 button 15
Joystick7Button16joystick 7 button 16
Joystick7Button17joystick 7 button 17
Joystick7Button18joystick 7 button 18
Joystick7Button19joystick 7 button 19
Joystick8Button0joystick 8 button 0
Joystick8Button1joystick 8 button 1
Joystick8Button2joystick 8 button 2
Joystick8Button3joystick 8 button 3
Joystick8Button4joystick 8 button 4
Joystick8Button5joystick 8 button 5
Joystick8Button6joystick 8 button 6
Joystick8Button7joystick 8 button 7
Joystick8Button8joystick 8 button 8
Joystick8Button9joystick 8 button 9
Joystick8Button10joystick 8 button 10
Joystick8Button11joystick 8 button 11
Joystick8Button12joystick 8 button 12
Joystick8Button13joystick 8 button 13
Joystick8Button14joystick 8 button 14
Joystick8Button15joystick 8 button 15
Joystick8Button16joystick 8 button 16
Joystick8Button17joystick 8 button 17
Joystick8Button18joystick 8 button 18
Joystick8Button19joystick 8 button 19

上記リンク先は今回の変換処理をすべて配列で持つ実装をしたものですが、そこでの定義によると、KeyCodeに定義はなくても名前指定のできるものが存在します。廃止されたとかなんでしょうか。調べてもよくわかりませんでした。

  • world 0world 95
  • compose
  • power
  • euro
  • undo
  • Joystick 9~11系