XNA チュートリアル1の内容をおさらい


やっぱりというか、日本のXNA系サイトは結構進んでいるらしく、チュートリアル程度なら全部やっているところが多いみたいですね。
まぁ、マイペースに前回のチュートリアルで何が起こっているのかをCreators Club OnlineにあるVideo Tutorial 1を見ながらおさらいしましょう。
まず、最初から覗いてみましょう。

#region Using Statements
using System;
using System.Collections.Generic;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Storage;
#endregion

#region~#endregionではこのプログラムで使うルーチンが含まれている「クラスライブラリ」を読み込むらしいです。
C#のプロジェクトを組むときに必ず記述されるのがSystemと言うクラスで、XNAのプロジェクトを組むと自動的にMicrosoft.Xna.Framework.○○が読み込まれます。
たぶん、このプロジェクトで必要なのはSystemMicrosoft.Xna.Frameworkは言わずもがな、~.Graphicsと.~Content、たぶん~.Inputも必要になると思います。
それ以外を削除しても、コンパイルと実行が可能でした。
何しろ、音は使ってないし、データを保存するようなこともしてませんからね。

さりげなく書いてしまいましたが、この「クラス」というものはC#やその原型となったC++、Javaと言った言語では重要な意味を持っているそうです。
クラスというのは要するに道具箱のようなもので、その中にメソッドという、要は工具が入っている、と言うイメージですかね。
1つの道具箱(クラス)には複数の工具(メソッド)が入っていて、それを使ってプログラムを作る、と言う感じなんだとか。
クラスライブラリはそのクラスをまとめておくいわば道具箱の詰まったワゴン車と言ったところでしょうか。
ちょいとショボい表現が続きましたかね?(笑

ちなみに、ここでusingによる読み込みをしなくても、例えばGraphicsDeviceManagerと記述されている項目をMicrosoft.Xna.Framework.Graphics.GraphicsDeviceManagerと記述することによって、同等のことができるそうです。
まぁ、最初に読み込んだ方が便利でしょうが。
さて、XNAのゲームを開発する場合必ず必要になるクラスがMicrosoft.Xna.Framework.Gameで、そのクラス内で必要なクラスがInitializeLoadGraphicsContentUnloadGraphicsContentUpdateDrawの5つ。
簡単に表にまとめてみました。

Initialize 直訳すると初期化。そのまんまですね。
LoadGraphicsContent
こで画面に描画するコンテントを読み込みます。例えばチュートリアル1では3Dモデルのp1_wedge.fbxを読み込んでいます。
UnloadGraphicsContent ロー
ドしたコンテントを使い終わったら解除するためのものですかね?あまりよくわかりません。
Update ゲームの進行状況に合わせた変化などを記述する場所。
Draw 描画する画像を記述する場所。これもそのまんまですね。

さて、Drawに移りましょう。

//空間内におけるモデルの位置と角度を設定
Vector3 modelPosition = Vector3.Zero;
//モデルの位置を初期化
float modelRotation = 0.0f;
//モデルの角度を初期化(フロート)
//空間内におけるカメラ位置を設定
Vector3 cameraPosition = new Vector3(
0.0f,	//X軸
50.0f,	//Y軸
5000.0f	//Z軸
);
//投影されるアスペクト比を設定
float aspectRatio = 640.0f / 480.0f;
//画面サイズ640x480を設定

Drawの前にVector3 modelPositionと言う宣言がなされています。
その名の通り、モデルの位置を記述するために宣言したクラスです。
ここのVector3の意味について触れていなかったと思うので書いておくと、Vector3構造で定義すると、X、Y、Z軸を定義するという意味合いを持つそうです。
Vector2X、Y軸、さらにVector4というのもあるんですけど、Vector4の使い方に関してはイマイチよくわからないので、必要になったときに触れましょう。
Vector3.ZeroVector3構造体で定義されているすべての値をゼロにするというクラス(?)です。

また、次の行のfloat modelRotationモデルの傾きを記述するためのクラスを宣言しています。
同様にfloat aspectRatioではアスペクト比を解像度640×480をベースに設定しています。
単位はfloatと言うもので、小数点を含む数値という意味です。
余談ですが、プログラミング言語には必ず型というものが存在します。
正数だけしか扱わないInt型、浮動小数点を扱うためのFloat型、文字を扱うChar型というものが最低限あります。
これらがなぜ必要になるかというと、頭がこんがらがってしまうはずです。
以下に書くことは興味のある人だけ読んでください。

例えばコンピュータが32ビット単位で処理をするとしましょう。
その場合、コンピュータは2進数なので4,294,967,295までの数字を処理することが可能と言うことになります。
また、マイナス(負数)を考慮に入れると一番最初の数字が0の場合正の整数1の場合は負の整数とすることが一般的です。
そういった場合、2,147,483,647 ~ -2,147,483,648まで扱うことができます。
-1は1・・・1で始まります。
-2は1・・・10と言った具合。
そして、浮動小数点を使用する場合、符号ビット指数ビット仮数ビットで構成されているFloat型を使います。
小数点の点の位置を決める指数ビットがあるため、Int型よりも扱える範囲も一見すると狭くなりますが、整数型では扱うことができない小数点を扱うことが可能となります。
近年、CPUの高速化などと共にグラフィックス関連の処理はこのFloat型が利用される機会が多くなっています。

Char型というものはCharacter、つまり文字を処理するための型です。
それぞれの文字にビットが割り当てられています。英語圏は文字が少ないので8ビットで事足ります。
日本語用に16ビットに拡張したJISShift-JISEUC、さらにはすべての言語を包括的に扱うことを目標としたUnicodeなどがあります。
ゲーム内でも文字を使う機会が必ずあると思うので、近々触れることになるでしょう。

さて、話を戻してDraw本体のコードを見てみましょう。

protected override void Draw(GameTime gameTime)
{
graphics.GraphicsDevice.Clear(Color.Green);
//座標変換させるための元をコピー
Matrix[] transforms = new Matrix[myModel.Bones.Count];
myModel.CopyAbsoluteBoneTransformsTo(transforms);
//複数のメッシュを持ったモデルを描画し、それを繰り返す
foreach (ModelMesh mesh in myModel.Meshes) {
//myModelにBasicEffectを付与
foreach (BasicEffect effect in mesh.Effects) {
//BasicEffectのデフォルトライトを設定
effect.EnableDefaultLighting();
//BasicEffectによる位置座標変換の設定
effect.World = transforms[mesh.ParentBone.Index] *
Matrix.CreateRotationY(modelRotation) *
Matrix.CreateTranslation(modelPosition);
//BasicEffectによるカメラの設定
effect.View = Matrix.CreateLookAt(
cameraPosition,	//カメラの位置座標
Vector3.Zero,	//カメラの向いている方向
Vector3.Up		//カメラの上方向を指定
);
effect.Projection = Matrix.CreatePerspectiveFieldOfView(
MathHelper.ToRadians(45.0f),	//視野角45度
aspectRatio,					//アスペクト比
1.0f,							//描画する一番近い距離
10000.0f						//描画する一番遠い距離
);
}
//エフェクト設定を避けてメッシュを描画
mesh.Draw();
}
}

まず、

graphics.GraphicsDevice.Clear(Color.Green);

と言うメソッドでは画面をGreenで初期化しています。
前回まではCornflowerBlueを使っていましたが、今回はちょっと変化を加えるためにあえてGreenで初期化してみました。
Color.はたぶんSystemで定義されているもので、GreenRedBlue、そしてCornflowerBlueなどが定義されています。
ここに直接RGBでの指定もできると思いますが、その方法などがイマイチわからないので、今回は定義された色を使います。
Visual Studioのヘルパーでいろんな色が候補として出てくるのでいろいろな色を試してみてください。
xna_11

さて、ここで出てくるforeach文
正直Cをちょっと勉強していた自分としては未知の構文です。
で、ちょろっと調べたところ、foreach文の書式は

foreach(型 変数 in コレクション)

となるそうです。
このコレクションというのはどうやら配列のことらしく、関数の中に格納されている情報を順番に使っていくといった感じのようです。
MSDNのC#リファレンスによれば下記のような使い方になるそうです。

int[] test = { 4, 6, 4, 9};
foreach (int hoge in test) {
System.Console.WriteLine(hoge);
}

以下が実行結果。

4649

話を戻すと、最初のforeachModelMesh構造体meshを作ってmyModel.Meshesの情報を渡す。
2つ目のforeachBasicEffect構造体effectを作って上層で取得したmeshの中の.Efffectsの情報を受け渡す、と。
するとmyModelのメッシュ情報が受け渡されてライトの設定空間内での場所の設定カメラのポジション、そして画面に映し出される映像の詳細が決定されるという流れのようです。
それをmyModelのメッシュすべてに掛ける、と。
うむ~、ちょいと難しいかも。

これまたややこしいのが位置座標変換の設定
Matrix構造体transformsクラスを作ってその中にボーンコレクションとして入れる。
そしてtransformsクラスにある親となるボーン x Y軸方向へのモデル回転 x 座標変換effect.Worldに代入する、と。
ただ、どうしてもMatrix.CreateTranslation(modelPosition)を追加する意味がわからなかったので、試しにこの部分だけコメントアウトしてデバッグしたところ、普通に動きました。
詰まるところ、今は必要ないようです。
CreateRotationYYXZに変えてデバッグしてみてください。
予想通りのことが起こります。

Matrix.CreateLookAtは最初にカメラの位置、次にカメラが見る座標、そしてカメラがワールド座標から見てどこを上とするかを記述します。
一般的にY軸が上になるので、Vector3.Upという(0,1,0)の座標が定義されたクラスがあります。
それじゃつまらないので、あえてX軸やZ軸を上にしてみましょう。

Drawの前に以下のコードを追加してみてください。

Vector3 UpPosition new Vector3 (
1,
0,
0);

また、Vector3.UpUpPositionに書き換えてみてください。
たぶん下のような感じになると思います。
xna_12
さて、このオブジェクトが動いているのは、Updateのおかげです。
では最後にUpdateを覗いてみましょう。

protected override void Update(GameTime gameTime)
{
//パッドのボタンを押すと終了
if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed)
this.Exit();	//modelRotationへミリ秒ごとに0.1fラジアンを加算する。
modelRotation += (float)gameTime.ElapsedGameTime.TotalMilliseconds * MathHelper.ToRadians(0.1f);
base.Update(gameTime);
}

