XNA キー押下検知

前回の

XNA ことはじめ
http://d.hatena.ne.jp/levin_gsp/20071212/1197440515

に続いて、XNA/C# で勝手に一人で盛り上がっていく。
 
今回は XNA におけるキーボード入力の検知を題材として取り上げる。
ソースコードレベルで手元にあるのは Spacewar Project と Empty Project (共にWin版)のみ。
ここから自力で解析しつつ、考察しつつ、色々試してみたい。
 
まず、Spacewar を少しプレイしてみると、“およ?”という印象の一つにキー操作がある。
XBOX360 を想定したキー配列になっているのだが、その設定がどこにあるのか。
探してみると、まずこんなのが見つかった。
 
Debug ビルドした後であれば、Debug フォルダ(Releaseも同様)の下に settings.xml がある。

<?xml version="1.0"?>
<Settings xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <MediaPath>content\</MediaPath>
  <WindowTitle>Spacewar</WindowTitle>
  
  〜中略〜
  
  <Player1Start>LeftControl</Player1Start>
  <Player1Back>LeftShift</Player1Back>
  <Player1A>V</Player1A>
  <Player1B>G</Player1B>
  <Player1X>F</Player1X>
  <Player1Y>T</Player1Y>
  <Player1ThumbstickLeftXmin>A</Player1ThumbstickLeftXmin>
  <Player1ThumbstickLeftXmax>D</Player1ThumbstickLeftXmax>
  <Player1ThumbstickLeftYmin>S</Player1ThumbstickLeftYmin>
  <Player1ThumbstickLeftYmax>W</Player1ThumbstickLeftYmax>
  <Player1Left>A</Player1Left>
  <Player1Right>D</Player1Right>
  <Player1Down>S</Player1Down>
  <Player1Up>W</Player1Up>
  <Player1LeftTrigger>Q</Player1LeftTrigger>
  <Player1RightTrigger>E</Player1RightTrigger>

  <Player2Start>RightControl</Player2Start>
  <Player2Back>RightShift</Player2Back>
  <Player2A>Home</Player2A>
  <Player2B>End</Player2B>
  <Player2X>PageUp</Player2X>
  <Player2Y>PageDown</Player2Y>
  <Player2ThumbstickLeftXmin>Left</Player2ThumbstickLeftXmin>
  <Player2ThumbstickLeftXmax>Right</Player2ThumbstickLeftXmax>
  <Player2ThumbstickLeftYmin>Down</Player2ThumbstickLeftYmin>
  <Player2ThumbstickLeftYmax>Up</Player2ThumbstickLeftYmax>
  <Player2Left>Left</Player2Left>
  <Player2Right>Right</Player2Right>
  <Player2Down>Down</Player2Down>
  <Player2Up>Up</Player2Up>
  <Player2LeftTrigger>Insert</Player2LeftTrigger>
  <Player2RightTrigger>Delete</Player2RightTrigger>
</Settings>

パッと見からして、キー配列以外にも数多くの(というより動作に必要なほとんどの)設定が記載された XML ファイルだとわかる。
settings.xml の名の通り、という感じ。
 
しかし、これはソースではなく、ビルドによって出力された“実行時に入力するための設定ファイル”だ。
(同パスに出来ている exe を実行する前に、上記の内容を変更しておけばそれに準じた設定となる(と思う
 
じゃあこれらの設定はどこで行っているのか。
とりあえず、キーの配列(XBOX360コントローラ用のボタン割り当て)はどこで決めているのか。
探してみた。
Program.cs や SpacewarGame.cs より一階層深いパス「common」フォルダの下にある4つのソースがどうもそれっぽい。
 
GamePadHelper.cs
GamePads.cs
Keymap.cs
XInputHelper.cs
 
ここでキー配列を含むキーパッド関連のクラスを定義してるみたい。
 
さて、本題。
XNA におけるキー押下通知、非常に気になる機構。
XNA Game Studio 2.0 で追加されたキーパッド関連のメソッドがある。
 
IsButtonDown
IsButtonUp
 
である。
これによるコードレベルでの変化を、XNA Game Studio Express 1.x までと比較してみる。
 
1.x

// AボタンとBボタンの同時押しを検出!
if (padState.Buttons.A == ButtonState.Pressed && padState.Buttons.B == ButtonState.Pressed ) ...

2.0

// AボタンとBボタンの同時押しを検出!
if (padState.IsButtonDown(Buttons.A&Buttons.B) ) ...

 
うん、スッキリ書ける。
2つのキー同時押しでこれだから、それ以上になれば更に。
同時押しに限らず、どちらか(いずれか)でも '&' が '|' の判定になるだけで、処理の簡素化の程度は同様。
これはイイ。
 
この判定方法って、1.x にせよ 2.0 にせよ「押されているかどうか」を見ている。
「押されたことを検知した」わけではない。
つまり、キー押下(KeyPress)イベントを受け取るような WinForms 的な発想とは異なるわけだ。
これはどうしてだろう。
 
個人的解釈を書いてみる。
XNA はゲームを開発するための Framework だ。
そのための機構が色々とデフォルトで組み込まれている。
その一つは Update メソッドの実装ではないかと思う。
 
空プロジェクトでも頻繁に呼ばれる Update メソッド。
実行して、どっか(Updateメソッドが妥当か)で BreakPoint 張って止めてみる。
 
this.base.TargetElapsedTime が Update のコール周期。
{00:00:00.0166667}
 
となっていた。
Default では、0.0166667 秒周期、つまり「秒間60回」頻度でコールされている。
設定変更はプロパティの値を変えることで出来るが、そういうことを言っているのではなくて。
「秒間60回」呼ばれるように初期設定されていることこそが、「ゲームを開発するための Framework 」ならではの設定だと感じる。
Windows アプリケーションや、Web アプリケーションと違って、ゲームの場合、画面に対して何かしら目に見える変化がある。
そうでなければ成り立たないからだ。(ゲームが
 
XNA は、KeyPress を検出するような機構を提供していない。
それは、Update メソッドで「キーが押されているかどうか」を判定することで、「キーが押されたことを検出する」のと同様の検知能力を提供できる、という意図の現れではないかと思う。
ふーむ。
XNA ならではの入力検知処理を垣間見た気がした。