今後、力をいれていきます。
PSMゲーム
Bloodred Wyvern (ブラッドレッドワイバーン)
Bloodred Wyvern 2
Gravity Anomaly Zone 不可思議重力帯
その他
PlayStation®Mobile関連リンク集
PlayStation®Mobile学習日記
XBLIG
BLASTER AND AMETHYST
BLASTER AND MORION
LEUCISTIC WYVERN
Bioerosion
URGENT EVASION 360
The Undead Syndrome / TUS 2
Dark Storm Blast 360
その他
Making Of The Undead Syndrome[3DCG]
Making Of The Undead Syndrome2
XNA関連リンク集
xna学習日記
第1〜3話
第4〜5話
第6〜7話
第8〜9話
第10〜12話
第13〜14話
第15話
ブラウザゲーム
発火少年の地面焦がし
MAGICAL METEOR SHOWER
メタセコイア備忘録
東日本大震災(Aftermath of the 2011 Tohoku earthquake and tsunami)
ムカゴソフトウェア開発
e-mail:cqp00202@nifty.ne.jp
日本における、Xbox LIVEコミュニティーゲームの受付開始時期がようやくアナウンスされた。
7月中旬らしい。
それでも、まだxnaに対して、半信半疑だったりするのだが・・・。
今回から、立体モデル同士の衝突を検出するコードを作成してみる。
xnaでは、バウンディングスフィア(衝突検出用の球)を定義できる「BoundingSphere構造体」が用意されている。
「BoundingSphere構造体」を使った処理の流れは、多分、こんな感じになると思う。
最初に、(衝突の検出が必要な)立体モデルの数だけ「BoundingSphere構造体」のインスタンスを生成する。
それによって、バウンディングスフィアが用意される。
次に、それらを、常に(衝突の検出が必要な)立体モデルを被うように重ね合わせる。
そして、もし、それらが交差した時は、立体モデル同士が衝突したと判断する・・・こんな感じかな?
バウンディングスフィアは、通常のゲームプレイ時には当然描画しない。
しかし、試作段階では、描画できた方が何かと好都合だ。
そこで、バウンディングスフィアを描画してくれる何らかのメソッドを探してみたのだが・・・どうやらそこまで親切では無いようだ。
もし、バウンディングスフィアを描画したい時は、事前に開発者側で球体の立体モデルを準備して、プロジェクトに組み込んでおかなければならないのか。
で、衝突の検出は、BoundingSphere.Intersectsメソッドを呼び出せば、数学的なものを意識しなくても、メソッドの戻り値から判断できるようになっている。
・・・思いっきり勘違いしてる気がしないでもないけど。(笑)
ただし、あの巨大な迷路の立体モデル(左の画像)を相手に、バウンディングスフィア同士の衝突検出処理を使うのは無理がある(もちろん、自キャラに球体を割り当てるのは問題ないけど)。
よって、迷路の描画に関する処理は全てコメントに変えて、その代わりに、自キャラに使っている謎のメカモデルをもう1体描画することにした。← 一方を敵キャラとした。
今回は、謎のメカ同士の衝突を検出してみたい。
↑
今回は出番無し
以下は、とにかく衝突を検出できるようにしただけのサンプルコード。(苦笑)
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.Media; using Microsoft.Xna.Framework.Net; using Microsoft.Xna.Framework.Storage; namespace WindowsGame1 { public class Game1 : Microsoft.Xna.Framework.Game { GraphicsDeviceManager graphics; SpriteBatch spriteBatch; Model samplemodel; //自キャラの立体モデル Model testmodel; //衝突検出用の敵キャラ //Model samplemap; //マップの立体モデル Vector3 camera_pos; //カメラの位置 Vector3 model_pos = Vector3.Zero; //自キャラの位置 float model_rotate_y; //自キャラの角度 Vector3 test_pos = new Vector3(5.0f, 0.0f, 5.0f); //敵キャラの位置 //バウンディングスフィア BoundingSphere samplebound = new BoundingSphere(); //自キャラ用 BoundingSphere testbound = new BoundingSphere(); //敵キャラ用 public Game1() { graphics = new GraphicsDeviceManager(this); Content.RootDirectory = "Content"; } protected override void Initialize() { base.Initialize(); } protected override void LoadContent() { spriteBatch = new SpriteBatch(GraphicsDevice); //モデルの読み込み samplemodel = Content.Load<Model>("sample_model"); //自キャラ //samplemap = Content.Load<Model>("sample_map"); //マップ testmodel = Content.Load<Model>("sample_model"); //敵キャラ } protected override void UnloadContent() { } // デフォルトで1/60秒固定で呼び出される protected override void Update(GameTime gameTime) { //ゲームパッドに関する情報取得用 GamePadState gamepadstate = GamePad.GetState(PlayerIndex.One); { //trueの場合、ゲームパッドが接続されている if (gamepadstate.IsConnected) { //終了 if (gamepadstate.Buttons.Back == ButtonState.Pressed) this.Exit(); //立体モデルを左右に回転 model_rotate_y -= gamepadstate.ThumbSticks.Left.X * 0.05f; //立体モデルの前進と後退 Vector3 vec3 = new Vector3(0, 0, gamepadstate.ThumbSticks.Left.Y * 3.0f); vec3 = Vector3.Transform(vec3, Matrix.CreateRotationY(model_rotate_y)); //正面 model_pos.Z += vec3.Z; model_pos.X += vec3.X; } } //バウンディングスフィアに関する設定 samplebound.Center = model_pos; //自キャラと重ねる(仮) testbound.Center = test_pos; //敵キャラと重ねる(仮) samplebound.Radius = 1.0f; //球体の半径 testbound.Radius = 1.0f; //球体の半径 base.Update(gameTime); } protected override void Draw(GameTime gameTime) { //立体モデルが衝突したかどうか? if (samplebound.Intersects(testbound)) GraphicsDevice.Clear(Color.Coral); else GraphicsDevice.Clear(Color.CornflowerBlue); //カメラの位置を更新 camera_pos = Vector3.Transform(new Vector3(0, 0, -8), Matrix.CreateRotationY(model_rotate_y)) + model_pos; //マップの描画 //DrawModel(samplemap, Matrix.CreateTranslation(Vector3.Zero)); //自キャラの描画 DrawModel(samplemodel, Matrix.CreateRotationY(model_rotate_y) * Matrix.CreateTranslation(model_pos)); //敵キャラの描画 DrawModel(testmodel, Matrix.CreateTranslation(test_pos)); base.Draw(gameTime); } //立体モデルの描画 void DrawModel(Model model, Matrix world) { foreach (ModelMesh mesh in model.Meshes) { foreach (BasicEffect effect in mesh.Effects) { //デフォルトのライティングを有効 effect.EnableDefaultLighting(); //ワールド行列を設定 effect.World = world; //ビュー行列を設定 effect.View = Matrix.CreateLookAt(camera_pos, model_pos, new Vector3(0.0f, 1.0f, 0.0f)); //射影行列を設定 effect.Projection = Matrix.CreatePerspectiveFieldOfView(MathHelper.ToRadians(45.0f), GraphicsDevice.Viewport.AspectRatio, 1.0f, 10000.0f); //スペキュラカラーの設定 effect.SpecularColor = new Vector3(0.4f, 0.4f, 0.4f); } mesh.Draw(); } } } }
前回の説明通り、まず最初に、 BoundingSphere構造体のインスタンスを生成している。
BoundingSphere samplebound = new BoundingSphere(); //自キャラ用 BoundingSphere testbound = new BoundingSphere(); //敵キャラ用
Update()メソッドにて、バウンディングスフィアの中心の位置と半径を設定している。
samplebound.Center = model_pos; //自キャラと重ねる(仮) testbound.Center = test_pos; //敵キャラと重ねる(仮) samplebound.Radius = 1.0f; //球体の半径 testbound.Radius = 1.0f; //球体の半径
しかし、この記述方法では、バウンディングスフィアが、該当する立体モデルをきちんと包み込まない恐れがある。
4月28日追記:
立体モデルをきちんと包み込まないというより、複数のメッシュを持つ立体モデルの場合、衝突が検出されないメッシュが出来てしまう。
と言うのも、BoundingSphere.Centerフィールドと、BoundingSphere.Radiusフィールドの値を、該当する立体モデルの各メッシュの情報(ModelMesh.BoundingSphereプロパティ)から求めていないからだ。
この辺は今1つ理解しておらず、とりあえず手抜きコードで誤魔化した。(苦笑)
それと、立体モデルとバウンディングスフィアの関係が、視覚的にチェックできるように、球体の立体モデルを用意すべきだったかもしれない。
あとは、Draw()メソッドの、
if (samplebound.Intersects(testbound)) GraphicsDevice.Clear(Color.Coral); else GraphicsDevice.Clear(Color.CornflowerBlue);
BoundingSphere.Intersectsメソッドが返す値より、立体モデル同士が衝突したかどうか判断する。
もし衝突している時は、背景の色をCoralに変えている。
実行結果
背景が無いせいで、自キャラが停止してるように見えるかもしれないが、停止してるのは敵キャラの方である。
1つの立体モデルが、複数のメッシュ(左の画像では、Model.Meshes[0]〜[2]の3つのメッシュがある)を持っている場合、その数だけ「BoundingSphere構造体」のインスタンスが必要となる。
メッシュの数だけ「BoundingSphere構造体」のインスタンス(左の画像では、boundingSpheres[0]〜[2]とした)を生成したら、それらのCenter、Radiusフィールドに、該当のメッシュが持つModelMesh.BoundingSphereプロパティから得た値を代入していく(BoundingSphere.Centerフィールドには、立体モデルそのものの位置情報も加算する)。
つまり、バウンディングスフィアの中心の位置は・・・
boundingSpheres[0〜2].Center = 【立体モデルそのものの位置情報】 + Model.Meshes[0〜2].BoundingSphere.Center;
そして、半径は・・・
boundingSpheres[0〜2].Radius = Model.Meshes[0〜2].BoundingSphere.Radius;
・・・って、思いっきり誤っていたりして。(笑)
追記:
BoundingSphere.CreateMergedメソッドを用いて、複数のバウンディングスフィアを、1つのBoundingSphereに結合していくことが可能・・・なのかな?