10/28のWindows Phoneセミナーでのプログラム
マイクロソフトの田中達彦です。
2011年10月28日(金)のセミナーで作成するゲームを紹介します。
セミナーは3つのセッションで構成されていて、最初のセッションでこのゲームを作ります。
このゲームは、表示される果物をタップして消していくという、もぐらたたきみたいなゲームです。
同じ果物を連続して消すと、得点が上がります。
このゲームの作り方とソースコードです。
[作り方]
1. Windows Phone SDKに含まれるVisual Studioを起動して、XNAのWindows Phoneのプロジェクトを作成します。
2. Windows Phone Apps Art Galleryから果物の画像をダウンロードし、マンゴ、ぶどう、リンゴ、バナナ、オレンジの画像をローカルに保存します。それぞれの名前は、mango.png、grape.png、apple.png、banana.png、orange.pngとします。
3. 画像ファイルのデフォルトの大きさは200x200なので、ツールなどを使用して120x120に縮小します。
4.果物を消すときの効果音として適当なサウンドファイルを、soundというファイル名にして用意します。
5. Visual Studioのソリューション エクスプローラーに表示されている、Contentという名前が最後についている2つめのプロジェクトにこれらの画像ファイルとサウンドファイルを追加します。
6. Contentのプロジェクトにスプライトフォントを2つ追加します。2つめのスプライトフォントの大きさを、少し大きめにしておきます。
7. ソースコードの背景が灰色でない部分を、game1.csにコピペします。灰色の部分は、Visual Studioが自動的に生成するコードです。
8. 実行します。
[ソースコード]
using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Input.Touch;
using Microsoft.Xna.Framework.Media;
namespace WindowsPhoneGame3_mogura
{
/// <summary>
/// 基底 Game クラスから派生した、ゲームのメイン クラスです。
/// </summary>
public class Game1 : Microsoft.Xna.Framework.Game
{
GraphicsDeviceManager graphics;
SpriteBatch spriteBatch;
// 果物の画像データ用のフィールド
private const int MaxFruitImage = 5;
private Texture2D[] FruitImage = new Texture2D[MaxFruitImage];
// 表示する果物用のフィールド
private const int MaxFruits = 8;
private Vector2[] FruitLocation = new Vector2[MaxFruits];
private int[] FruitImageNumber = new int[MaxFruits];
private int[] FruitRestTime = new int[MaxFruits];
// 得点用のフィールド
private int Score;
// 個別の点数の表示用フィールド
private const int MaxEachScore = 12;
private Vector2[] EachScoreLocation = new Vector2[MaxEachScore];
private int[] EachScoreRestTime = new int[MaxEachScore];
private string[] EachScoreText = new string[MaxEachScore];
private Color[] EachScoreColor = new Color[MaxEachScore];
// 逃した果物の数用のフィールド
private int MissedFruits;
// 連続判定用のフィールド
private int PreviousFruit;
private int PreviousScore;
// ゲームの難易度用のフィールド
private int GameLevelFrequency;
private int GameLevelTimeSpan;
private int TotalFruits;
// 文字用のフィールド
private SpriteFont TextFont;
private SpriteFont TextFontLarge;
// 効果音用のフィールド
private SoundEffect HitSound;
// 背景用のフィールド
private Texture2D Background;
// ゲームオーバー用のフィールド
private bool GameOver;
public Game1()
{
graphics = new GraphicsDeviceManager(this);
Content.RootDirectory = "Content";
// Windows Phone のフレーム レートは既定で 30 fps です。
TargetElapsedTime = TimeSpan.FromTicks(333333);
// ロック中のバッテリ寿命を延長する。
InactiveSleepTime = TimeSpan.FromSeconds(1);
}
/// <summary>
/// ゲームの開始前に実行する必要がある初期化を実行できるようにします。
/// ここで、要求されたサービスを問い合わせて、非グラフィック関連のコンテンツを読み込むことができます。
/// base.Initialize を呼び出すと、任意のコンポーネントが列挙され、
/// 初期化もされます。
/// </summary>
protected override void Initialize()
{
// TODO: ここに初期化ロジックを追加します。
int i;
// フィールドの初期化
Score = 0;
GameLevelFrequency = 30;
GameLevelTimeSpan = 60;
TotalFruits = 0;
MissedFruits = 0;
GameOver = false;
PreviousFruit = -1;
PreviousScore = 10;
// 果物を表示している時間を保持しているフィールドの初期化
for(i = 0; i < MaxFruits; i++)
{
FruitRestTime[i] = -1;
}
for (i = 0; i < MaxEachScore; i++)
{
EachScoreRestTime[i] = -1;
}
// タッチを有効化する
TouchPanel.EnabledGestures = GestureType.Tap;
base.Initialize();
}
/// <summary>
/// LoadContent はゲームごとに 1 回呼び出され、ここですべてのコンテンツを
/// 読み込みます。
/// </summary>
protected override void LoadContent()
{
// 新規の SpriteBatch を作成します。これはテクスチャーの描画に使用できます。
spriteBatch = new SpriteBatch(GraphicsDevice);
// TODO: this.Content クラスを使用して、ゲームのコンテンツを読み込みます。
FruitImage[0] = Content.Load<Texture2D>("apple");
FruitImage[1] = Content.Load<Texture2D>("banana");
FruitImage[2] = Content.Load<Texture2D>("grape");
FruitImage[3] = Content.Load<Texture2D>("mango");
FruitImage[4] = Content.Load<Texture2D>("orange");
TextFont = Content.Load<SpriteFont>("SpriteFont1");
TextFontLarge = Content.Load<SpriteFont>("SpriteFont2");
Random rnd = new Random();
string str = "back" + rnd.Next(1, 5);
Background = Content.Load<Texture2D>(str);
HitSound = Content.Load<SoundEffect>("sound");
}
/// <summary>
/// UnloadContent はゲームごとに 1 回呼び出され、ここですべてのコンテンツを
/// アンロードします。
/// </summary>
protected override void UnloadContent()
{
// TODO: ここで ContentManager 以外のすべてのコンテンツをアンロードします。
}
/// <summary>
/// ワールドの更新、衝突判定、入力値の取得、オーディオの再生などの
/// ゲーム ロジックを、実行します。
/// </summary>
/// <param name="gameTime">ゲームの瞬間的なタイミング情報</param>
protected override void Update(GameTime gameTime)
{
// ゲームの終了条件をチェックします。
if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed)
this.Exit();
// TODO: ここにゲームのアップデート ロジックを追加します。
int i;
Random rnd = new Random();
// 表示している個別の点数に対する処理
for (i = 0; i < MaxEachScore; i++)
{
if (EachScoreRestTime[i] >= 0)
{
EachScoreRestTime[i]--;
EachScoreLocation[i].Y -= 2; // 少し上に移動
// 色を少し変える
EachScoreColor[i].A -= 20;
if (EachScoreColor[i].A < 0)
{
EachScoreColor[i].A = 0;
}
}
}
if (GameOver)
{
return; // ゲームオーバーのとき、これ以降の処理を実行しない
}
// 画面がタップされていたとき
while (TouchPanel.IsGestureAvailable)
{
GestureSample gs = TouchPanel.ReadGesture();
if (gs.GestureType == GestureType.Tap)
{
Rectangle r1, r2;
// タッチしている部分を矩形として定義
r1 = new Rectangle((int)gs.Position.X, (int)gs.Position.Y, 1, 1);
for (i = 0; i < MaxFruits; i++)
{
if (FruitRestTime[i] >= 0)
{
// 果物を表示している部分を矩形として定義
r2 = new Rectangle((int)FruitLocation[i].X,
(int)FruitLocation[i].Y,
FruitImage[FruitImageNumber[i]].Width,
FruitImage[FruitImageNumber[i]].Height);
// 衝突判定と得点計算
if (r1.Intersects(r2))
{
if (PreviousFruit == FruitImageNumber[i])
{
// 前回と同じ果物の場合
PreviousScore += 10;
}
else
{
PreviousFruit = FruitImageNumber[i];
PreviousScore = 10;
}
Score += PreviousScore;
// 効果音の再生
HitSound.Play();
// 難易度の変更
TotalFruits++;
if (TotalFruits == 30)
{
GameLevelFrequency = 20;
}
if (TotalFruits == 50)
{
GameLevelTimeSpan = 30;
}
if (TotalFruits == 80)
{
GameLevelFrequency = 15;
GameLevelTimeSpan = 20;
}
// 果物を表示しないようにする
FruitRestTime[i] = -1;
// 点数の表示の処理
for (int j = 0; j < MaxEachScore; j++)
{
if (EachScoreRestTime[j] < 0)
{
EachScoreLocation[j].X = FruitLocation[i].X;
EachScoreLocation[j].Y = FruitLocation[i].Y;
EachScoreRestTime[j] = 10;
EachScoreText[j] = " " + PreviousScore.ToString();
if (PreviousScore > 10)
{
// 連続して同じ果物をタップしているときは
// 緑色の文字で点数を表示する
EachScoreColor[j] = Color.Green;
}
else
{
EachScoreColor[j] = Color.Yellow;
}
break;
}
}
}
}
}
}
}
// 新たな果物を出現させるかどうかの判断
if (rnd.Next(GameLevelFrequency) == 0)
{
// 配列の中で、果物を表示していない要素を探して、
// その中に新しい果物の情報を入れる
for (i = 0; i < MaxFruits; i++)
{
if (FruitRestTime[i] < 0)
{
// 表示させる果物
int fruitNumber = rnd.Next(MaxFruitImage);
FruitImageNumber[i] = fruitNumber;
// 表示させる位置
FruitLocation[i].X = rnd.Next(800 - FruitImage[fruitNumber].Width);
FruitLocation[i].Y = rnd.Next(480 - FruitImage[fruitNumber].Height);
// 表示させる時間
FruitRestTime[i] = GameLevelTimeSpan + rnd.Next(10);
return;
}
}
}
// 表示している果物に対する処理
for (i = 0; i < MaxFruits; i++)
{
if (FruitRestTime[i] >= 0)
{
// 果物を表示する残り時間を減らす
FruitRestTime[i]--;
// 果物を表示する時間が0未満になったとき
if (FruitRestTime[i] < 0)
{
MissedFruits++;
if (MissedFruits >= 10)
{
GameOver = true;
}
// missという文字の表示のための処理
for (int j = 0; j < MaxEachScore; j++)
{
if (EachScoreRestTime[j] < 0)
{
EachScoreLocation[j].X = FruitLocation[i].X;
EachScoreLocation[j].Y = FruitLocation[i].Y;
EachScoreRestTime[j] = 10;
EachScoreText[j] = "miss";
EachScoreColor[j] = Color.Red;
break;
}
}
}
}
}
base.Update(gameTime);
}
/// <summary>
/// ゲームが自身を描画するためのメソッドです。
/// </summary>
/// <param name="gameTime">ゲームの瞬間的なタイミング情報</param>
protected override void Draw(GameTime gameTime)
{
GraphicsDevice.Clear(Color.CornflowerBlue);
// TODO: ここに描画コードを追加します。
int i;
spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.AlphaBlend);
// 背景の描画
spriteBatch.Draw(Background, Vector2.Zero, Color.White);
// 個別の点数の描画
for (i = 0; i < MaxEachScore; i++)
{
if (EachScoreRestTime[i] >= 0)
{
spriteBatch.DrawString(TextFontLarge, EachScoreText[i],
EachScoreLocation[i], EachScoreColor[i]);
}
}
// 果物の描画
for (i = 0; i < MaxFruits; i++)
{
if (FruitRestTime[i] >= 0)
{
if (FruitRestTime[i] > 10)
{
spriteBatch.Draw(FruitImage[FruitImageNumber[i]],
FruitLocation[i], Color.White);
}
else
{
// 果物が消えるまでの時間が10/30秒以下の場合は、
// 果物を少し暗く表示する
spriteBatch.Draw(FruitImage[FruitImageNumber[i]],
FruitLocation[i], Color.DarkGreen);
}
}
}
// 得点の描画
spriteBatch.DrawString(TextFont, "Score : " + Score.ToString() +
" Missed : " + MissedFruits.ToString(),
new Vector2(0, 0), Color.LightBlue);
// ゲームオーバーの表示
if (GameOver)
{
spriteBatch.DrawString(TextFontLarge, "GAME OVER",
new Vector2(270, 200), Color.LightPink);
}
spriteBatch.End();
base.Draw(gameTime);
}
}
}
詳細については、10月28日のセミナーで紹介します。
2つめ、3つめのセッションでは、このゲームをブラッシュアップしていきます。
ライブ配信も実施します。詳細は、下記のWebサイトをご覧ください。
https://www.microsoft.com/japan/powerpro/TF/seminar.mspx
マイクロソフト
田中達彦