地上の洞窟

どこにも行かず、液晶と「にらめっこ」し続ける人の物語。

【Ruby】Win32APIを使用してキー入力を取得する方法


前置き

RGSS3みたいな「ゲーム系のアプリケーションでキー入力を取得したい」
そういう時に使えるかもしれないお話。
※古いRuby1.9ぐらいの話なので、Win32APIみたいなdllの呼び出しは
また新しいバージョンだと話が変わってきたりすると思う。自分は詳しくないけど。

Win32APIの概要

APIとは略さずに言うと "Application Programming Interfaces" と言うらしく、
Win32APIとはなんぞやというのをざっくりとまとめるなら、
Windowsの機能をプログラムから簡単に呼び出すための仕組み」と言える。

Rubyの「Win32API」クラス

Rubyには「Win32API」というクラスがある。
これはその名の通りWin32APIを介してWindowsの機能を呼び出すことができる。
実際には単純にdllファイルの機能を呼び出すことが出来るクラスであるため、
自作したdllをRubyで読み込んで使用するといったことも可能。

Win32APIで出来ること

Windowsのアプリケーションで出来ること、と言ったら
多岐に渡りすぎるので全て紹介することは不可能だが、簡単な所で言うと

  • ウィンドウの作成、移動
  • ファイル・ディレクトリの操作
  • マウス・キーボード入力の取得

などが挙げられる。
他にもなんやかんやと描画したり、IMEに入力された文字を取得したりとか…
挙げてたらキリがないと言えば、その通りである。

Rubyは基本、コンソール上で動かすので、Win32APIと関わることは通常、無い。
RPGツクール「RGSS」みたいにRubyの延長線上でWindowsアプリケーションを動かす」
といった場合に、必要になる技術と言える。

実装

とりあえず簡単に。
キーボードの「A」が押されたら"A"と出力するだけのコード。

# encoding: utf-8
require "win32api"

get_key_state = Win32API.new('user32', 'GetKeyState', 'i', 'i')

while true
  state = get_key_state.call(65)
  if state & 0xFF00 > 0
    puts "A"
  end
  sleep(0.01)
end


解説

とりあえずRubyのWin32APIは組み込みではないのでrequireを忘れずに。
(RGSS3では要らないよ!)

Win32APIクラスの初期化

new時には以下の4つの引数が必要。

引数 説明
dll_name String 機能(関数)を呼び出すdllの名称
func String 呼び出す関数の名前
import String
またはStringの配列
関数に渡す引数の「型」
export String 関数が返す値の「型」

import, exportで指定する型には以下の文字列を使用。

文字
ポインタ 'p'
long 'l'
int 'i'
void 'v'



今回のキーボード入力の取得に使うWin32APIクラスの初期化は以下のようになる。

get_key_state = Win32API.new('user32', 'GetKeyState', 'i', 'i')



キーボード入力の取得にはUser32.dll内のGetKeyState関数を使用する。*1
引数には入力を取得したいキーの「仮想キーコード」を渡す。
仮想キーコードはキーボード上の各キーを識別するための数値である。int型。
例えばEnterキーの入力取得をしたければ13、
アルファベットのAキーを取得したければ65などと細かく決まっている。
仮想キーコード一覧は調べれば出てくる。
返り値は指定したキーの入力状態が返ってくる。

関数の呼び出し

では早速GetKeyState関数を呼び出してみる。
呼び出しには「.call」するだけ。そのまんまだね。

get_key_state.call(65)

引数には取得したいキーの仮想キーコードを指定。
今回はAキーを取得したいので65を指定。

キーの押下判定

取得したキーの状態は16Bitの整数で表されており、
キーが押されている場合は、16Bitの内の上側8Bitが全て立つ。*2
つまり、キーが押下中かどうかを判定するには、ビット演算で上側8Bitを抽出し、
その値が0を超えているかで判定すればいい、ということになる。

state = get_key_state.call(65)
if state & 0xFF00 > 0
  # キーが押されている場合の処理
end

…と、このようにしてキー入力が取得できる。意外と簡単?

まとめ

意外とキー取得は回りくどい!!

まぁRubyWindowsアプリケーションであることを
前提としてない?ので当然といえば当然だが。

余談だがこのdll呼び出しは結構重い。ので、
Rubyで計算するのは遅いから、dll上はC++だしそっちで動かせば速いんじゃね?」
とかいう邪道に手を出そうとして失敗したことがあったりする。
もうそれ、Ruby使ってる意味よ…?

*1:他にもGetAsyncKeyStateだったりGetKeyboardStateだったりがあるが
それぞれで使い方、使い道は異なる。今回は割愛

*2:0b1111_1111_0000_0000の様になる