FC2ブログ
ヽ|∵|ゝ(Fantom) の 開発blog? ホーム » Unity »【Unity】【C#】CapsLock のオン・オフ状態を取得する (Windows)

【Unity】【C#】CapsLock のオン・オフ状態を取得する (Windows)  


 Unity には Input クラスという入力に関するクラスが元からあるが、残念ながら「Input.GetKey(KeyCode.CapsLock)」のようにして、CapsLock の状態を取得するのは上手く行かなかった(押下した瞬間の判定なので、常に true になる)。

 また、OnGUI() での Event.capsLock というものもあるが(Update() などでは使えない=エラーが出る)、こちらも同じように押下した瞬間に true になるだけで、「オン/オフの継続している状態」が取得できない。

 なのでいつものようにググってたら、Windows API から取得する方法があったので、試してみた。しかし同時に利用するにあたって、いくつか注意する点もあったので、備忘録として残しておこう。

(※) Unity 2019.2.21f1 / Windows10(x64) で確認

 まずは Windows API から状態を取得するメソッドを static で定義しよう。以下のコードは参考資料の内容を、使い回せるように少しだけ手を加えたものだ。.NET 4.x 以降のコードになっているので注意して欲しい(.NET 3.x にしたいなら「=>」などを通常のメソッドに書き直せば使える)。

(参考)
Capslock detection ?

●Windows API から CapsLock の状態を取得する
#if UNITY_STANDALONE_WIN || UNITY_EDITOR_WIN
using System.Runtime.InteropServices;

public static class WindowsUtil //※名前は任意
{
//https://forum.unity.com/threads/capslock-detection.538792/
[DllImport("user32.dll")]
public static extern short GetKeyState(int keyCode);

//CapsLock の状態取得
public static bool IsCapsLockOn
=> (((ushort)GetKeyState(0x14)) & 0xffff) != 0; //※.NET 4.x
}
#endif

 あとは参考資料と同じように、起動時にプロパティにでも入れておいて、キー入力のたびに反転するのが良いだろう。毎フレーム API 使うのは負荷が高そうだしね(常に型変換の必要があるので)。ここまではほとんど参考資料と同じだ。

 メインは例えば、以下のようなコードで確認できるだろう(何かキーを押したときに CapsLock のオン・オフ状態が取得できる)。

●メインでの使用例
using UnityEngine;

bool IsCapsLockOn { get; set; }

private void Awake()
{
#if UNITY_STANDALONE_WIN || UNITY_EDITOR_WIN
IsCapsLockOn = WindowsUtil.IsCapsLockOn; //Windows API を定義したクラス
#endif
}

private void Update()
{
if (Input.anyKeyDown)
{
if (Input.GetKeyDown(KeyCode.CapsLock))
{
IsCapsLockOn = !IsCapsLockOn;
}

Debug.Log($"IsCapsLockOn = {IsCapsLockOn}"); //※.NET 4.x
}
}

 ただこれには1つ注意点があって、「アプリから離れる → 他のアプリ上などで CapsLock を切り替える → アプリに戻る」などの操作をすると、IsCapsLock のプロパティが狂ってしまう。なので、再度アプリにフォーカスが当たったときや、アプリのポーズ復帰(Resume)のようなタイミングで再度 Windows API から取得した方が良いようだ。

●フォーカスやレジュームのタイミングで再取得例
・・・(中略 ※前述のコード)・・・
private void OnApplicationFocus(bool focus)
{
if (focus)
{
#if UNITY_STANDALONE_WIN || UNITY_EDITOR_WIN
IsCapsLockOn = WindowsUtil.IsCapsLockOn;
#endif
}
}

private void OnApplicationPause(bool pause)
{
if (!pause) //Resume
{
#if UNITY_STANDALONE_WIN || UNITY_EDITOR_WIN
IsCapsLockOn = WindowsUtil.IsCapsLockOn;
#endif
}
}

 しかし、実はまだこれでも上手く行かないときがある。何度か試してみたら、どうやら OnApplicationFocus 等と同フレームで取得すると、不正確になるようだ。なので1フレーム遅らせて取得するなどの工夫も必要になる(エディタ上では上手く行くかも知れないが、ランタイム時にはタイミングによって上手く行かないことがある=Unity ではよくある事なので覚えておくと良い)。

 1フレーム遅らせて処理する方法は色々あるので、好きにやって欲しい。コルーチンでも良いし、.NET 4.x なら async/await でも良いだろう。UniRx プラグイン使っているなら Observable.NextFrame() を使うのも良いね。以下に例を載せておくので、既存のコードとの兼ね合いで実装すると良いだろう。

●コルーチン例
・・・(中略)・・・
private IEnumerator OnApplicationFocus(bool focus)
{
if (focus)
{
yield return null;
#if UNITY_STANDALONE_WIN || UNITY_EDITOR_WIN
IsCapsLockOn = WindowsUtil.IsCapsLockOn;
#endif
}
}

//OnApplicationPause も同様


●UniRx, UniTask / async, await 例
【Unity】【UniRx】時間系のオペレータまとめ
【Unity】【C#】UniRx で「1フレームごと待機して処理」してみる





(関連記事)
【Unity】【C#】UniRx で「1フレームごと待機して処理」してみる
【Unity】【C#】KeyCode をリアルタイムで調べる / Windows 日本語キーボードでの記号の KeyCode 一覧
【Unity】5ボタンマウスの KeyCode 図解
【Unity】【C#】ゲームパッドの右スティックを使えるようにする


関連記事
スポンサーサイト



category: Unity

thread: ゲーム開発

janre: コンピュータ

tag: Unityリファレンス  WindowsAPI  C# 
tb: 0   cm: --


トラックバック

トラックバックURL
→http://fantom1x.blog130.fc2.com/tb.php/347-52adc2b4
この記事にトラックバックする(FC2ブログユーザー)

プロフィール

Social

検索フォーム

全記事一覧

カテゴリ

ユーザータグ

最新記事

リンク

PR