タッチパネル
Windows Phone のマルチタッチ
Windows Phone 端末は Windows PC や Xbox 360 と異なり、タッチパネルからの入力が標準となります。タッチパネルはマウスと同じように画面上の座標を入力しますが、マルチタッチによる複数のタッチ位置を取得できます。
一部のハードウェアキーボードを搭載した端末や Bluetooth 通信による無線キーボードの接続など、キーボード入力に対応することも可能です。しかし、基本的にハードウェアキーボードは搭載されていないため、キーボード入力を前提としたゲームを作ることはできないでしょう。
タッチパネルの状態を取得するには Microsoft.Xna.Framework.Input.Touch.TouchPanel クラスを用います。
public static class TouchPanel
マウスと異なり、タッチパネルは複数のタッチ位置を入力できます。いわゆるマルチタッチによる複数の指を使った操作が可能です。タッチパネルで感知している複数のタッチ位置は GetState() メソッドから得られます。
public static TouchCollection GetState ()
このメソッドは、現在のタッチパネルの状態を表す Microsoft.Xna.Framework.Input.Touch.TouchCollection 構造体の値を返します。この構造体は入力されているタッチ位置の配列を表します。
public struct TouchCollection : IList<TouchLocation>, ICollection<TouchLocation>, IEnumerable<TouchLocation>, IEnumerable
TouchCollection 構造体は IList<TouchLocation> インタフェースを実装しているので配列のように扱うことができます。また IEnumerable インタフェースを実装しているため、タッチ位置のコレクションとして foreach 文で個々のタッチ位置を取り出すことも可能です。
TouchCollection 構造体が持つ個々のタッチ位置は Microsoft.Xna.Framework.Input.Touch.TouchLocation 構造体の値です。
public struct TouchLocation : IEquatable<TouchLocation>
この構造体の値が 1 つのタッチ位置に相当します。
複数のタッチは Id プロパティによって識別されます。タッチされている座標は Position プロパティから得られます。
public int Id { get; }
public Vector2 Position { get; }
Windows Phone の画面を複数の指でなぞると、上記の GetState() メソッドから得られる TouchCollection 構造体に、指ごとのタッチ位置が含まれています。
using System; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; using Microsoft.Xna.Framework.Input.Touch; public class TestGame : Game { #if WINDOWS || XBOX public static void Main(string[] args) { using (Game game = new TestGame()) game.Run(); } #endif private GraphicsDeviceManager graphicsDeviceManager; private SpriteBatch sprite; private SpriteFont font; private TouchCollection touches; public TestGame() { graphicsDeviceManager = new GraphicsDeviceManager(this); Content.RootDirectory = "Content"; TargetElapsedTime = TimeSpan.FromMilliseconds(33.3333); InactiveSleepTime = TimeSpan.FromMilliseconds(1000); } protected override void LoadContent() { sprite = new SpriteBatch(GraphicsDevice); font = Content.Load<SpriteFont>("TestFont"); base.LoadContent(); } protected override void Update(GameTime gameTime) { touches = TouchPanel.GetState(); base.Update(gameTime); } protected override void Draw(GameTime gameTime) { GraphicsDevice.Clear(Color.White); sprite.Begin(); foreach(TouchLocation touch in touches) { string text = "*ID=" + touch.Id + "\n" + touch.Position; sprite.DrawString(font, text, touch.Position, Color.Black); } sprite.End(); base.Draw(gameTime); } }
コード1はタッチ位置の情報をテキストでリアルタイムに表示します。タッチ位置の ID がどのような数になるかは不定ですが、異なるタッチ位置は必ず異なる ID となります。
タッチ位置には、タッチされた瞬間、タッチを維持している、タッチを離したなどの状態があります。タッチ位置の状態は State プロパティから得られます。
public TouchLocationState State { get; }
このプロパティは、タッチ位置の状態を表す Microsoft.Xna.Framework.Input.Touch.TouchLocationState 列挙体の値を返します。
public enum TouchLocationState
TouchLocationState 列挙体には、以下のメンバが宣言されています。
メンバ | 解説 |
Invalid | 無効なタッチ位置です。 |
Moved | タッチ位置が更新または移動しました。 |
Pressed | 新しいタッチ位置です。 |
Released | タッチ位置が解放されました。 |
Pressed メンバはタッチした瞬間に、Released メンバはタッチを離した瞬間にのみ得られる値で、タッチし続けている間は常に Moved メンバが状態として得られます。
using System; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; using Microsoft.Xna.Framework.Input.Touch; public class TestGame : Game { #if WINDOWS || XBOX public static void Main(string[] args) { using (Game game = new TestGame()) game.Run(); } #endif private GraphicsDeviceManager graphicsDeviceManager; private SpriteBatch sprite; private Texture2D texture; private Rectangle rect; private TouchLocation? currentTouch; public TestGame() { graphicsDeviceManager = new GraphicsDeviceManager(this); Content.RootDirectory = "Content"; TargetElapsedTime = TimeSpan.FromMilliseconds(33.3333); InactiveSleepTime = TimeSpan.FromMilliseconds(1000); } protected override void LoadContent() { sprite = new SpriteBatch(GraphicsDevice); texture = Content.Load<Texture2D>("TestImage"); base.LoadContent(); } protected override void Update(GameTime gameTime) { TouchCollection touches = TouchPanel.GetState(); foreach (TouchLocation touch in touches) { if (currentTouch == null && touch.State == TouchLocationState.Pressed) { currentTouch = touch; rect = new Rectangle((int)touch.Position.X, (int)touch.Position.Y, 0, 0); } else if (currentTouch != null && touch.Id == currentTouch.Value.Id) { if (touch.State == TouchLocationState.Moved) { rect.Width = (int)touch.Position.X - rect.X; rect.Height = (int)touch.Position.Y - rect.Y; } else if (touch.State == TouchLocationState.Released) { currentTouch = null; } } } base.Update(gameTime); } protected override void Draw(GameTime gameTime) { GraphicsDevice.Clear(Color.White); sprite.Begin(); sprite.Draw(texture, rect, Color.White); sprite.End(); base.Draw(gameTime); } }
コード2はタッチパネル上でドラッグすると、タッチ位置に合わせてスプライトの位置とサイズが変化します。
追跡中のタッチ位置がない状態で新しいタッチ位置が検出されると、追跡用のタッチ位置として currentTouch フィールドに保存します。currentTouch フィールドが null でなければ、追跡中のタッチ位置があると判断し、現在のタッチ位置の中から currentTouch フィールドと同じ ID の値を探します。追跡中のタッチ位置の状態が Released メンバになった時点で追跡を終了し、currentTouch フィールドに null を代入します。