traP Member's Blog

MonoGameを使ってAndroidのゲームを作ろう その2

Namazu
このエントリーをはてなブックマークに追加

どうも、Namazuです。

前回の記事はこちら。まさか続くとは。
ということで、今回扱う内容は以下の通りです。

  • タッチ入力

これだけ?と思うかもしれませんが、意外と構造が面倒くさいのです。
具体的な実装の話の前に、関連するクラスの説明から。

  • TouchPanel
    タッチ パネル デバイスの情報を取得するためのメソッドを提供します。
  • TouchCollection
    タッチ対応デバイスのタッチスクリーンのステート情報にアクセスするためのメソッドとプロパティを提供します。
  • TouchLocation
    タッチスクリーン デバイス上のタッチ位置と情報をやり取りするためのメソッドとプロパティを提供します。

(MSDN公式サイトより引用)

よく分からないので、少しずつ整理していきます。

まず、私たちが欲しい情報は

  • 今何ヵ所タッチされているか
  • タッチされている座標はどこか
  • タッチしている指がどのように動いたか

などですね。
そこである一ヶ所のタッチについての情報をまとめているのがTouchLocationです。
しかし複数タッチに対応するためには複数のTouchLocationが必要です。
そこで、複数のTouchLocationをまとめたものがTouchCollectionです。
そして、タッチ情報は毎フレーム更新しなくては意味がありません。
画面からタッチ情報を受け取り、TouchCollectionを更新するために用いるのがTouchPanelです。

それではそれぞれのクラスの主要なメンバやメソッドを具体的な実装方法を交えてご紹介します。

  • TouchPanelクラス
    GetState:画面で検出したタッチ情報を返す。返り値の型はTouchCollection。
//TouchPanelクラス
protected override void Update(GameTime gameTime)
 {
 if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed)
 Exit();

 touchCollection = TouchPanel.GetState();
 //Update関数の中で記述する。
 //TouchPanelは、Input.Touchクラスをインポート(using Microsoft.Xna.Framework.Input.Touch;)としておけば定義しなくてよい。

 base.Update(gameTime);
 }
  • TouchCollectionクラス
    Count:現在のタッチ入力の数を返す。返り値の型は整数(int)型。
    TouchCollectionはTouchLocationの配列を拡張したクラスであるため、以下のような使い方ができる。
  • TouchLocationクラス
    Position:現在のタッチ位置の座標を返す。返り値の型はVector2。
    Id:現在のタッチ位置のIDを返す。タッチを始めてからやめるまで、同じ指によるタッチ位置には同じIDが入る。返り値の型は整数(int)型。
    State:タッチの状態を返す。タッチ開始時はPressed、継続時はMoved、終了時はReleased、その他無効時にはInvalidを返す。返り値の型はenum型。
//TouchCollectionクラス、TouchLocationクラス
protected override void Draw(GameTime gameTime)
 {
 for(var i=0;i<touchCollection.Count;i++){
  TouchLocation touchLocation = touchCollection[i]; //これでタッチID昇順のi番目のTouchLocationが取得できる。
  Point point = new Point((int)touchLocation.Position.X,(int)touchLocation.Position.Y); //Pointの中身はint型、Vector2の中身はfloat型
  if(ayayaRect.Contains(point)) {}//ayayaRectはRectangleクラスのオブジェクト。Contains(point)でその領域にpointが含まれているか判定できる。
 }

 base.Update(gameTime);
 }

タッチ入力を検出する流れは分かっていただけたでしょうか?
それでは、タッチしたら画面の背景色が変わるゲーム(?)を作ってみましょう。

Activity1.csは変更せずに、Game1.csを以下のように変更します。
Game1.cs

using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Input.Touch;
using System;

namespace TouchOnly
{
 /// <summary>
 /// This is the main type for your game.
 /// </summary>
 public class Game1 : Game
 {
  GraphicsDeviceManager graphics;
  SpriteBatch spriteBatch;

  Texture2D ayaya, yoko, alice, karen;
  Vector2 touchPosition;
  TouchCollection touchCollection;
  Rectangle ayayarect = new Rectangle(300, 200, 200, 200);
  Rectangle alicerect = new Rectangle(700, 200, 200, 200);
  Rectangle yokorect = new Rectangle(300, 500, 200, 200);
  Rectangle karenrect = new Rectangle(700, 500, 200, 200);

  Color backGroundColor = Color.CornflowerBlue;
  string[] text;

  public Game1()
  {
   graphics = new GraphicsDeviceManager(this);
   Content.RootDirectory = "Content";

   graphics.IsFullScreen = true;
   graphics.PreferredBackBufferWidth = 800;
   graphics.PreferredBackBufferHeight = 480;
   graphics.SupportedOrientations = DisplayOrientation.LandscapeLeft | DisplayOrientation.LandscapeRight;
  }

