STEP.01 アニメーションスプライト
このチュートリアルの完成イメージ

概要・解説
2Dチャットを作成する過程をまとめてみようと思います。基本的な構成として、 クライアント側に XNA 2.0 を使用してサーバー側では Java を使用する予定です。
一番初めのチュートリアルとして、カーソルキーによりアニメーションをしながら移動 するキャラクタを作成してみます。 なるべくソースコードにコメントを残すように心掛けていきますが分かりにくい部分などありましたらご連絡ください。 また、不具合や改善案等ありましたらよろしくお願いします(^^;
ソースコードを記載していますが、コーディングが面倒とかすぐに試してみたい方はこの ページの一番下にソリューションファイルを用意しておきましたのでダウンロードして下さい。
これをベースにどんどん更新をして行こうと思うのでよろしくお願いします!!
注意事項
このチュートリアルは、REFMAP様が配布しているフリー画像素材を使用してます。
このソフト内で使用されている画像を、このゲームを遊ぶ以外の用途には使用しないで下さい。
※ 詳細は First Seed Material をご確認ください。
※ 詳細は First Seed Material をご確認ください。
用意するもの
-
Visual Studio 2005 Express Edition
(VS2008 もありますが、VS2005 で問題ないと思います。) -
XNA Game Studio 2.0
(XNA を利用するために必要です。) -
Paint.NET
(bmp ファイルから dds ファイルに変換するために使用しています。) -
First Seed Material
(2Dキャラクタ素材)
ソリューション設定

ソースコード
Program.cs
using System;
using System.Collections.Generic;
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.Net;
using Microsoft.Xna.Framework.Storage;
using Sample2DRPG.Charactor;
namespace Sample2DRPG
{
/// <summary>
/// 簡略化のため Program.cs に Game クラスを記述
/// </summary>
public class GameMain : Microsoft.Xna.Framework.Game
{
GraphicsDeviceManager graphics;
SpriteBatch spriteBatch;
// キャラクタイメージ
Texture2D texture01;
// キャラクタ定義
Chara chara01;
// プレイヤー情報
Player player01;
public GameMain()
{
graphics = new GraphicsDeviceManager(this);
Content.RootDirectory = "Content";
// 画面サイズ
graphics.PreferredBackBufferWidth = 320;
graphics.PreferredBackBufferHeight = 240;
}
/// <summary>
/// 初期化
/// </summary>
protected override void Initialize()
{
base.Initialize();
}
/// <summary>
/// リソース読み込み
/// </summary>
protected override void LoadContent()
{
spriteBatch = new SpriteBatch(GraphicsDevice);
// キャラクタイメージを取得
texture01 = Content.Load<Texture2D>("chara01");
// キャラクタ定義を生成
chara01 = new Chara(texture01);
// プレイヤーを生成
player01 = new Player(chara01, new Vector2(158, 100));
}
/// <summary>
/// リソース破棄
/// </summary>
protected override void UnloadContent()
{
}
/// <summary>
/// 更新処理
/// </summary>
/// <param name="gameTime"></param>
protected override void Update(GameTime gameTime)
{
// キー入力状況を取得
KeyboardState keyState = Keyboard.GetState();
// エスケープ押下で終了
if (keyState.IsKeyDown(Keys.Escape)) Exit();
// カーソルキー判定によりアクションを設定
if (keyState.IsKeyDown(Keys.Up))
{
// アクション:上に移動
player01.doAction(Chara.MOVE_UP);
}
else if (keyState.IsKeyDown(Keys.Right))
{
// アクション:右に移動
player01.doAction(Chara.MOVE_RIGHT);
}
else if (keyState.IsKeyDown(Keys.Down))
{
// アクション:下に移動
player01.doAction(Chara.MOVE_DOWN);
}
else if (keyState.IsKeyDown(Keys.Left))
{
// アクション:左に移動
player01.doAction(Chara.MOVE_LEFT);
}
else
{
// アクションを停止
player01.stopAction();
}
// プレイヤー情報を更新
player01.Update(gameTime);
// ちょっとした情報をタイトルに表示
Window.Title = "player01 " + player01.ToString();
base.Update(gameTime);
}
/// <summary>
/// 描画処理
/// </summary>
/// <param name="gameTime"></param>
protected override void Draw(GameTime gameTime)
{
graphics.GraphicsDevice.Clear(Color.CornflowerBlue);
spriteBatch.Begin();
// プレイヤー情報を描画
player01.Draw(spriteBatch);
spriteBatch.End();
base.Draw(gameTime);
}
}
static class Program
{
/// <summary>
/// The main entry point for the application.
/// </summary>
static void Main(string[] args)
{
using (GameMain game = new GameMain())
{
game.Run();
}
}
}
}
using System.Collections.Generic;
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.Net;
using Microsoft.Xna.Framework.Storage;
using Sample2DRPG.Charactor;
namespace Sample2DRPG
{
/// <summary>
/// 簡略化のため Program.cs に Game クラスを記述
/// </summary>
public class GameMain : Microsoft.Xna.Framework.Game
{
GraphicsDeviceManager graphics;
SpriteBatch spriteBatch;
// キャラクタイメージ
Texture2D texture01;
// キャラクタ定義
Chara chara01;
// プレイヤー情報
Player player01;
public GameMain()
{
graphics = new GraphicsDeviceManager(this);
Content.RootDirectory = "Content";
// 画面サイズ
graphics.PreferredBackBufferWidth = 320;
graphics.PreferredBackBufferHeight = 240;
}
/// <summary>
/// 初期化
/// </summary>
protected override void Initialize()
{
base.Initialize();
}
/// <summary>
/// リソース読み込み
/// </summary>
protected override void LoadContent()
{
spriteBatch = new SpriteBatch(GraphicsDevice);
// キャラクタイメージを取得
texture01 = Content.Load<Texture2D>("chara01");
// キャラクタ定義を生成
chara01 = new Chara(texture01);
// プレイヤーを生成
player01 = new Player(chara01, new Vector2(158, 100));
}
/// <summary>
/// リソース破棄
/// </summary>
protected override void UnloadContent()
{
}
/// <summary>
/// 更新処理
/// </summary>
/// <param name="gameTime"></param>
protected override void Update(GameTime gameTime)
{
// キー入力状況を取得
KeyboardState keyState = Keyboard.GetState();
// エスケープ押下で終了
if (keyState.IsKeyDown(Keys.Escape)) Exit();
// カーソルキー判定によりアクションを設定
if (keyState.IsKeyDown(Keys.Up))
{
// アクション:上に移動
player01.doAction(Chara.MOVE_UP);
}
else if (keyState.IsKeyDown(Keys.Right))
{
// アクション:右に移動
player01.doAction(Chara.MOVE_RIGHT);
}
else if (keyState.IsKeyDown(Keys.Down))
{
// アクション:下に移動
player01.doAction(Chara.MOVE_DOWN);
}
else if (keyState.IsKeyDown(Keys.Left))
{
// アクション:左に移動
player01.doAction(Chara.MOVE_LEFT);
}
else
{
// アクションを停止
player01.stopAction();
}
// プレイヤー情報を更新
player01.Update(gameTime);
// ちょっとした情報をタイトルに表示
Window.Title = "player01 " + player01.ToString();
base.Update(gameTime);
}
/// <summary>
/// 描画処理
/// </summary>
/// <param name="gameTime"></param>
protected override void Draw(GameTime gameTime)
{
graphics.GraphicsDevice.Clear(Color.CornflowerBlue);
spriteBatch.Begin();
// プレイヤー情報を描画
player01.Draw(spriteBatch);
spriteBatch.End();
base.Draw(gameTime);
}
}
static class Program
{
/// <summary>
/// The main entry point for the application.
/// </summary>
static void Main(string[] args)
{
using (GameMain game = new GameMain())
{
game.Run();
}
}
}
}
Animation.cs
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Net;
using Microsoft.Xna.Framework.Storage;
namespace Sample2DRPG.Charactor
{
/// <summary>
/// キャラクタのアニメーション定義
/// </summary>
class Animation
{
// 次のフレームか判定
internal bool isNextFrame(int current)
{
return (timing < current);
}
// 繰り返しアニメーションか判定
internal bool isLoop = true;
// 描画範囲
internal Rectangle rect;
// 次のアニメーション
internal Animation next;
// 次のアニメーションまでの時間(ミリ秒)
internal int timing = 125;
}
}
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Net;
using Microsoft.Xna.Framework.Storage;
namespace Sample2DRPG.Charactor
{
/// <summary>
/// キャラクタのアニメーション定義
/// </summary>
class Animation
{
// 次のフレームか判定
internal bool isNextFrame(int current)
{
return (timing < current);
}
// 繰り返しアニメーションか判定
internal bool isLoop = true;
// 描画範囲
internal Rectangle rect;
// 次のアニメーション
internal Animation next;
// 次のアニメーションまでの時間(ミリ秒)
internal int timing = 125;
}
}
Chara.cs
using System.Collections.Generic;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Net;
using Microsoft.Xna.Framework.Storage;
namespace Sample2DRPG.Charactor
{
/// <summary>
/// 複数のプレイヤーで共有するキャラクタ定義
/// </summary>
class Chara
{
// アクション定義
public static readonly int MOVE_UP = 0;
public static readonly int MOVE_RIGHT = 1;
public static readonly int MOVE_DOWN = 2;
public static readonly int MOVE_LEFT = 3;
// MOVE_UP
static private Animation[] up = new Animation[4];
// MOVE_RIGHT
static private Animation[] right = new Animation[4];
// MOVE_DOWN
static private Animation[] down = new Animation[4];
// MOVE_LEFT
static private Animation[] left = new Animation[4];
/// <summary>
/// コンストラクタ
/// </summary>
public Chara(Texture2D texture)
{
this.texture = texture;
Initialize();
}
/// <summary>
/// 初期化処理
/// </summary>
private void Initialize()
{
if (animd != null) return;
animd = new Dictionary<int, Animation[]>();
// MOVE_UP
InitializeMove(up, MOVE_UP);
animd.Add(MOVE_UP, up);
// MOVE_RIGHT
InitializeMove(right, MOVE_RIGHT);
animd.Add(MOVE_RIGHT, right);
// MOVE_DOWN
InitializeMove(down, MOVE_DOWN);
animd.Add(MOVE_DOWN, down);
// MOVE_LEFT
InitializeMove(left, MOVE_LEFT);
animd.Add(MOVE_LEFT, left);
}
/// <summary>
/// 移動アニメーションの初期化
/// </summary>
/// <param name="anim"></param>
/// <param name="type"></param>
private void InitializeMove(Animation[] anim, int type)
{
// キャラクタチップのサイズ定義
int w = 24;
int h = 32;
// 配列をインスタンス化
for (int ii = 0; ii < anim.Length; ii++)
{
anim[ii] = new Animation();
}
// 各アニメーションの描画範囲を設定
for (int ii = 0; ii < anim.Length; ii++ )
{
if (ii != anim.Length - 1)
{
anim[ii].rect = new Rectangle(w * ii, h * type, w, h);
anim[ii].next = anim[ii + 1];
}
else
{
anim[ii].rect = anim[1].rect;
anim[ii].next = anim[0];
}
}
}
/// <summary>
/// デフォルトアニメーション
/// </summary>
public Animation defaultAnimation
{
get { return down[0]; }
}
/// <summary>
/// 描画処理
/// </summary>
/// <param name="sprite"></param>
/// <param name="animation"></param>
/// <param name="position"></param>
public void Draw(SpriteBatch sprite, Animation animation, Vector2 position)
{
sprite.Draw(texture, position, animation.rect, Color.White);
}
/// <summary>
/// アクションに対応するアニメーションを取得
/// </summary>
/// <param name="action"></param>
/// <returns></returns>
public Animation getAnimation(int action)
{
Animation[] animation;
animd.TryGetValue(action, out animation);
// アニメーション配列の先頭を返す
return animation[0];
}
/// <summary>
/// テクスチャ
/// </summary>
private Texture2D texture;
/// <summary>
/// アニメーションの索引付きコレクション
/// </summary>
protected static Dictionary<int, Animation[]> animd;
}
}
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Net;
using Microsoft.Xna.Framework.Storage;
namespace Sample2DRPG.Charactor
{
/// <summary>
/// 複数のプレイヤーで共有するキャラクタ定義
/// </summary>
class Chara
{
// アクション定義
public static readonly int MOVE_UP = 0;
public static readonly int MOVE_RIGHT = 1;
public static readonly int MOVE_DOWN = 2;
public static readonly int MOVE_LEFT = 3;
// MOVE_UP
static private Animation[] up = new Animation[4];
// MOVE_RIGHT
static private Animation[] right = new Animation[4];
// MOVE_DOWN
static private Animation[] down = new Animation[4];
// MOVE_LEFT
static private Animation[] left = new Animation[4];
/// <summary>
/// コンストラクタ
/// </summary>
public Chara(Texture2D texture)
{
this.texture = texture;
Initialize();
}
/// <summary>
/// 初期化処理
/// </summary>
private void Initialize()
{
if (animd != null) return;
animd = new Dictionary<int, Animation[]>();
// MOVE_UP
InitializeMove(up, MOVE_UP);
animd.Add(MOVE_UP, up);
// MOVE_RIGHT
InitializeMove(right, MOVE_RIGHT);
animd.Add(MOVE_RIGHT, right);
// MOVE_DOWN
InitializeMove(down, MOVE_DOWN);
animd.Add(MOVE_DOWN, down);
// MOVE_LEFT
InitializeMove(left, MOVE_LEFT);
animd.Add(MOVE_LEFT, left);
}
/// <summary>
/// 移動アニメーションの初期化
/// </summary>
/// <param name="anim"></param>
/// <param name="type"></param>
private void InitializeMove(Animation[] anim, int type)
{
// キャラクタチップのサイズ定義
int w = 24;
int h = 32;
// 配列をインスタンス化
for (int ii = 0; ii < anim.Length; ii++)
{
anim[ii] = new Animation();
}
// 各アニメーションの描画範囲を設定
for (int ii = 0; ii < anim.Length; ii++ )
{
if (ii != anim.Length - 1)
{
anim[ii].rect = new Rectangle(w * ii, h * type, w, h);
anim[ii].next = anim[ii + 1];
}
else
{
anim[ii].rect = anim[1].rect;
anim[ii].next = anim[0];
}
}
}
/// <summary>
/// デフォルトアニメーション
/// </summary>
public Animation defaultAnimation
{
get { return down[0]; }
}
/// <summary>
/// 描画処理
/// </summary>
/// <param name="sprite"></param>
/// <param name="animation"></param>
/// <param name="position"></param>
public void Draw(SpriteBatch sprite, Animation animation, Vector2 position)
{
sprite.Draw(texture, position, animation.rect, Color.White);
}
/// <summary>
/// アクションに対応するアニメーションを取得
/// </summary>
/// <param name="action"></param>
/// <returns></returns>
public Animation getAnimation(int action)
{
Animation[] animation;
animd.TryGetValue(action, out animation);
// アニメーション配列の先頭を返す
return animation[0];
}
/// <summary>
/// テクスチャ
/// </summary>
private Texture2D texture;
/// <summary>
/// アニメーションの索引付きコレクション
/// </summary>
protected static Dictionary<int, Animation[]> animd;
}
}
Player.cs
using System;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Net;
using Microsoft.Xna.Framework.Storage;
namespace Sample2DRPG.Charactor
{
/// <summary>
/// 各プレイヤーを表すクラス
/// </summary>
class Player
{
/// <summary>
/// コンストラクタ
/// </summary>
/// <param name="chara">キャラクタ</param>
/// <param name="position">座標</param>
public Player(Chara chara, Vector2 position)
{
// キャラクタ設定
this.chara = chara;
this.animation = chara.defaultAnimation;
// 初期座標
this.position = position;
}
/// <summary>
/// 更新処理
/// </summary>
/// <param name="gameTime"></param>
public virtual void Update(GameTime gameTime)
{
// 停止している場合はリターン
if (!isDoing) return;
// 前回更新時からのタイミングを加算
timing += gameTime.ElapsedGameTime.Milliseconds;
// 次のアニメーションへ切り替えのタイミングの場合
if (animation.isNextFrame(timing))
{
// オーバーした分の時間を算出
this.timing = timing - animation.timing;
// 次のアニメーションを取得
animation = animation.next;
}
// アクションに応じてプレイヤー座標を更新
if (action == Chara.MOVE_UP)
{
this.position.Y -= SPEED;
}
else if (action == Chara.MOVE_RIGHT)
{
this.position.X += SPEED;
}
else if (action == Chara.MOVE_DOWN)
{
this.position.Y += SPEED;
}
else if (action == Chara.MOVE_LEFT)
{
this.position.X -= SPEED;
}
}
/// <summary>
/// 描画処理
/// </summary>
/// <param name="sprite"></param>
public void Draw(SpriteBatch sprite)
{
chara.Draw(sprite, this.animation, this.position);
}
/// <summary>
/// 行動開始
/// </summary>
/// <param name="action"></param>
public virtual void doAction(int action)
{
// 行動中で現在と同じアクションの場合
if (isDoing && this.action.Equals(action))
{
return;
}
// キャラクタ定義よりアクションに対応したアニメーションを取得
animation = chara.getAnimation(action);
if (animation == null)
{
Console.WriteLine("警告:対象のアニメーションがありません。");
this.isDoing = false;
return;
}
// 行動中に設定
this.isDoing = true;
// アクションを設定
this.action = action;
// フレームを初期化
this.timing = 0;
}
/// <summary>
/// 行動停止
/// </summary>
public virtual void stopAction()
{
isDoing = false;
}
public override string ToString()
{
StringBuilder buf = new StringBuilder();
buf.Append("[x=");
buf.Append(position.X);
buf.Append(",y=");
buf.Append(position.Y);
buf.Append("]");
return buf.ToString();
}
// プレイヤー座標
Vector2 position;
// 行動中か判定
bool isDoing;
// 現在のアクション
int action;
// アニメーション切り替えのタイミング
int timing = 0;
// アニメーション
Animation animation;
// キャラクタクラス
Chara chara;
// 移動速度
static readonly int SPEED = 1;
}
}
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Net;
using Microsoft.Xna.Framework.Storage;
namespace Sample2DRPG.Charactor
{
/// <summary>
/// 各プレイヤーを表すクラス
/// </summary>
class Player
{
/// <summary>
/// コンストラクタ
/// </summary>
/// <param name="chara">キャラクタ</param>
/// <param name="position">座標</param>
public Player(Chara chara, Vector2 position)
{
// キャラクタ設定
this.chara = chara;
this.animation = chara.defaultAnimation;
// 初期座標
this.position = position;
}
/// <summary>
/// 更新処理
/// </summary>
/// <param name="gameTime"></param>
public virtual void Update(GameTime gameTime)
{
// 停止している場合はリターン
if (!isDoing) return;
// 前回更新時からのタイミングを加算
timing += gameTime.ElapsedGameTime.Milliseconds;
// 次のアニメーションへ切り替えのタイミングの場合
if (animation.isNextFrame(timing))
{
// オーバーした分の時間を算出
this.timing = timing - animation.timing;
// 次のアニメーションを取得
animation = animation.next;
}
// アクションに応じてプレイヤー座標を更新
if (action == Chara.MOVE_UP)
{
this.position.Y -= SPEED;
}
else if (action == Chara.MOVE_RIGHT)
{
this.position.X += SPEED;
}
else if (action == Chara.MOVE_DOWN)
{
this.position.Y += SPEED;
}
else if (action == Chara.MOVE_LEFT)
{
this.position.X -= SPEED;
}
}
/// <summary>
/// 描画処理
/// </summary>
/// <param name="sprite"></param>
public void Draw(SpriteBatch sprite)
{
chara.Draw(sprite, this.animation, this.position);
}
/// <summary>
/// 行動開始
/// </summary>
/// <param name="action"></param>
public virtual void doAction(int action)
{
// 行動中で現在と同じアクションの場合
if (isDoing && this.action.Equals(action))
{
return;
}
// キャラクタ定義よりアクションに対応したアニメーションを取得
animation = chara.getAnimation(action);
if (animation == null)
{
Console.WriteLine("警告:対象のアニメーションがありません。");
this.isDoing = false;
return;
}
// 行動中に設定
this.isDoing = true;
// アクションを設定
this.action = action;
// フレームを初期化
this.timing = 0;
}
/// <summary>
/// 行動停止
/// </summary>
public virtual void stopAction()
{
isDoing = false;
}
public override string ToString()
{
StringBuilder buf = new StringBuilder();
buf.Append("[x=");
buf.Append(position.X);
buf.Append(",y=");
buf.Append(position.Y);
buf.Append("]");
return buf.ToString();
}
// プレイヤー座標
Vector2 position;
// 行動中か判定
bool isDoing;
// 現在のアクション
int action;
// アニメーション切り替えのタイミング
int timing = 0;
// アニメーション
Animation animation;
// キャラクタクラス
Chara chara;
// 移動速度
static readonly int SPEED = 1;
}
}
ソリューションファイル
Download Sample2DRPG_STEP01.zip
更新日 2008/05/27