Touch operation in game development on Windows Phone 7 Part 4 Pinch

Page updated :

Programming! - 4. Scaling sprites by pinching and stretching

About this sample

Modern smartphones allow you to zoom in and out of an image by widening or closing the distance between your fingers using two fingers, such as the image viewer. You can do the same with Internet Explorer on Windows Phone 7. This is called pinch, stretch ing in Windows Phone 7.

In addition to pinch and stretch operations, there are operations such as "tap" to touch one point of the screen for a short time and "flick" to trace the screen quickly. These operations are collectively referred to as gestures.

In this program, I want to zoom in and out of the sprite displayed on the screen by this pinch and stretch operation. In addition, until the last time, i was doing processing by obtaining the information of the touch panel directly, but this time I want to handle the touch operation using the method dedicated to gestures.

The goal of this sample program

Pinch and stretch to zoom in and out of the sprite.

図 1 :ストレッチでスプライトを拡大
Figure 1: Stretch ing sprites

Program - Declaring fields

Although there are no special new classes in field declarations, we try to have "texture center coordinates," "sprite magnification," and "the distance between the last two touch positions when you pinch operation."

/// <summary>
/// テクスチャー
/// </summary>
Texture2D texture;

/// <summary>
/// テクスチャーの中心座標
/// </summary>
Vector2 textureCenterPos;

/// <summary>
/// スプライトのスケール
/// </summary>
float scale = 1;

/// <summary>
/// 前回の 2 点間の距離
/// </summary>
float previousLength = 0;

Program - Enable gestures

We're doing gesture-only, but by default, you can't use any gestures. To get each gesture information, you must set the gesture to be used for the TouchPanel.EnabledGestures property.

// タッチパネルでピンチのジェスチャーを有効にする
TouchPanel.EnabledGestures = GestureType.Pinch | GestureType.PinchComplete;

Enabling all gestures affects performance, so you should only set the gestures that you want to use. GestureType.Pinch is set to obtain information for pinch operations and GestureType.PinchComplete, which indicates that pinch processing is complete. The setting location is set in the Game constructor.

Program - Loading textures

The texture loading is no different than it used to be, but this time we are calculating the center coordinates of the texture. This is to scale the center coordinate sit as the origin when the sprite is scaled.

// テクスチャーをコンテンツパイプラインから読み込む
texture = Content.Load<Texture2D>("Texture");

// テクスチャーの中心座標を求める
textureCenterPos = new Vector2(texture.Width / 2, texture.Height / 2);

Program - Get gesture information

We are getting gesture information (pinch here) in the Game.Update method. Gesture processing is a format that repeats as many gestures as you have enabled. We do not use the "TouchCollection" structure that we used until last time.

// 次のジェスチャー情報が取得できる場合は true を返し続けます
while (TouchPanel.IsGestureAvailable)
{
  // 次のジェスチャー情報を読み込みます
  GestureSample gs = TouchPanel.ReadGesture();
  
  switch (gs.GestureType)
  {
    case GestureType.Pinch:
      // ここにピンチ処理を記述します
      break;
    case GestureType.PinchComplete:
      // ここにピンチ完了処理を記述します
      break;
  }
}

Gesture information checks the status of the TouchPanel.IsGestureAvailable property in the while loop to see if the following gesture information exists: If you have the following gesture information, use the TouchPanel.ReadGesture method to retrieve the information for the gesture.

GestureSample.GestureType contains what gesture type of information is stored, so the processing is branched with a switch statement. In this sample, the "TouchPanel.EnabledGestures" property in the constructor is branched, respectively, because i set the GestureType.Pinch and GestureType.PinchComplete enumerations.

Program - Handling Pinch Gestures

It's faster to see the code comments on what you're doing, but if you summarize what you're doing, you'll find the difference between the touch point distance between the last two points and the distance between the touch points between the last two points, and you're increasing or reducing the scale value based on the difference.

case GestureType.Pinch:
  // 現在の2点間の距離を求めます
  float nowLength = (gs.Position - gs.Position2).Length();

  if (previousLength == 0)
  {
    // 前回の2点間の距離が 0 の場合は
    // 現在の2点間の距離を設定します
    // 初回はスケール計算を行いません
    previousLength = nowLength;
  }
  else
  {
    // 前回の2点間の距離との差分を計算します
    float diffLength = nowLength - previousLength;
    
    // ピンチ、ストレッチした距離に応じて
    // スケールを変化させています
    // 補正値は適当に設定します
    scale = MathHelper.Max(scale + diffLength / 150, 0);
    
    // 今回の距離を次の計算のために保持します
    previousLength = nowLength;
  }
  break;

You can get the two touch positions in GestureSample.Position and GestureSample.Position2, respectively. You can find the difference between the two retrieved vectors by subtracting and calling the Vector2.Length method to determine the distance between the two points.

By the way, since the previous distance is defined as 0 when the pinch processing is not done here, the processing is branched in the loop after the second time and the time of the pinch processing. This is because there is no need to scale the first time of pinch processing because there is no previous distance.

In addition, we use only two properties, GestureSample.Position and GestureSample.Position2, but there are also gesturesample.Delta and GestureSample.Delta2 properties, which allow you to obtain difference information from the previous touch position. Two properties are provided, each for multi-touch purposes, and for gestures that use only single touch, you will use a property that does not have a "2".

