Wählen Sie ein Modell von seiner Position auf dem Bildschirm aus
Zusammenfassung
Es ermöglicht Ihnen, das Modell an der Position des Mauszeigers auszuwählen. Wenn Sie den Mauszeiger über das Modell bewegen, ändert sich der Treffertext in True.
Betriebsumgebung
Voraussetzungen
Unterstützte XNA-Versionen |
|
Unterstützte Plattformen |
|
Erforderliche Vertex-Shader-Version für Windows | 2.0 |
Erforderliche Pixel-Shader-Version für Windows | 2.0 |
Betriebsumgebung
Bahnsteig |
|
So arbeiten Sie mit der Stichprobe
Funktioniert mit TastaturXbox | 360 ControllerMaus | Touch | ||
---|---|---|---|---|
Bewegung des Cursors | ↑↓←→ | Linker Stick | Mausbewegung | - |
Substanz
Konvertieren von Bildschirmkoordinaten in 3D-Raumkoordinaten
Sie können ein Modell im 3D-Raum mit der Maus auswählen. In diesem Fall ist es notwendig, den zweidimensionalen Koordinatenpunkt auf dem Bildschirm in die dreidimensionale Koordinate umzuwandeln, in der sich das Modell befindet, und eine Trefferbeurteilung durchzuführen.
Um das Element jedoch von 2D auf 3D zu erweitern, ist es nicht möglich, einen Punkt mit 3D-Koordinaten aus 2D-Bildschirmkoordinaten von nur X und Y zu finden. Wenn Sie sich zum Beispiel vorstellen, tatsächlich auf den Bildschirm zu klicken, werden Sie verstehen, dass es nicht möglich ist, zu bestimmen, ob die Position im 3D-Raum beim Klicken vor dem Objekt, dem Objekt selbst oder hinter dem Objekt liegt.
Daher wird die angeklickte Position nicht als Punkt, sondern als Linie behandelt, die von der Kameraposition in Richtung des Klicks gestreckt wird. Durch die Kollisionserkennung zwischen der Linie und dem Objekt ist es möglich, ein Modell auszuwählen. Übrigens, Linienparameter können in XNA durch eine Struktur namens Ray behandelt werden.
Ermitteln Sie die Position im 3D-Raum aus der Position des Bildschirms
XNA verfügt nicht über eine Methode, um eine Linie in Richtung eines Klicks auf dem Bildschirm zu finden. Da es jedoch möglich ist, einen Punkt im 3D-Raum zu finden, indem man die Bildschirmkoordinaten und die Tiefe angibt, ist es möglich, eine Linie zu finden, indem man die Position der Kamera mit dem Koordinatenpunkt des 3D-Raums verbindet, der in einer bestimmten Tiefe transformiert wurde.
Das Ermitteln von Objektraumkoordinaten aus Bildschirmbereichskoordinaten ist mit der Methode "Viewport.Unproject" einfach zu bewerkstelligen.
// ビューポートを取得
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);
Das erste Argument ist ein Vector3 mit Bildschirmkoordinaten und Tiefe. Legen Sie X, Y auf die Koordinaten des Bildschirms und Z auf den Tiefenwert fest. Die Tiefe hängt von den Parametern "nearPlaneDistance" und "farPlaneDistance" der Projektionsmatrix ab, wobei Sie 0,0f angeben können, um den Abstand von der Kameraposition zu nearPlaneDistance zu bestimmen, und 1,0f, um den Abstand von der Kameraposition zu farPlaneDistance zu bestimmen.
Das zweite Argument ist die Projektionsmatrix, und das dritte Argument ist die Ansichtsmatrix.
Als Rückgabewert finden Sie einen Objektraumvektor.
Viewport.Unproject
Methode
Projiziert einen Vektor aus dem Bildschirmbereich in den Objektraum.
Quelle | Vektor3 | Bildschirmkoordinatenvektor für die Konvertierung in Objektraumkoordinaten |
Projektion | Matrix | Projektive Matrix |
ansehen | Matrix | Matrix anzeigen |
Welt | Matrix | Gibt die endgültige Koordinatentransformation der Weltmatrix-Koordinaten an, die durchgeführt werden soll |
Rückgabewerte | Vektor3 | Abrufen eines Vektors im Objektraum |
Erstellen eines Strahls
Linienparameter können Ray-Strukturen sein. Das erste Argument für den Konstruktor ist der Startpunkt des Strahls, und das zweite Argument ist die Ausrichtung des Strahls.
Legen Sie die Position der Kamera als Ausgangspunkt fest und berechnen Sie die Ausrichtung, indem Sie die Position der Kamera von den 3D-Raumkoordinaten subtrahieren, die bereits in die Ausrichtung umgewandelt wurden. Die Ausrichtung wird mit der Vector3.Normalize-Methode auf einen Einheitsvektor festgelegt.
// マークが指す方向へのレイを作成
Ray ray = new Ray(this.cameraPosition,
Vector3.Normalize(worldPoint - this.cameraPosition));
Ray
Konstruktor
Erstellen Sie eine Instanz der "Ray"-Struktur, die die Parameter der Zeile enthält.
Position | Vektor3 | Der Startpunkt des Strahls |
Richtung | Vektor3 | Richtung des Strahls |
Ball- und Ray-Hitbox
Die ModelMesh-Klasse, die aus der Inhaltspipeline geladen wird, enthält Kugeldaten, die das Netz umfassen, und wird als BoundingSphere-Eigenschaft bezeichnet. Wenn Sie den Ray angeben, den Sie gerade in der Intersects-Methode dieser Klasse erstellt haben, können Sie überprüfen, ob die Kugel und der Strahl kollidieren.
Im Falle einer Kollision wird der Abstand zwischen dem Beginn des Strahls und dem Kollisionspunkt zurückgegeben. Wenn keine Kollision vorliegt, wird null zurückgegeben, sodass im Beispiel überprüft wird, ob eine Kollision durch NULL-Urteil vorliegt.
Bei dieser Methode wird jedoch davon ausgegangen, dass sich das Modell am Ursprung befindet. Wenn Sie das Modell verschieben, müssen Sie die Strahlen transformieren, während sich das Modell bewegt.
Übrigens, dieses Beispielmodell ist eine Kugel, daher denke ich, dass es genau bestimmt werden kann.
// 球とレイとの当たり判定を行う
this.isHit = false;
foreach (ModelMesh mesh in this.model.Meshes)
{
if (mesh.BoundingSphere.Intersects(ray) != null)
{
// 球とレイは交差している
this.isHit = true;
break;
}
}
BoundingSphere.Intersects
Methode
Es wird eine Kollisionserkennung zwischen der inklusiven Kugel und dem Strahl durchgeführt.
Strahl | Strahl | Ray, um die Kollision mit dem Ball zu beurteilen |
Rückgabewerte | NULL-Werte zulassen<float> | Im Falle einer Kollision gibt sie den Abstand zwischen dem Startpunkt des Strahls und dem Aufprallpunkt mit der Kugel zurück. Wenn keine Kollision vorliegt, wird null zurückgegeben. |
Alle Codes
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);
}
}
}