Välj en modell från dess position på skärmen
sammanfattning
Det låter dig välja modellen vid positionen för muspekaren. När du håller muspekaren över modellen ändras träfftexten till Sant.
Operativ miljö
Förutsättningar
XNA-versioner som stöds |
|
Plattformar som stöds |
|
Windows Nödvändig version av Vertex Shader | 2.0 |
Windows Pixel Shader-version som krävs | 2.0 |
Operativ miljö
plattform |
|
Så här arbetar du med exemplet
Fungerar tangentbordXbox | 360-kontrollermuspekare | |||
---|---|---|---|---|
Markörens förflyttning | ↑↓←→ | Vänster styrspak | Rörelse med musen | - |
substans
Konvertera skärmkoordinater till rumsliga 3D-koordinater
Du kanske vill välja en modell i 3D-rymden med en mus. I det här fallet är det nödvändigt att konvertera den tvådimensionella koordinatpunkten på skärmen till den tredimensionella koordinaten där modellen finns och utföra en träffbedömning.
För att utöka elementet från 2D till 3D är det dock inte möjligt att hitta en punkt med 3D-koordinater från 2D-skärmkoordinater på endast X och Y. Om du till exempel föreställer dig att du faktiskt klickar på skärmen kommer du att förstå att det inte är möjligt att avgöra om positionen i 3D-rymden när du klickar är framför objektet, själva objektet eller bakom objektet.
I stället för att representera den klickade positionen som en punkt behandlas den därför som en linje som sträcks ut från kamerans position i klickets riktning. Genom att utföra kollisionsdetektering mellan linjen och objektet är det möjligt att välja en modell. Förresten, linjeparametrar kan hanteras i XNA av en struktur som kallas Ray.
Få positionen i 3D-rymden från skärmens position
XNA har ingen metod för att hitta en linje i riktning mot ett klick på skärmen. Men eftersom det är möjligt att hitta en punkt i 3D-rymden genom att ange skärmens koordinater och djup, är det möjligt att hitta en linje genom att koppla kamerans position och 3D-rummets koordinatpunkt som transformeras på ett specifikt djup.
Det är lätt att hitta koordinater för objektutrymme från koordinater för skärmutrymme med metoden "Viewport.Unproject".
// ビューポートを取得
Viewport viewport = this.GraphicsDevice.Viewport;
// スクリーンの位置を Vector3 で作成 (Z は 1.0 以下を設定)
Vector3 screenPosition = new Vector3(this.markPosition, 1.0f);
// スクリーン座標を3次元座標に変換
Vector3 worldPoint = viewport.Unproject(screenPosition,
this.projection,
this.view,
Matrix.Identity);
Det första argumentet är en Vector3 med skärmkoordinater och djup. Ställ in X, Y till skärmens koordinater och Z till djupvärdet. Djupet beror på parametrarna "nearPlaneDistance" och "farPlaneDistance" i projektionsmatrisen, där du kan ange 0,0f för att hitta avståndet från kamerapositionen till nearPlaneDistance och 1,0f för att bestämma avståndet från kamerapositionen till farPlaneDistance.
Det andra argumentet är projektionsmatrisen och det tredje argumentet är vymatrisen.
Du kan hitta en objektrymdsvektor som returvärde.
Viewport.Unproject
metod
Projicerar en vektor från skärmrymden till objektrymden.
källa | Vektor3 | Skärmkoordinatvektor för konvertering till objektrymdskoordinater |
projektion | Matris | Projektiv matris |
utsikt | Matris | Visa matris |
värld | Matris | Anger den slutliga världsmatriskoordinattransformationen som ska utföras |
Returnera värden | Vektor3 | Hämta en vektor i objektrymden |
Skapa en stråle
Linjeparametrar kan vara strålstrukturer. Det första argumentet till konstruktorn är startpunkten för strålen, och det andra argumentet är strålens orientering.
Ställ in kamerans position som startpunkt och beräkna orienteringen genom att subtrahera kamerans position från de 3D-rymdkoordinater som redan har konverterats till orientering. Orienteringen anges till en enhetsvektor med hjälp av Vector3.Normalize metoden .
// マークが指す方向へのレイを作成
Ray ray = new Ray(this.cameraPosition,
Vector3.Normalize(worldPoint - this.cameraPosition));
Ray
konstruktor
Skapa en instans av "Ray"-strukturen som innehåller parametrarna för raden.
position | Vektor3 | Utgångspunkten för strålen |
riktning | Vektor3 | Strålens riktning |
Boll och ray hitbox
Klassen ModelMesh som läses in från innehållspipelinen innehåller sfärdata som omfattar nätet, som kallas BoundingSphere egenskap. Genom att ange den stråle som du just skapade i metoden Överlappar i den här klassen kan du kontrollera om sfären och strålen kolliderar.
I händelse av en kollision returneras avståndet mellan strålens början och kollisionspunkten. Om det inte finns någon kollision null returneras, så exemplet kontrollerar om det finns en kollision genom null bedömning.
Den här metoden förutsätter dock att modellen finns i ursprunget. Om du flyttar modellen måste du omvandla strålarna när modellen rör sig.
Förresten, den här provmodellen är en sfär, så jag tror att den kan bestämmas exakt.
// 球とレイとの当たり判定を行う
this.isHit = false;
foreach (ModelMesh mesh in this.model.Meshes)
{
if (mesh.BoundingSphere.Intersects(ray) != null)
{
// 球とレイは交差している
this.isHit = true;
break;
}
}
BoundingSphere.Intersects
metod
Kollisionsdetektering mellan den inkluderande bollen och rockan utförs.
stråle | Stråle | Ray för att bedöma kollisionen med bollen |
Returnera värden | Nullable<float> | I händelse av en kollision returnerar den avståndet mellan startpunkten för strålen och träffpunkten med sfären. Om det inte finns någon kollision returneras null. |
Alla koder
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;
#if WINDOWS_PHONE
using Microsoft.Xna.Framework.Input.Touch;
#endif
namespace ModelSelectByScreenPosition
{
<summary>
ゲームメインクラス
</summary>
public class GameMain : Microsoft.Xna.Framework.Game
{
<summary>
グラフィックデバイス管理クラス
</summary>
private GraphicsDeviceManager graphics = null;
<summary>
スプライトのバッチ化クラス
</summary>
private SpriteBatch spriteBatch = null;
<summary>
スプライトでテキストを描画するためのフォント
</summary>
private SpriteFont font = null;
<summary>
モデル
</summary>
private Model model = null;
<summary>
マーク
</summary>
private Texture2D mark = null;
<summary>
マーク画像の中心位置
</summary>
private Vector2 markCenterPosition = Vector2.Zero;
<summary>
マークの位置
</summary>
private Vector2 markPosition = new Vector2(100.0f, 100.0f);
<summary>
モデルへの当たり判定フラグ
</summary>
private bool isHit = false;
<summary>
カメラの位置
</summary>
private Vector3 cameraPosition = new Vector3(0.0f, 0.0f, 10.0f);
<summary>
ビューマトリックス
</summary>
private Matrix view;
<summary>
プロジェクションマトリックス
</summary>
private Matrix projection;
<summary>
GameMain コンストラクタ
</summary>
public GameMain()
{
// グラフィックデバイス管理クラスの作成
this.graphics = new GraphicsDeviceManager(this);
// ゲームコンテンツのルートディレクトリを設定
this.Content.RootDirectory = "Content";
#if WINDOWS_PHONE
// Windows Phone のデフォルトのフレームレートは 30 FPS
this.TargetElapsedTime = TimeSpan.FromTicks(333333);
// バックバッファサイズの設定
this.graphics.PreferredBackBufferWidth = 480;
this.graphics.PreferredBackBufferHeight = 800;
// フルスクリーン表示
this.graphics.IsFullScreen = true;
#endif
}
<summary>
ゲームが始まる前の初期化処理を行うメソッド
グラフィック以外のデータの読み込み、コンポーネントの初期化を行う
</summary>
protected override void Initialize()
{
// ビューマトリックス
this.view = Matrix.CreateLookAt(
this.cameraPosition,
Vector3.Zero,
Vector3.Up
);
// プロジェクションマトリックス
this.projection = Matrix.CreatePerspectiveFieldOfView(
MathHelper.ToRadians(45.0f),
(float)this.GraphicsDevice.Viewport.Width /
(float)this.GraphicsDevice.Viewport.Height,
1.0f,
100.0f
);
// コンポーネントの初期化などを行います
base.Initialize();
}
<summary>
ゲームが始まるときに一回だけ呼ばれ
すべてのゲームコンテンツを読み込みます
</summary>
protected override void LoadContent()
{
// テクスチャーを描画するためのスプライトバッチクラスを作成します
this.spriteBatch = new SpriteBatch(this.GraphicsDevice);
// フォントをコンテンツパイプラインから読み込む
this.font = this.Content.Load<SpriteFont>("Font");
// モデルを作成
this.model = this.Content.Load<Model>("Model");
// ライトとビュー、プロジェクションはあらかじめ設定しておく
foreach (ModelMesh mesh in this.model.Meshes)
{
foreach (BasicEffect effect in mesh.Effects)
{
// デフォルトのライト適用
effect.EnableDefaultLighting();
// ビューマトリックスをあらかじめ設定
effect.View = this.view;
// プロジェクションマトリックスをあらかじめ設定
effect.Projection = this.projection;
}
}
// マーク作成
this.mark = this.Content.Load<Texture2D>("Mark");
// マークの中心位置
this.markCenterPosition = new Vector2(this.mark.Width / 2, this.mark.Height / 2);
}
<summary>
ゲームが終了するときに一回だけ呼ばれ
すべてのゲームコンテンツをアンロードします
</summary>
protected override void UnloadContent()
{
// TODO: ContentManager で管理されていないコンテンツを
// ここでアンロードしてください
}
<summary>
描画以外のデータ更新等の処理を行うメソッド
主に入力処理、衝突判定などの物理計算、オーディオの再生など
</summary>
<param name="gameTime">このメソッドが呼ばれたときのゲーム時間</param>
protected override void Update(GameTime gameTime)
{
// キーボードの情報取得
KeyboardState keyboardState = Keyboard.GetState();
// ゲームパッドの情報取得
GamePadState gamePadState = GamePad.GetState(PlayerIndex.One);
// Xbox 360 コントローラ、Windows Phone の BACK ボタンを押したときに
// ゲームを終了させます
if (gamePadState.Buttons.Back == ButtonState.Pressed)
{
this.Exit();
}
// 移動スピード
float speed = 200.0f;
// キーボードによるマークの移動
if (keyboardState.IsKeyDown(Keys.Left))
{
this.markPosition.X -= speed * (float)gameTime.ElapsedGameTime.TotalSeconds;
}
if (keyboardState.IsKeyDown(Keys.Right))
{
this.markPosition.X += speed * (float)gameTime.ElapsedGameTime.TotalSeconds;
}
if (keyboardState.IsKeyDown(Keys.Up))
{
this.markPosition.Y -= speed * (float)gameTime.ElapsedGameTime.TotalSeconds;
}
if (keyboardState.IsKeyDown(Keys.Down))
{
this.markPosition.Y += speed * (float)gameTime.ElapsedGameTime.TotalSeconds;
}
// ゲームパッドによるマークの移動
if (gamePadState.IsConnected)
{
this.markPosition.X += gamePadState.ThumbSticks.Left.X * speed *
(float)gameTime.ElapsedGameTime.TotalSeconds;
this.markPosition.Y -= gamePadState.ThumbSticks.Left.Y * speed *
(float)gameTime.ElapsedGameTime.TotalSeconds;
}
// マウス処理
MouseState mouseState = Mouse.GetState();
if (mouseState.X >= 0 && mouseState.X < this.Window.ClientBounds.Width &&
mouseState.Y >= 0 && mouseState.Y < this.Window.ClientBounds.Height &&
mouseState.LeftButton == ButtonState.Pressed)
{
// マウスがウインドウ内にあればマウスの位置を優先する
this.markPosition = new Vector2(mouseState.X, mouseState.Y);
}
// ビューポートを取得
Viewport viewport = this.GraphicsDevice.Viewport;
// スクリーンの位置を Vector3 で作成 (Z は 1.0 以下を設定)
Vector3 screenPosition = new Vector3(this.markPosition, 1.0f);
// スクリーン座標を3次元座標に変換
Vector3 worldPoint = viewport.Unproject(screenPosition,
this.projection,
this.view,
Matrix.Identity);
// マークが指す方向へのレイを作成
Ray ray = new Ray(this.cameraPosition,
Vector3.Normalize(worldPoint - this.cameraPosition));
// 球とレイとの当たり判定を行う
this.isHit = false;
foreach (ModelMesh mesh in this.model.Meshes)
{
if (mesh.BoundingSphere.Intersects(ray) != null)
{
// 球とレイは交差している
this.isHit = true;
break;
}
}
// 登録された GameComponent を更新する
base.Update(gameTime);
}
<summary>
描画処理を行うメソッド
</summary>
<param name="gameTime">このメソッドが呼ばれたときのゲーム時間</param>
protected override void Draw(GameTime gameTime)
{
// 画面を指定した色でクリアします
this.GraphicsDevice.Clear(Color.CornflowerBlue);
// Zバッファを有効にする
this.GraphicsDevice.DepthStencilState = DepthStencilState.Default;
// モデルを描画
foreach (ModelMesh mesh in this.model.Meshes)
{
mesh.Draw();
}
// スプライトの描画準備
this.spriteBatch.Begin();
// マーク描画
this.spriteBatch.Draw(this.mark, this.markPosition,
null, Color.White, 0.0f,
this.markCenterPosition, 1.0f, SpriteEffects.None, 0.0f);
// テキスト描画
this.spriteBatch.DrawString(this.font,
"Cursor Key Press or" + Environment.NewLine +
" MouseLeftButton Drag" + Environment.NewLine +
"Hit : " + this.isHit,
new Vector2(50.0f, 50.0f), Color.White);
// スプライトの一括描画
this.spriteBatch.End();
// 登録された DrawableGameComponent を描画する
base.Draw(gameTime);
}
}
}