次の方法で共有


Windowsストア アプリ 作り方解説 Line Attack編 第2回 ~宝石を動かす~

マイクロソフトの田中達彦です。
本連載では、Windowsストアアプリとして作成したパズルゲームである、Line Attackのプログラムを解説します。
Line Attack : https://apps.microsoft.com/webpdp/app/f11e327c-6228-4c8f-8245-ea57d65e0f09

[注意事項]
- この連載で提供するプロジェクトファイルは、サンプルとして提供しています。
- 毎回の記事で提供するプロジェクトファイルは、その時点でのソースコードです。最終バージョンのソースコードと異なる場合があります。

[今回のプロジェクトファイル]
今回公開するプロジェクトは、宝石を上下左右に動かすところを実装しています。

[画面のクリックやタップの検知]
宝石や十字線をドラッグしてラインを動かすには、マウスでクリックされているか、または画面がタッチされているかを検知して、その情報をもとに描画を行います。
Windows 8では、マウスでクリックしたのか、それともタッチしたのかを意識しなくてもよいように、Pointerというイベントが用意されています。
マウスクリック、画面タッチのどちらの場合も、Pointerのイベントが発生します。

マウスのクリック時、または画面にタッチした瞬間は、PointerPressedというイベントが発生します。
マウスを動かしたとき、またはタッチしている指を動かしているときは、PointerMovedというイベントが発生します。
マウスのボタンを離したとき、または画面から指を離したときにはPointerReleasedというイベントが発生します。

これらのイベントを検知して、それに合わせた描画を行います。

[ラインの描画]
ライン上の宝石や十字線は、Imageコントロールを使用して表示しています。
Imageコントロールには、表示位置を上下左右に指定量だけ動かすプロパティがあります。
RenderTransformというプロパティです。
このRenderTransformに移動するX座標とY座標を入れると、その座標だけ表示する位置をずらすことができるのです。
Line Attackでは、RenderTransformを使用して宝石や十字線を移動したように見せています。

[実際の実装]
今回のプロジェクトで追加しているPointer関連のイベントハンドラーは、以下のように実装されています。
PointerReleasedイベントハンドラーには、まだ何も実装していません。

private void GamePage_PointerPressed(object sender, PointerRoutedEventArgs e)
{
    Point p = e.GetCurrentPoint(MainPanel).Position;

    if(p.X > MainPanel.ActualWidth / 3 && p.X < MainPanel.ActualWidth / 3 * 2 &&
        p.Y > MainPanel.ActualHeight / 3 && p.Y < MainPanel.ActualHeight / 3 * 2)
    {
        PressedPoint = p;

        PressedPieceX = (int)((p.X - MainPanel.ActualWidth / 3) / (MainPanel.ActualWidth / (MaxColumn * 3)));
        PressedPieceY = (int)((p.Y - MainPanel.ActualHeight / 3) / (MainPanel.ActualHeight / (MaxColumn * 3)));

        testText.Text = p.X.ToString() + " / " + p.Y.ToString() + " (" + PressedPieceX.ToString() + " / " + PressedPieceY.ToString() + ") " + MainPanel.ActualWidth.ToString();

        if (PressedPieceX < 0)
            PressedPieceX = 0;
        if (PressedPieceX >= MaxColumn)
            PressedPieceX = MaxColumn - 1;
        if (PressedPieceY < 0)
            PressedPieceY = 0;
        if (PressedPieceY >= MaxColumn)
            PressedPieceY = MaxColumn - 1;
    }

    MoveDirection = 0;
}

private void GamePage_PointerMoved(object sender, PointerRoutedEventArgs e)
{
    Point p = e.GetCurrentPoint(MainPanel).Position;
    Point delta;
    int i;

    delta.X = p.X - PressedPoint.X;
    delta.Y = p.Y - PressedPoint.Y;

    if(MoveDirection == 0)
    {
        if(Math.Abs(delta.X) > Math.Abs(delta.Y))
            MoveDirection = 1; // horizontal
        else
            MoveDirection = 2; // virtical
    }

    if (MoveDirection == 1)
    {
        CompositeTransform ct = new CompositeTransform();
        ct.TranslateX = delta.X;

        for (i = 0; i < MaxColumn * 3; i++)
        {
            Piece[i, PressedPieceY + MaxColumn].RenderTransform = ct;
        }
    }
    else
    {
        CompositeTransform ct = new CompositeTransform();
        ct.TranslateY = delta.Y;

        for (i = 0; i < MaxColumn * 3; i++)
        {
            Piece[PressedPieceX + MaxColumn, i].RenderTransform = ct;
        }
    }
}

private void GamePage_PointerReleased(object sender, PointerRoutedEventArgs e)
{

}

これらのPointer系のイベントハンドラーでは、引数eから以下の方法で座標を取得できます。

Point p = e.GetCurrentPoint(MainPanel).Position;

ここでの座標は、MainPanelというモニターの大きさよりもおおきなGridコントロール上での座標として取得しています。
MainPanelについては、第1回の記事をご参照ください。

PointerPressedイベントハンドラーでは、マウスでクリックされた座標または画面をタッチした時の座標をPressedPointというフィールドに代入しています。
PressedPieceXとPressedPieceYには、どの場所の宝石または十字線が押されたかを、以下のような0~5の数値に変換して入れています。

PointerPressedイベントハンドラーの最後では、MoveDirectionというフィールドを初期化しています。
このMoveDirectionは、ドラッグしている宝石を縦方向に動しているのか、それとも横方向に動しているのかの情報を入れます。
1が代入されているときには横方向、2が代入されているときには縦方向に動かしています。

PointerMovedイベントハンドラーの中では、ユーザーが横方向に動かそうとしたのか、または縦方向に動かそうとしたのかを判別しています。
現段階では、ポインターの横方向の移動量が縦方向の移動量よりも大きければラインを横方向に動かし、そうでなければラインを縦方向に動かすように設定しています。
この部分は、後ほどマウスでのクリックに対応させるべく少し調整しています。
なぜならば、マウスのボタンを押した瞬間は、少しマウスカーソルが下方向に移動してしまうことがあり、現段階のコードではユーザーが横方向に動かそうとしても、縦方向への移動と認識することがあるためです。

deltaフィールドには、クリック/タッチしたときの起点からの距離が入ります。
この距離によって、移動量を決めています。

MoveDirectionの値によって、RenderTransformにX方向の移動量のみを代入するか、Y方向の移動量のみを代入するかを判定しています。
RenderTransformに移動量を代入するときは、CompositeTransformを使用します。

今日のプロジェクトを実行すると、マウスやタッチの動きに応じてラインが動くことが確認できます。
ただ、PointerReleasedイベントハンドラーを実装していないため、単にラインが動くだけのアプリとなっています。
ここまで実装している今回のプロジェクトを実行すると、このようになります。

[前後の記事]
第1回 画面のレイアウトと表示
第3回 宝石をちゃんと止める

マイクロソフト
田中達彦

 

Pazzle_idea1_201301161750_move.zip