Chọn một mô hình từ vị trí của nó trên màn hình
tóm tắt
Nó cho phép bạn chọn mô hình tại vị trí của con trỏ chuột. Khi bạn di con trỏ qua mô hình, văn bản Hit sẽ thay đổi thành True.
Môi trường hoạt động
Điều kiện tiên quyết
Các phiên bản XNA được hỗ trợ |
|
Nền tảng được hỗ trợ |
|
Phiên bản Vertex Shader yêu cầu của Windows | 2.0 |
Phiên bản Pixel Shader yêu cầu của Windows | 2.0 |
Môi trường hoạt động
nền tảng |
|
Cách làm việc với mẫu
điều khiển XboxBàn phím hoạt độngBộ | 360cảm | ứng chuột | ||
---|---|---|---|---|
Chuyển động con trỏ | ↑↓←→ | Gậy trái | Chuyển động chuột | - |
chất
Chuyển đổi tọa độ màn hình thành tọa độ không gian 3D
Bạn có thể muốn chọn một mô hình trong không gian 3D bằng chuột. Trong trường hợp này, cần phải chuyển đổi điểm tọa độ hai chiều trên màn hình thành tọa độ ba chiều nơi mô hình tồn tại và thực hiện đánh giá trúng đích.
Tuy nhiên, để mở rộng phần tử từ 2D sang 3D, không thể tìm thấy điểm có tọa độ 3D từ tọa độ màn hình 2D chỉ X và Y. Ví dụ, nếu bạn tưởng tượng thực sự nhấp vào màn hình, bạn sẽ hiểu rằng không thể xác định xem vị trí trong không gian 3D khi bạn nhấp là phía trước đối tượng, chính đối tượng hay phía sau đối tượng.
Do đó, thay vì biểu diễn vị trí được nhấp dưới dạng một dấu chấm, nó được coi là một đường thẳng kéo dài từ vị trí máy ảnh theo hướng nhấp chuột. Bằng cách thực hiện phát hiện va chạm giữa đường thẳng và đối tượng, có thể chọn một mô hình. Nhân tiện, các tham số dòng có thể được xử lý trong XNA bằng một cấu trúc gọi là Ray.
Lấy vị trí trong không gian 3D từ vị trí của màn hình
XNA không có phương pháp để tìm một đường thẳng theo hướng nhấp chuột trên màn hình. Tuy nhiên, vì có thể tìm một điểm trong không gian 3D bằng cách chỉ định tọa độ và độ sâu màn hình, nên có thể tìm một đường thẳng bằng cách kết nối vị trí của máy ảnh và điểm tọa độ không gian 3D được chuyển đổi ở một độ sâu cụ thể.
Tìm tọa độ không gian đối tượng từ tọa độ không gian màn hình rất dễ thực hiện bằng phương thức "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);
Đối số đầu tiên là Vector3 với tọa độ và độ sâu màn hình. Đặt X, Y thành tọa độ của màn hình và Z thành giá trị độ sâu. Độ sâu phụ thuộc vào các tham số "nearPlaneDistance" và "farPlaneDistance" của ma trận chiếu, trong đó bạn có thể chỉ định 0.0f để tìm khoảng cách từ vị trí máy ảnh đến nearPlaneDistance và 1.0f để xác định khoảng cách từ vị trí máy ảnh đến farPlaneDistance.
Đối số thứ hai là ma trận chiếu và đối số thứ ba là ma trận chế độ xem.
Bạn có thể tìm thấy một vectơ không gian đối tượng làm giá trị trả về.
Viewport.Unproject
phương pháp
Chiếu một vectơ từ không gian màn hình vào không gian đối tượng.
nguồn | Vectơ 3 | Vector tọa độ màn hình để chuyển đổi thành tọa độ không gian đối tượng |
Chiếu | Ma trận | Ma trận chiếu |
cảnh | Ma trận | Xem ma trận |
thế giới | Ma trận | Chỉ định biến đổi tọa độ ma trận thế giới cuối cùng sẽ được thực hiện |
Giá trị trả về | Vectơ 3 | Lấy một vectơ trong không gian đối tượng |
Tạo một tia
Các thông số đường thẳng có thể là cấu trúc Ray. Đối số đầu tiên cho hàm tạo là điểm bắt đầu của tia và đối số thứ hai là hướng của tia.
Đặt vị trí của máy ảnh làm điểm bắt đầu và tính toán hướng bằng cách trừ vị trí của máy ảnh từ tọa độ không gian 3D đã được chuyển đổi thành hướng. Hướng được đặt thành một vectơ đơn vị bằng phương pháp Vector3.Normalize.
// マークが指す方向へのレイを作成
Ray ray = new Ray(this.cameraPosition,
Vector3.Normalize(worldPoint - this.cameraPosition));
Ray
Constructor
Tạo một thực thể của cấu trúc "Ray" chứa các tham số của đường thẳng.
vị trí | Vectơ 3 | Điểm bắt đầu của tia |
hướng | Vectơ 3 | Hướng của tia |
Hitbox bóng và tia
Lớp ModelMesh được tải từ quy trình nội dung chứa dữ liệu hình cầu bao gồm lưới, được gọi là thuộc tính BoundingSphere. Bằng cách chỉ định Ray bạn vừa tạo trong phương thức Intersects của lớp này, bạn có thể kiểm tra xem hình cầu và tia có va chạm hay không.
Trong trường hợp va chạm, khoảng cách giữa điểm bắt đầu của tia và điểm va chạm được trả về. Nếu không có va chạm, null được trả về, vì vậy mẫu kiểm tra xem có va chạm hay không bằng phán đoán null.
Tuy nhiên, phương pháp này giả định rằng mô hình nằm ở điểm gốc. Nếu bạn đang di chuyển mô hình, bạn sẽ cần phải biến đổi các tia khi mô hình di chuyển.
Nhân tiện, mô hình mẫu này là một hình cầu, vì vậy tôi nghĩ nó có thể được xác định chính xác.
// 球とレイとの当たり判定を行う
this.isHit = false;
foreach (ModelMesh mesh in this.model.Meshes)
{
if (mesh.BoundingSphere.Intersects(ray) != null)
{
// 球とレイは交差している
this.isHit = true;
break;
}
}
BoundingSphere.Intersects
phương pháp
Phát hiện va chạm giữa quả bóng bao gồm và tia được thực hiện.
cá đuối | Cá đuối | Ray để đánh giá va chạm với quả bóng |
Giá trị trả về | Nullable<float> | Trong trường hợp va chạm, nó trả về khoảng cách giữa điểm bắt đầu của tia và điểm va chạm với hình cầu. Nếu không có va chạm, null sẽ được trả về. |
Tất cả mã
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);
}
}
}