Pilih model dari posisinya di layar
ringkasan
Ini memungkinkan Anda untuk memilih model pada posisi kursor mouse. Saat Anda mengarahkan kursor ke model, teks Hit berubah menjadi True.
Lingkungan operasi
Prasyarat
Versi XNA yang Didukung |
|
Platform yang Didukung |
|
Versi Vertex Shader yang Diperlukan Windows | 2.0 |
Versi Pixel Shader yang Diperlukan Windows | 2.0 |
Lingkungan operasi
balei-balei |
|
Cara bekerja dengan sampel
Keyboard kerjaPengontrol Xbox | 360sentuhan mouse | |||
---|---|---|---|---|
Gerakan kursor | ↑↓←→ | Tongkat Kiri | Gerakan tikus | - |
zat
Ubah koordinat layar menjadi koordinat spasial 3D
Anda mungkin ingin memilih model dalam ruang 3D dengan mouse. Dalam hal ini, perlu untuk mengubah titik koordinat dua dimensi di layar menjadi koordinat tiga dimensi di mana model ada dan melakukan penilaian hit.
Namun, untuk memperluas elemen dari 2D ke 3D, tidak mungkin menemukan titik dengan koordinat 3D dari koordinat layar 2D hanya X dan Y. Misalnya, jika Anda membayangkan benar-benar mengklik layar, Anda akan mengerti bahwa tidak mungkin untuk menentukan apakah posisi dalam ruang 3D saat Anda mengklik berada di depan objek, objek itu sendiri, atau di belakang objek.
Oleh karena itu, alih-alih merepresentasikan posisi yang diklik sebagai titik, posisi ini diperlakukan sebagai garis yang membentang dari posisi kamera ke arah klik. Dengan melakukan deteksi tabrakan antara garis dan objek, dimungkinkan untuk memilih model. Omong-omong, parameter garis dapat ditangani di XNA dengan struktur yang disebut Ray.
Dapatkan posisi dalam ruang 3D dari posisi layar
XNA tidak memiliki metode untuk menemukan garis ke arah klik di layar. Namun, karena dimungkinkan untuk menemukan titik dalam ruang 3D dengan menentukan koordinat dan kedalaman layar, dimungkinkan untuk menemukan garis dengan menghubungkan posisi kamera dan titik koordinat ruang 3D yang diubah pada kedalaman tertentu.
Menemukan koordinat ruang objek dari koordinat ruang layar mudah dilakukan menggunakan metode "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);
Argumen pertama adalah Vector3 dengan koordinat dan kedalaman layar. Atur X, Y ke koordinat layar, dan Z ke nilai kedalaman. Kedalaman tergantung pada parameter "nearPlaneDistance" dan "farPlaneDistance" dari matriks proyeksi, di mana Anda dapat menentukan 0.0f untuk menemukan jarak dari posisi kamera ke nearPlaneDistance, dan 1.0f untuk menentukan jarak dari posisi kamera ke farPlaneDistance.
Argumen kedua adalah matriks proyeksi, dan argumen ketiga adalah matriks tampilan.
Anda dapat menemukan vektor ruang objek sebagai nilai pengembalian.
Viewport.Unproject
Metode
Memproyeksikan vektor dari ruang layar ke ruang objek.
sumber | Vektor3 | Vektor koordinat layar untuk dikonversi ke koordinat ruang objek |
Proyeksi | Matriks | Matriks proyektif |
melihat | Matriks | Lihat Matriks |
dunia | Matriks | Menentukan transformasi koordinat matriks dunia akhir yang akan dilakukan |
Nilai Pengembalian | Vektor3 | Mendapatkan vektor di ruang objek |
Buat Sinar
Parameter garis dapat berupa struktur Ray. Argumen pertama untuk konstruktor adalah titik awal sinar, dan argumen kedua adalah orientasi sinar.
Tetapkan posisi kamera sebagai titik awal dan hitung orientasi dengan mengurangi posisi kamera dari koordinat ruang 3D yang telah dikonversi menjadi orientasi. Orientasi diatur ke vektor satuan menggunakan metode Vector3.Normalize.
// マークが指す方向へのレイを作成
Ray ray = new Ray(this.cameraPosition,
Vector3.Normalize(worldPoint - this.cameraPosition));
Ray
pembangun
Buat contoh struktur "Ray" yang berisi parameter garis.
posisi | Vektor3 | Titik awal sinar |
arah | Vektor3 | Arah sinar |
Hitbox bola dan sinar
Kelas ModelMesh yang dimuat dari alur konten berisi data bola yang mencakup mesh, yang disebut properti BoundingSphere. Dengan menentukan Ray yang baru saja Anda buat dalam metode Intersects dari kelas ini, Anda dapat memeriksa apakah bola dan sinar bertabrakan.
Jika terjadi tabrakan, jarak antara awal sinar dan titik tumbukan dikembalikan. Jika tidak ada tabrakan, null dikembalikan, sehingga sampel memeriksa apakah ada tabrakan dengan penilaian nol.
Namun, metode ini mengasumsikan bahwa model terletak di asal. Jika Anda memindahkan model, Anda perlu mengubah sinar saat model bergerak.
Ngomong-ngomong, model sampel ini adalah bola, jadi saya pikir itu dapat ditentukan secara akurat.
// 球とレイとの当たり判定を行う
this.isHit = false;
foreach (ModelMesh mesh in this.model.Meshes)
{
if (mesh.BoundingSphere.Intersects(ray) != null)
{
// 球とレイは交差している
this.isHit = true;
break;
}
}
BoundingSphere.Intersects
Metode
Deteksi tabrakan antara bola inklusif dan sinar dilakukan.
ikan pari | Ikan pari | Ray untuk menilai tabrakan dengan bola |
Nilai Pengembalian | Nullable<float> | Dalam kasus tabrakan, ini mengembalikan jarak antara titik awal sinar dan titik tumbukan dengan bola. Jika tidak ada tabrakan, null dikembalikan. |
Semua Kode
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);
}
}
}