  /// <summary>
  /// Allows the game to perform any initialization it needs to before starting to run.
  /// This is where it can query for any required services and load any non-graphic
  /// related content. Calling base.Initialize will enumerate through any components
  /// and initialize them as well.
  /// </summary>
  protected override void Initialize()
  {
   // TODO: Add your initialization logic here

   base.Initialize();
  }

  /// <summary>
  /// LoadContent will be called once per game and is the place to load
  /// all of your content.
  /// </summary>
  protected override void LoadContent()
  {
   // Create a new SpriteBatch, which can be used to draw textures.
   spriteBatch = new SpriteBatch(GraphicsDevice);
   ayaya = Content.Load<Texture2D>("ayaya2");
   alice = Content.Load<Texture2D>("alice");
   karen = Content.Load<Texture2D>("karen");
   yoko = Content.Load<Texture2D>("yoko");

   // TODO: use this.Content to load your game content here
  }

  /// <summary>
  /// UnloadContent will be called once per game and is the place to unload
  /// game-specific content.
  /// </summary>
  protected override void UnloadContent()
  {
   // TODO: Unload any non ContentManager content here
  }

  /// <summary>
  /// Allows the game to run logic such as updating the world,
  /// checking for collisions, gathering input, and playing audio.
  /// </summary>
  /// <param name="gameTime">Provides a snapshot of timing values.</param>
  protected override void Update(GameTime gameTime)
  {
   if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed)
   Exit();

   // TODO: Add your update logic here

   touchCollection = TouchPanel.GetState();

   base.Update(gameTime);
  }

  /// <summary>
  /// This is called when the game should draw itself.
  /// </summary>
  /// <param name="gameTime">Provides a snapshot of timing values.</param>
  protected override void Draw(GameTime gameTime)
  {
   text = new string[touchCollection.Count];
   for (var i = 0; i < touchCollection.Count; i++)
   {

    TouchLocation touchLocation = touchCollection[i];
    touchPosition = touchLocation.Position;
    Point point = new Point((int)touchPosition.X, (int)touchPosition.Y);
    if (ayayarect.Contains(point))
    {
     backGroundColor = Color.RoyalBlue;
    }
    else if (alicerect.Contains(point))
    {
     backGroundColor = Color.Gold;
    }
    else if (yokorect.Contains(point))
    {
     backGroundColor = Color.Orange;
    }
    else if (karenrect.Contains(point))
    {
     backGroundColor = Color.Yellow;
    }
    else
    {
     backGroundColor = Color.CornflowerBlue;
    }
    text[i] = string.Format("id={0} X={1} Y={2} state={3} touch count={4}", touchLocation.Id, touchLocation.Position.X, touchLocation.Position.Y, touchLocation.State, touchCollection.Count);
    Console.WriteLine(text[i]);
   }

   GraphicsDevice.Clear(backGroundColor);

   spriteBatch.Begin();
   spriteBatch.Draw(ayaya, ayayarect, Color.White);
   spriteBatch.Draw(alice, alicerect, Color.White);
   spriteBatch.Draw(yoko, yokorect, Color.White);
   spriteBatch.Draw(karen, karenrect, Color.White);
   spriteBatch.End();

   base.Draw(gameTime);
  }
 }
}

簡単に説明します。

  • namespace
    日本語で名前空間というのですが…とりあえず、作成したプロジェクト名と同一にしましょう。
    今回私はTouchOnlyというプロジェクト名にしたので、namespace TouchOnlyとなっています。
  • Rectangle
    領域を指定できます。
    Textureの表示やPointの包含判定(後述)にも使えるので便利です。
  • Point
    座標を指定できます。
  • Rectangle.Contains(point)
    作成したRectangleインスタンスの指定する領域の中にPointで指定した座標が含まれているかを判定します。
    返り値はboolです。
  • Console.WriteLine(string)
    VisualStudioの実行中に表示されるOutputに一行の文字列を表示します。
    アプリ側には影響しないので、デバッグ用に重宝します。
  • Textureの表示
    前回記事の内容になります。
    同様にContent.mgcbを用いて追加してください。

何か説明に不備または分かりにくい点等ありましたらコメントにてご指摘ください。

さて、ここからが重要ですがきんいろモザイクの映画「きんいろモザイク PrettyDays」が2016年11月12日に公開されますね。
今回の記事であややを始めとするきんいろモザイクのキャラの可愛さが伝わったと思うので是非見に行きましょう。
私はもう前売り券をゲットしました。特典ブロマイドはあややでした。完全に勝利です。

このエントリーをはてなブックマークに追加

コメントを残す

メールアドレスが公開されることはありません。