ここでは弧度法を使って角度を加算していきます。
まず、gameTime.ElaspendGameTime.TotalMillisecondsを使ってゲーム時間をミリ秒で取得します。
そこで取得したミリ秒に0.1ラジアンを掛けたものをmodelRotationへ加算します。
つまり、1ミリ秒ごとに0.1ラジアンを加算すると言うことです。
これにより、オブジェクトが回転するのです。
今回もダラダラ解説して終わってしまいましたね・・・日々精進します。
今回若干コメントも増やしたり何が起こってるのかを分かる範囲で修正しました。
それが下記のコードです。
前回のソリューションファイルがあれば、そのまま動くはずです。

#region Using Statementsusing System;
using System.Collections.Generic;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
#endregion
namespace yu++_XNA_Game_01
{
public class Game1 : Microsoft.Xna.Framework.Game
{
GraphicsDeviceManager graphics;
ContentManager content;
public Game1()
{
graphics = new GraphicsDeviceManager(this);
content = new ContentManager(Services);
}
protected override void Initialize()
{
base.Initialize();
}
//3Dモデルを描画
Model myModel; //モデルの宣言
protected override void LoadGraphicsContent(bool loadAllContent)
{
if (loadAllContent) {
myModel = content.Load<Model>("Content\\Models\\p1_wedge");	//p1_wedgeをmyModelとして代入
}
}
protected override void UnloadGraphicsContent(bool unloadAllContent) {
if (unloadAllContent == true) {
content.Unload();
}
}
protected override void Update(GameTime gameTime) {
//パッドのボタンを押すと終了
if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed)
this.Exit();
//modelRotationへミリ秒ごとに0.1fラジアンを加算する。
modelRotation += (float)gameTime.ElapsedGameTime.TotalMilliseconds * MathHelper.ToRadians(0.1f);
base.Update(gameTime);        }
//空間内におけるモデルの位置と角度を設定
Vector3 modelPosition = Vector3.Zero;	//モデルの位置を初期化
float modelRotation = 0.0f;				//モデルの角度を初期化(フロート)
//空間内におけるカメラ位置を設定
Vector3 cameraPosition = new Vector3(
0.0f,	//X軸
50.0f,	//Y軸
5000.0f	//Z軸
);
Vector3 UpPosition = new Vector3(
0,
1,
0);
//投影されるアスペクト比を設定
float aspectRatio = 640.0f / 480.0f; //画面サイズ640x480を設定
protected override void Draw(GameTime gameTime)
{
graphics.GraphicsDevice.Clear(Color.Green);
//変形させるための元をコピー
Matrix[] transforms = new Matrix[myModel.Bones.Count];
myModel.CopyAbsoluteBoneTransformsTo(transforms);
//複数のメッシュを持ったモデルを描画し、それを繰り返す
foreach (ModelMesh mesh in myModel.Meshes) {
//myModelにBasicEffectを付与
foreach (BasicEffect effect in mesh.Effects) {
//BasicEffectのデフォルトライトを設定
effect.EnableDefaultLighting();
//BasicEffectによる位置座標変換の設定
effect.World = transforms[mesh.ParentBone.Index]
* Matrix.CreateRotationY(modelRotation)
* Matrix.CreateTranslation(modelPosition);
//BasicEffectによるカメラの設定
effect.View = Matrix.CreateLookAt(
cameraPosition,	//カメラの位置座標
Vector3.Zero,	//カメラの向いている方向
UpPosition	//カメラの上方向を指定
);
effect.Projection = Matrix.CreatePerspectiveFieldOfView(
MathHelper.ToRadians(45.0f),	//視野角45度
aspectRatio,					//アスペクト比
1.0f,							//描画する一番近い距離
10000.0f						//描画する一番遠い距離
);
}
//エフェクト設定を避けてメッシュを描画
mesh.Draw();
}
}
}
}

アディオス。

Post to Twitter

, ,

  1. No comments yet.
(will not be published)