Program - What to do when pinch gestures are completed

When the pinch gesture is complete (when one of your fingers is released from the touch panel), the distance between the previous two points is returned to 0. Originally, it might be better to use a separate flag, but it is physically impossible to touch the same position with two fingers, so we define it as not pinching for distance 0. (If the resolution is low, it may be possible to touch the same position.)

case GestureType.PinchComplete:
  // ピンチが終了した場合は保持している距離を 0 に戻して
  // 完了したことにします
  previousLength = 0;
  break;

Program - Drawing sprites

I don't go into much detail because it's just drawing sprites, but I'm not going to go into the details, but I'm placing the sprite in the center of the screen and zooming in and drawing the calculated scale value from the center of the sprite as the origin.

// スプライトの描画準備
spriteBatch.Begin();

// スプライトを描画する
spriteBatch.Draw(texture,
                 new Vector2(graphics.PreferredBackBufferWidth / 2,
                             graphics.PreferredBackBufferHeight / 2),
                 null,
                 Color.White,
                 0.0f,
                 textureCenterPos,
                 scale,
                 SpriteEffects.None,
                 0.0f);

// スプライトの一括描画
spriteBatch.End();

Summary of this sample

In this time, i explained the processing only for gestures. Samples only create pinch-action, but there are other things like Flick and Hold. For more information about what gestures are available, check out the XNA Game Studio 4.0 help for gesturetype enumerations.

You can implement the same from touch information obtained from touch panel.GetState method without using gesture-only methods or structures, but in that case you will need to calculate the speed of multi-touch ID management, touch time, flick, etc. on your own. You can also simplify these implementations by using gesture-specific methods, and you can interact with any game or application in the same way.

When you create your own touch panel processing, it is recommended that you use this if similar processing can be substituted as a gesture.

Programming! - 5. Rotate sprites by pinching

About this sample

Normal pinchoperation refers to "pinch" and "stretch", but xna Game Studio 4.0's pinch processing is not particularly limited to two, so you can do something around one touch point with another touch point.

I want to rotate the sprite by the operation method here. By the way, no new methods or classes appeared this time, and it is applied based on the previous scaling.

The sample code description of this sample omits the same part as the previous scaling sample.

The goal of this sample program

The sprite rotates by moving it to rotate the two touch points. The previous scaling process also works at the same time.

図 2 :タッチポイントを回してスプライトを回転させています
Figure 2: Rotating the sprite by turning the touch point

Program - Declaring fields

We have added sprite rotation and last rotation angle to the previous scaling program. All angles are calculated in radians.

/// <summary>
/// スプライトの回転量(radian)
/// </summary>
float rotate;

/// <summary>
/// 前回の回転角度(radian)
/// </summary>
float previousAngle;

Program - Rotation Process

The rotation process is done in the pinch gesture as it does when scaling.

I won't go into too much detail about calculating rotation because it's a math story, but you can get the angle with radian with the Math.Atan2 method for the difference vector from touch point 1 to touch point 2. Finds the difference between the angle it got and the angle it got last time, and adds it to the sprite's rotation angle.

switch (gs.GestureType)
{
  case GestureType.Pinch:
    //===== スケール計算 =====//
    // 前回と同じなので省略

    //===== 回転計算 =====//

    // 2点間のベクトルを求めます
    Vector2 pinchVector = gs.Position2 - gs.Position;

    // Atan2 で角度を求めます
    float nowAngle = (float)Math.Atan2(pinchVector.Y,
                                       pinchVector.X);

    if (previousAngle == 0)
    {
      // 前回の角度が 0 の場合は
      // 現在の角度を設定します
      // 初回は回転計算を行いません
      previousAngle = nowAngle;
    }
    else
    {
      // 前回の角度との差分をスプライトの回転角度に加算します
      rotate += nowAngle - previousAngle;

      // 今回の距離を次の計算のために保持します
      previousAngle = nowAngle;
    }
    break;
  case GestureType.PinchComplete:
    // ピンチが終了した場合は保持している距離、角度を 0 に戻して
    // 完了したことにします
    previousLength = 0;
    previousAngle = 0;
    break;
}

Program - Drawing sprites

There are no major changes to the drawing of sprites. Sets the calculated rotation angle for the fifth argument of the SpriteBacth.Draw method.

// スプライトの描画準備
spriteBatch.Begin();

// スプライトを描画する
spriteBatch.Draw(texture,
                 new Vector2(graphics.PreferredBackBufferWidth / 2,
                             graphics.PreferredBackBufferHeight / 2),
                 null,
                 Color.White,
                 rotate,
                 textureCenterPos,
                 scale,
                 SpriteEffects.None,
                 0.0f);

// スプライトの一括描画
spriteBatch.End();

Summary of this sample

This time I tried to rotate the sprite by pinch operation. We don't use any new classes in particular, but you know that we can apply them based on the features provided.

At last

We covered the sample and discussed implementing touch interactions using Windows Phone 7 and XNA Game Studio 4.0. The contents were single-touch, multi-touch touch information acquisition and manipulation, and touch gesture manipulation using gesture-specific methods.

This time, i've focused on the touch panel and explained it, but there are some features that have not yet been introduced. Windows Phone 7 also includes accelerometer input and voice input as a new input feature in XNA Game Studio 4.0. There are also interesting features in the features that could not be introduced this time, please enjoy to examine the features that you want to use each one.