Pilih model dari kedudukannya pada skrin
Ringkasan
Ia membolehkan anda memilih model pada kedudukan kursor tetikus. Apabila anda menuding kursor pada model, teks Hit bertukar kepada Benar.
Persekitaran operasi
Prasyarat
Versi XNA yang Disokong |
|
Platform yang Disokong |
|
Versi Vertex Shader yang Diperlukan Windows | 2.0 |
Windows Versi Pixel Shader Diperlukan | 2.0 |
Persekitaran operasi
Platform |
|
Bagaimana untuk bekerja dengan sampel
Papan kekunci berfungsiXbox | 360 pengawalsentuhan tetikus | |||
---|---|---|---|---|
Pergerakan kursor | ↑↓←→ | Tongkat Kiri | Pergerakan tetikus | - |
Bahan
Tukar koordinat skrin kepada koordinat spatial 3D
Anda mungkin mahu memilih model dalam ruang 3D dengan tetikus. Dalam kes ini, adalah perlu untuk menukar titik koordinat dua dimensi pada skrin kepada koordinat tiga dimensi di mana model itu wujud dan melakukan penghakiman pukulan.
Walau bagaimanapun, untuk memanjangkan elemen daripada 2D ke 3D, tidak mungkin untuk mencari titik dengan koordinat 3D daripada koordinat skrin 2D hanya X dan Y. Sebagai contoh, jika anda membayangkan benar-benar mengklik pada skrin, anda akan memahami bahawa tidak mungkin untuk menentukan sama ada kedudukan dalam ruang 3D apabila anda mengklik berada di hadapan objek, objek itu sendiri atau di belakang objek.
Oleh itu, daripada mewakili kedudukan yang diklik sebagai titik, ia dianggap sebagai garisan yang diregangkan dari kedudukan kamera ke arah klik. Dengan melakukan pengesanan perlanggaran antara garisan dan objek, adalah mungkin untuk memilih model. Dengan cara ini, parameter garisan boleh dikendalikan dalam XNA oleh struktur yang dipanggil Ray.
Dapatkan kedudukan dalam ruang 3D dari kedudukan skrin
XNA tidak mempunyai kaedah untuk mencari garisan ke arah klik pada skrin. Walau bagaimanapun, kerana mungkin untuk mencari titik dalam ruang 3D dengan menentukan koordinat dan kedalaman skrin, adalah mungkin untuk mencari garisan dengan menyambungkan kedudukan kamera dan titik koordinat ruang 3D yang diubah pada kedalaman tertentu.
Mencari koordinat ruang objek daripada koordinat ruang skrin mudah dilakukan menggunakan kaedah "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);
Hujah pertama ialah Vector3 dengan koordinat dan kedalaman skrin. Tetapkan X, Y kepada koordinat skrin, dan Z kepada nilai kedalaman. Kedalaman bergantung pada parameter "nearPlaneDistance" dan "farPlaneDistance" matriks unjuran, di mana anda boleh menentukan 0.0f untuk mencari jarak dari kedudukan kamera ke nearPlaneDistance, dan 1.0f untuk menentukan jarak dari kedudukan kamera ke farPlaneDistance.
Hujah kedua ialah matriks unjuran, dan hujah ketiga ialah matriks pandangan.
Anda boleh mencari vektor ruang objek sebagai nilai kembalian.
Viewport.Unproject
Kaedah
Menayangkan vektor daripada ruang skrin ke dalam ruang objek.
Sumber | Vektor3 | Vektor koordinat skrin untuk menukar kepada koordinat ruang objek |
Unjuran | Matriks | Matriks projektif |
Lihat | Matriks | Lihat Matriks |
Dunia | Matriks | Menentukan transformasi koordinat matriks dunia akhir yang akan dilakukan |
Nilai Pulangan | Vektor3 | Dapatkan vektor dalam ruang objek |
Cipta Sinar
Parameter garisan boleh menjadi struktur Ray. Hujah pertama kepada pembina ialah titik permulaan sinar, dan hujah kedua ialah orientasi sinar.
Tetapkan kedudukan kamera sebagai titik permulaan dan kira orientasi dengan menolak kedudukan kamera daripada koordinat ruang 3D yang telah ditukar kepada orientasi. Orientasi ditetapkan kepada vektor unit menggunakan kaedah Vector3.Normalize.
// マークが指す方向へのレイを作成
Ray ray = new Ray(this.cameraPosition,
Vector3.Normalize(worldPoint - this.cameraPosition));
Ray
Constructor
Buat contoh struktur "Ray" yang mengandungi parameter garisan.
Kedudukan | Vektor3 | Titik permulaan sinar |
Arah | Vektor3 | Arah sinar |
Hitbox bola dan sinar
Kelas ModelMesh yang dimuatkan daripada saluran paip kandungan mengandungi data sfera yang merangkumi mesh, dipanggil sifat BoundingSphere. Dengan menentukan Sinar yang baru anda buat dalam kaedah Persilangan kelas ini, anda boleh menyemak sama ada sfera dan sinar berlanggar.
Sekiranya berlaku perlanggaran, jarak antara permulaan sinar dan titik perlanggaran dikembalikan. Jika tiada perlanggaran, nol dikembalikan, jadi sampel menyemak sama ada terdapat perlanggaran dengan penghakiman nol.
Walau bagaimanapun, kaedah ini mengandaikan bahawa model itu terletak di asalnya. Jika anda menggerakkan model, anda perlu mengubah sinar semasa model bergerak.
By the way, model sampel ini adalah sfera, jadi saya fikir ia boleh ditentukan dengan tepat.
// 球とレイとの当たり判定を行う
this.isHit = false;
foreach (ModelMesh mesh in this.model.Meshes)
{
if (mesh.BoundingSphere.Intersects(ray) != null)
{
// 球とレイは交差している
this.isHit = true;
break;
}
}
BoundingSphere.Intersects
Kaedah
Pengesanan perlanggaran antara bola inklusif dan sinar dilakukan.
Ray | Ray | Ray untuk menilai perlanggaran dengan bola |
Nilai Pulangan | Boleh dibatalkan<terapung> | Dalam kes perlanggaran, ia mengembalikan jarak antara titik permulaan sinar dan titik impak dengan sfera. Jika tiada perlanggaran, nol dikembalikan. |
Semua Kod
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);
}
}
}