Xbox 360 コントローラ
入力に反応する
何らかの絵を画面に表示することは、ゲーム制作の第一歩となりますが、これだけではゲームになりません。どんなに美しいグラフィックを表示しても、それだけではただの映像作品です。これをゲームとして仕掛けるには、プレイヤーが世界に何らかの影響を与える仕組みを提供しなければなりません。多くの場合、プレイヤーはコントローラなどの入力機器を通して、ゲームの世界の主人公を操作します。操作する対象は、勇者、戦闘機、あるいは大悪党や神様かもしれません。
ゲームの世界の何らかの要素をプレイヤーに操作させるには、ゲームが入力に対応しなければなりません。XNA Framework では、Xbox 360 コントローラ、キーボード、マウス(ポインティングデバイス)、及びタッチパネルからの入力に対応しています。
ただし、入力デバイスの対応は環境によって異なります。例えば Xbox 360 はマウスとタッチパネルに対応していません。Xbox 360 コントローラは USB なので、Windows PC でもゲームコントローラとして使えます。また、Xbox 360 でも USB キーボードを接続して利用できます。
Windows PC | Xbox 360 | Windows Phone | |
Xbox 360 コントローラ | ○ | ○ | × |
キーボード | ○ | ○ | △ |
マウス | ○ | × | △ |
タッチパネル | × | × | ○ |
Xbox 360 専用のゲームを開発する場合は、Xbox 360 コントローラに最適化するべきです。Xbox 360 でキーボードを使うこともできますが、多くのプレイヤーはコントローラでゲームを遊ぶことに慣れています。逆に、Windows PC 専用のゲームを開発する場合は、キーボードとマウスの操作に最適化するべきです。多くの PC ユーザーは、キーボードをマウスを使って操作することに慣れています。最もよい手段は、両方に対応することです。コントローラとキーボード、どちらでも操作できるようにプログラムすれば、クロスプラットフォームでの展開でも気にする必要はありません。
Xbox 360 コントローラは、図1のようなボタンやスティックを持ちます。また、振動機能があるため、必要であればゲームに合わせてコントローラを振動させることも可能です。加えて、Xbox 360 コントローラは 4 つまで接続可能です。USB キーボードやマウスは、システムに対して 1 つしか接続できません。ただし、コントローラに直接接続する Chatpad は、各プレイヤーに関連付けられます。
左ショルダボタンは LB、右ショルダボタンは RB、左トリガーは LT、右トリガーは RT と略されます。左トリガーと右トリガー、図のコントローラの背面に配置されています。
各種ボタンは、押しているか、押していないかという 2 つの状態を持ちます。これに対して、トリガーは押されていない状態を 0.0、完全に押し込まれている状態を 1.0 とし、その間の値を得ることができます。左スティックと右スティックは、特定の方向にどの程度傾けられているかを取得できるほか、押すことによってボタンとしても機能します。
中央のガイドボタンは、ゲームでは使われません。システムの機能を呼び出すために用いられるもので、Xbox 360 上でボタンを押すと、ブレードが表示されます。Windows PC では機能しませんが、XNA Framework ゲームで、ゲーマーサービスに対応している場合は、サインインなどを行うための画面が表示されます。ゲーマーサービスについては後述します。
プレイヤーのコントローラの操作は、コントローラの状態を取得することで認識できます。コントローラの状態を取得するには Microsoft.Xna.Framework.Input.GamePad クラスを使います。
public static class GamePad
このクラスは、Xbox 360 コントローラへのアクセスを提供する機能を静的メソッドで提供します。
コントローラの状態を取得するには GetState() メソッドを使います。
public static GamePadState GetState ( PlayerIndex playerIndex )
playerIndex パラメータには、コントローラの状態を取得するプレイヤーの番号を指定します。Xbox 360 コントローラは、1 システムに対して最大で 4 プレイヤーまで接続できます。コントローラを使っているプレイヤーは Microsoft.Xna.Framework.PlayerIndex 列挙型のメンバで識別します。
public enum PlayerIndex
この列挙型には、第 1 プレイヤーから第 4 プレイヤーまで順に One、Two、Three、Four メンバを定義しています。プレイヤーの番号は、Xbox 360 コントローラのガイドボタンのライトから判断できます。
GetState() メソッドは、playerIndex パラメータで指定されたプレイヤーのコントローラの状態を返します。コントローラの状態は Microsoft.Xna.Framework.Input.GamePadState 構造体で表されます。
public struct GamePadState
この構造体は、メンバから各種のボタンやスティックなどの状態を表します。コントローラの状態は、大きく分けてボタン、方向パッド、スティック、トリガーに分けられています。
ボタン
ボタンの状態は、押されているか押されていないかの 2 状態しかないためシンプルです。主な操作に使われるボタンは A、B、X、Y ボタンの 4 つで、この他に LB、RB、スティック、Start、Back などもボタンに該当します。ボタンが押されているかどうかを調べるには、GamePadState 構造体の Buttons プロパティを使います。
public GamePadButtons Buttons { get; }
このプロパティは、各ボタンの状態を提供する Microsoft.Xna.Framework.Input.GamePadButtons 構造体の値を返します。
public struct GamePadButtons
この構造体は、各ボタンの名前に対応するプロパティを公開しています。プロパティは、対応するボタンが押されているかどうかを表します。例えば、A ボタンが押されているかどうかを調べるには A プロパティの値を取得します。
public ButtonState A { get; }
A プロパティは、ボタンの状態を表す Microsoft.Xna.Framework.Input.ButtonState 列挙型の値を返します。
public enum ButtonState
この列挙型には、ボタンが押されていることを表す Pressed メンバと、離されていることを表す Released メンバが定義されています。各ボタンの値を取得し Pressed と一致すればボタンが押されていると判断できます。 A ボタン以外の、各種ボタンの状態も同じ方法で取得できます。ボタンに対応するプロパティは図2を参考にしてください。
左右のスティックも、傾きによる方向だけではなく押し込むことでボタンとして機能します。
using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; using Microsoft.Xna.Framework.Input; public class TestGame : Game { public static void Main(string[] args) { using (Game game = new TestGame()) game.Run(); } private GraphicsDeviceManager graphicsDeviceManager; private Color color; public TestGame() { graphicsDeviceManager = new GraphicsDeviceManager(this); } protected override void Update(GameTime gameTime) { GamePadState state = GamePad.GetState(PlayerIndex.One); color = Color.Black; if (state.Buttons.A == ButtonState.Pressed) color = new Color(color.R, (byte)(color.G | 0xFF), color.B); if (state.Buttons.B == ButtonState.Pressed) color = new Color((byte)(color.R | 0xFF), color.G, color.B); if (state.Buttons.X == ButtonState.Pressed) color = new Color(color.R, color.G, (byte)(color.B | 0xFF)); if (state.Buttons.Y == ButtonState.Pressed) color = new Color((byte)(color.R | 0xFF), (byte)(color.G | 0xA5), color.B); base.Update(gameTime); } protected override void Draw(GameTime gameTime) { GraphicsDevice.Clear(color); base.Draw(gameTime); } }
コード1は、Xbox 360 コントローラの主要なボタンである A、B、X、Y のいずれかが押されると、ゲーム画面の背景をボタンに対応する色で塗りつぶします。複数のボタンが同時に押されている場合は、論理和で合成した色で塗りつぶしています。コントローラの入力を調べ、色を作成しているのは Update() メソッドです。最初に GamePad クラスの GetState() メソッドから、第 1 プレイヤーのコントローラの状態を取得し state 変数に保存します。その後、各ボタンの状態を調べ、ButtonState.Pressed であれば押されていると判断して色を設定します。
トリガー
コントローラ上部の後ろにある左右のトリガーは、単純なボタンではありません。トリガーがどのくらい引かれているかを 0.0 ~ 1.0 までの値で測定できます。よって、ドライブゲームのアクセルやブレーキなど、より精密な踏み込みを表現するために用いられます。銃の引き金のように、何かを「引く」ような動作をプレイヤーに体験させるといった使い方が考えられます。一般に、トリガーは人差し指で引くことになるため、親指と独立して操作することができます。よって、一人称視点 シューティング FPS (First Person Shooter)で使われる操作のような、親指で操作する方向スティックとトリガーを組み合わせた精密で複雑な入力を可能とします。
トリガーの入力を感知するには GamePadState 構造体の Triggers プロパティを使います。
public GamePadTriggers Triggers { get; }
このプロパティは、左右のトリガーの状態を表す Microsoft.Xna.Framework.Input.GamePadTriggers 構造体の値を返します。
public struct GamePadTriggers
この構造体は、左トリガーの値を返す Left プロパティと、右トリガーの値を返す Right プロパティを公開しています。
public float Left { get; }
public float Right { get; }
これらのプロパティが返す値は、トリガーが押されていない状態を 0.0、完全に押し込まれている状態を 1.0 とする浮動小数点数です。トリガーを 50% まで押し込んでいる場合は 0.5 が得られることになります。段階的に変化するオブジェクトなどの操作に適しています。
using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; using Microsoft.Xna.Framework.Input; public class TestGame : Game { public static void Main(string[] args) { using (Game game = new TestGame()) game.Run(); } private GraphicsDeviceManager graphicsDeviceManager; private Color color; public TestGame() { graphicsDeviceManager = new GraphicsDeviceManager(this); } protected override void Update(GameTime gameTime) { GamePadState state = GamePad.GetState(PlayerIndex.One); color = new Color( (byte)(0xFF * state.Triggers.Left), 0, (byte)(0xFF * state.Triggers.Right) ); Window.Title = "Left=" + state.Triggers.Left + ", Right=" + state.Triggers.Right; base.Update(gameTime); } protected override void Draw(GameTime gameTime) { GraphicsDevice.Clear(color); base.Draw(gameTime); } }
コード2は、左右のトリガーを押すと画面の色が変化するプログラムです。左トリガーが押されるとより赤色に、右トリガーが押されるとより青色に近づきます。トリガーの値は 0.0 ~ 1.0 までの float 型なので、これに限界値を乗算することで間を取ることができます。上記のプログラムでは、色要素の最大値である 0xFF すなわち 255 とトリガーの値を乗算し、この結果を Color 構造体のコンストラクタに渡しています。
スティック
左右のスティックからは、水平軸、垂直軸に対して、どの程度傾いているかを得られます。主に、キャラクターの移動や視点の操作に使われています。
スティックの値を得るには GamePadState 構造体の ThumbSticks プロパティを使います。
public GamePadThumbSticks ThumbSticks { get; }
このプロパティは、左右の方向スティックの状態を表す Microsoft.Xna.Framework.Input.GamePadThumbSticks 構造体の値を返します。
public struct GamePadThumbSticks
この構造体は、左スティックの状態を Left プロパティで、右スティックの状態を Right プロパティで提供します。
public Vector2 Left { get; }
public Vector2 Right { get; }
これらのプロパティは、方向スティックの水平軸の傾きと、垂直軸の傾きを表す Vector2 構造体の値を返します。水平軸に対する傾きは X プロパティに、垂直軸に対する傾きは Y プロパティに格納されています。それぞれのプロパティの値は、傾きによって -1.0 ~ 1.0 の範囲で得られます。方向スティックが傾けられていないデフォルトの状態は 0 を表します。
スティックが左に傾けられると X プロパティの値は -1.0 に向かい、右に傾けられると 1.0 に向かいます。スティックが上に傾けられると 1.0 に向かい、下に傾けられると -1.0 に向かいます。垂直軸に対しては、ピクセルの座標系とは反対なので注意してください。座標の場合、Y 座標の値が増えるほど下に向かいましたが、方向スティックの場合、下は負数になります。
using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; using Microsoft.Xna.Framework.Input; public class TestGame : Game { public static void Main(string[] args) { using (Game game = new TestGame()) game.Run(); } private GraphicsDeviceManager graphicsDeviceManager; private SpriteBatch sprite; private Texture2D texture; private Vector2 position; public TestGame() { graphicsDeviceManager = new GraphicsDeviceManager(this); Content.RootDirectory = "Content"; } protected override void LoadContent() { sprite = new SpriteBatch(GraphicsDevice); texture = Content.Load<Texture2D>("TestImage"); base.LoadContent(); } protected override void Update(GameTime gameTime) { GamePadState state = GamePad.GetState(PlayerIndex.One); position.X += 4 * state.ThumbSticks.Left.X; position.Y += 4 * -state.ThumbSticks.Left.Y; Window.Title = "X=" + state.ThumbSticks.Left.X + ", Y=" + state.ThumbSticks.Left.Y; base.Update(gameTime); } protected override void Draw(GameTime gameTime) { GraphicsDevice.Clear(Color.White); sprite.Begin(); sprite.Draw(texture, position, Color.White); sprite.End(); base.Draw(gameTime); } }
コード3は、左スティックの傾きによって画面に表示されているスプライトを移動させるプログラムです。スプライトの座標には Vector2 構造体型の position フィールドが使われています。Update() メソッドでは、左スティックの X 軸と Y 軸の傾きを position フィールドに加算しています。 スティックの傾きは 0.0 ~ 1.0 までの比率で得られるので、この値に 1 フレームで移動可能な最大ピクセル数を乗算することで、移動速度を制御できます。このプログラムでは 4 を乗算しているため、スティックが完全に傾けられると 1 フレームで 4 ピクセル移動できます。
また、Update() メソッド内で Y 座標を更新する時、単項 - 演算子を使ってスティックの Y プロパティの値を反転しています。これは、スティックが下に傾けられたときの Y プロパティの値が負数になるためです。符号を反転させない場合、スティックの垂直軸に対する傾きと、スプライトの移動方向が逆になってしまいます。ウィンドウのタイトルバーに、スティックの状態から得られた GamePadThumbSticks 構造体の X プロパティと Y プロパティの値を表示しているので、確認してください。
方向パッド
方向パッドは、キーボードの方向キーと同じように、上下左右の 4 つのボタン機能を提供します。スティックからは、水平および垂直軸に対する傾けられている率を得られましたが、方向パッドはボタンと同じで押されているか、押されていないかの 2 つの状態しかありません。
方向パッドの状態を取得するには GamePadState 構造体の DPad プロパティを使います。
public GamePadDPad DPad { get; }
このプロパティは、方向パッドの各ボタンの状態を表す GamePadDPad 構造体の値を返します。
public struct GamePadDPad
この構造体は、上下左右の方向ボタンが押されているかどうかを返すプロパティを公開しています。左ボタンは Left プロパティ、右ボタンは Right プロパティ、上ボタンは Up プロパティ、下ボタンは Down プロパティから、それぞれ状態を取得できます。
public ButtonState Left { get; }
public ButtonState Right { get; }
public ButtonState Up { get; }
public ButtonState Down { get; }
これらのプロパティは、GamePadButtons 構造体のプロパティと同じように、ボタンが押されているか、押されていないかを表す ButtonState 列挙型の値を返します。複数のボタンが同時に押される可能性もありますが、ま逆のボタンを同時に押すことはできません。よって、Left と Down を同時に押して左下を表すことができます。しかし、Left と Right を同時に押すことはできません。
using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; using Microsoft.Xna.Framework.Input; public class TestGame : Game { public static void Main(string[] args) { using (Game game = new TestGame()) game.Run(); } private GraphicsDeviceManager graphicsDeviceManager; private SpriteBatch sprite; private Texture2D texture; private Vector2 position; public TestGame() { graphicsDeviceManager = new GraphicsDeviceManager(this); Content.RootDirectory = "Content"; } protected override void LoadContent() { sprite = new SpriteBatch(GraphicsDevice); texture = Content.Load<Texture2D>("TestImage"); base.LoadContent(); } protected override void Update(GameTime gameTime) { GamePadState state = GamePad.GetState(PlayerIndex.One); string title = ""; if (state.DPad.Left == ButtonState.Pressed) { position.X += -2; title += "Left "; } else if (state.DPad.Right == ButtonState.Pressed) { position.X += 2; title += "Right "; } if (state.DPad.Up == ButtonState.Pressed) { position.Y += -2; title += "Up "; } else if (state.DPad.Down == ButtonState.Pressed) { position.Y += 2; title += "Down "; } Window.Title = title; base.Update(gameTime); } protected override void Draw(GameTime gameTime) { GraphicsDevice.Clear(Color.White); sprite.Begin(); sprite.Draw(texture, position, Color.White); sprite.End(); base.Draw(gameTime); } }
コード4は、方向パッドを使ってスプライトを移動できます。基本的な仕組みはコード3と共通しています。方向パッドは、押しているか押していないかの 2 つの状態しか持たないため、ボタンが押されている状態であれば、1 フレーム中に 2 ピクセル、押されているボタンの方向に移動させます。
振動
Xbox 360 コントローラには、左右に振動モーターが内蔵されています。アプリケーションから振動モーターを制御することができるので、ゲームの状況に合わせてコントローラを振動させましょう。コントローラを振動させるには GamePad クラスの SetVibration() メソッドを使います。
public static bool SetVibration ( PlayerIndex playerIndex, float leftMotor, float rightMotor )
playerIndex パラメータには、振動させるコントローラに対応するプレイヤー番号を指定します。leftMotor には左側のモーターを振動させる強さ、rightMotor には右側のモーターを振動させる強さを、それぞれ 0.0 ~ 1.0 までの範囲で指定します。0 は震えていない状態を表し、1 は最も強くふるえている状態を表します。
左側の低周波モーターは、ゆっくりと大きく震えます。右側の高周波モーターは、小刻みに震えます。片方だけを振動させることもできますし、両方の割合を調整して振動させることもできます。
using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; using Microsoft.Xna.Framework.Input; public class TestGame : Game { public static void Main(string[] args) { using (Game game = new TestGame()) game.Run(); } private GraphicsDeviceManager graphicsDeviceManager; private Color color; public TestGame() { graphicsDeviceManager = new GraphicsDeviceManager(this); } protected override void Update(GameTime gameTime) { GamePadState state = GamePad.GetState(PlayerIndex.One); color = new Color( (byte)(0xFF * state.Triggers.Left), 0, (byte)(0xFF * state.Triggers.Right) ); GamePad.SetVibration(PlayerIndex.One, state.Triggers.Left, state.Triggers.Right); base.Update(gameTime); } protected override void Draw(GameTime gameTime) { GraphicsDevice.Clear(color); base.Draw(gameTime); } }
コード5は、コード2を改良して左右のトリガーに同期してコントローラを振動させる機能を追加したプログラムです。左右のトリガーを引くと、画面の色が変化すると同時にコントローラーが振動します。