WisdomSoft - for your serial experiences.

タッチパネル

Windows Phone ゲームの入力はタッチパネルによるマルチタッチが中心となります。タッチパネルから、複数のタッチ位置を取得する方法を解説します。

Windows Phone のマルチタッチ

Windows Phone 端末は Windows PC や Xbox 360 と異なり、タッチパネルからの入力が標準となります。タッチパネルはマウスと同じように画面上の座標を入力しますが、マルチタッチによる複数のタッチ位置を取得できます。

一部のハードウェアキーボードを搭載した端末や Bluetooth 通信による無線キーボードの接続など、キーボード入力に対応することも可能です。しかし、基本的にハードウェアキーボードは搭載されていないため、キーボード入力を前提としたゲームを作ることはできないでしょう。

タッチパネルの状態を取得するには Microsoft.Xna.Framework.Input.Touch.TouchPanel クラスを用います。

Microsoft.Xna.Framework.Input.Touch.TouchPanel クラス
public static class TouchPanel

マウスと異なり、タッチパネルは複数のタッチ位置を入力できます。いわゆるマルチタッチによる複数の指を使った操作が可能です。タッチパネルで感知している複数のタッチ位置は GetState() メソッドから得られます。

Touch クラス GetState() メソッド
public static TouchCollection GetState ()

このメソッドは、現在のタッチパネルの状態を表す Microsoft.Xna.Framework.Input.Touch.TouchCollection 構造体の値を返します。この構造体は入力されているタッチ位置の配列を表します。

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 構造体の値です。

Microsoft.Xna.Framework.Input.Touch.TouchLocation 構造体
public struct TouchLocation : IEquatable<TouchLocation>

この構造体の値が 1 つのタッチ位置に相当します。

複数のタッチは Id プロパティによって識別されます。タッチされている座標は Position プロパティから得られます。

TouchLocation 構造体 Id プロパティ
public int Id { get; }
TouchLocation 構造体 Position プロパティ
public Vector2 Position { get; }

Windows Phone の画面を複数の指でなぞると、上記の GetState() メソッドから得られる TouchCollection 構造体に、指ごとのタッチ位置が含まれています。

コード1
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 実行結果

コード1はタッチ位置の情報をテキストでリアルタイムに表示します。タッチ位置の ID がどのような数になるかは不定ですが、異なるタッチ位置は必ず異なる ID となります。

タッチ位置には、タッチされた瞬間、タッチを維持している、タッチを離したなどの状態があります。タッチ位置の状態は State プロパティから得られます。

TouchLocation 構造体 State プロパティ
public TouchLocationState State { get; }

このプロパティは、タッチ位置の状態を表す Microsoft.Xna.Framework.Input.Touch.TouchLocationState 列挙体の値を返します。

Microsoft.Xna.Framework.Input.Touch.TouchLocationState 列挙体
public enum TouchLocationState

TouchLocationState 列挙体には、以下のメンバが宣言されています。

表1 TouchLocationState 列挙体
メンバ 解説
Invalid 無効なタッチ位置です。
Moved タッチ位置が更新または移動しました。
Pressed 新しいタッチ位置です。
Released タッチ位置が解放されました。

Pressed メンバはタッチした瞬間に、Released メンバはタッチを離した瞬間にのみ得られる値で、タッチし続けている間は常に Moved メンバが状態として得られます。

コード2
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 を代入します。