Selectați un model din poziția sa pe ecran
rezumat
Vă permite să selectați modelul în poziția cursorului mouse-ului. Când treceți cursorul peste model, textul Accesare se schimbă în Adevărat.
Mediul de operare
Cerințe preliminare
Versiuni XNA acceptate |
|
Platforme acceptate |
|
Versiunea Vertex Shader necesară pentru Windows | 2.0 |
Versiunea Pixel Shader necesară pentru Windows | 2.0 |
Mediul de operare
peron |
|
Cum se lucrează cu eșantionul
Tastatură de lucruControler Xbox | 360 tactil de mouse | |||
---|---|---|---|---|
Mișcarea cursorului | ↑↓←→ | Stick-ul stânga | Mișcarea mouse-ului | - |
substanță
Convertiți coordonatele ecranului în coordonate spațiale 3D
Poate doriți să selectați un model în spațiul 3D cu un mouse. În acest caz, este necesar să convertiți punctul de coordonate bidimensional de pe ecran în coordonata tridimensională în care există modelul și să efectuați o judecată de lovitură.
Cu toate acestea, pentru a extinde elementul de la 2D la 3D, nu este posibil să găsiți un punct cu coordonate 3D din coordonatele ecranului 2D doar X și Y. De exemplu, dacă vă imaginați că faceți clic pe ecran, veți înțelege că nu este posibil să determinați dacă poziția în spațiul 3D atunci când faceți clic este în fața obiectului, a obiectului în sine sau în spatele obiectului.
Prin urmare, în loc să reprezinte poziția pe care s-a făcut clic ca un punct, aceasta este tratată ca o linie întinsă din poziția camerei în direcția clicului. Prin efectuarea detectării coliziunilor între linie și obiect, este posibil să selectați un model. Apropo, parametrii de linie pot fi gestionați în XNA de o structură numită Ray.
Obțineți poziția în spațiul 3D din poziția ecranului
XNA nu are o metodă de a găsi o linie în direcția unui clic pe ecran. Cu toate acestea, deoarece este posibil să găsiți un punct în spațiul 3D specificând coordonatele ecranului și adâncimea, este posibil să găsiți o linie conectând poziția camerei și punctul de coordonate spațial 3D transformat la o anumită adâncime.
Găsirea coordonatelor spațiului obiect din coordonatele spațiului ecranului este ușor de făcut folosind metoda "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);
Primul argument este un Vector3 cu coordonatele ecranului și adâncimea. Setați X, Y la coordonatele ecranului și Z la valoarea adâncimii. Adâncimea depinde de parametrii "nearPlaneDistance" și "farPlaneDistance" ai matricei de proiecție, unde puteți specifica 0.0f pentru a găsi distanța de la poziția camerei la nearPlaneDistance și 1.0f pentru a determina distanța de la poziția camerei la farPlaneDistance.
Al doilea argument este matricea de proiecție, iar al treilea argument este matricea de vizualizare.
Puteți găsi un vector de spațiu obiect ca valoare returnată.
Viewport.Unproject
metodă
Proiectează un vector din spațiul ecranului în spațiul obiectului.
sursă | Vector3 | Vector de coordonate de ecran pentru conversia în coordonate spațiu-obiect |
proiecție | Matrice | Matrice proiectivă |
vedere | Matrice | Vezi matricea |
lume | Matrice | Specifică transformarea finală a coordonatelor matricei mondiale care trebuie efectuată |
Valori returnate | Vector3 | Obțineți un vector în spațiul obiectelor |
Creați o rază
Parametrii de linie pot fi structuri Ray. Primul argument al constructorului este punctul de plecare al razei, iar al doilea argument este orientarea razei.
Setați poziția camerei ca punct de plecare și calculați orientarea scăzând poziția camerei din coordonatele spațiale 3D care au fost deja convertite în orientare. Orientarea este setată la un vector unitar utilizând metoda Vector3.Normalize.
// マークが指す方向へのレイを作成
Ray ray = new Ray(this.cameraPosition,
Vector3.Normalize(worldPoint - this.cameraPosition));
Ray
constructor
Creați o instanță a structurii "Ray" care conține parametrii liniei.
poziție | Vector3 | Punctul de plecare al razei |
direcție | Vector3 | Direcția razei |
Hitbox cu bile și raze
Clasa ModelMesh încărcată din conducta de conținut conține date sferice care cuprind plasa, numită proprietatea BoundingSphere. Specificând raza pe care tocmai ați creat-o în metoda Intersects a acestei clase, puteți verifica dacă sfera și raza se ciocnesc.
În cazul unei coliziuni, distanța dintre începutul razei și punctul de coliziune este returnată. Dacă nu există nicio coliziune, se returnează null, astfel încât eșantionul verifică dacă există o coliziune prin judecată nulă.
Cu toate acestea, această metodă presupune că modelul este localizat la origine. Dacă mutați modelul, va trebui să transformați razele pe măsură ce modelul se mișcă.
Apropo, acest model eșantion este o sferă, așa că cred că poate fi determinat cu precizie.
// 球とレイとの当たり判定を行う
this.isHit = false;
foreach (ModelMesh mesh in this.model.Meshes)
{
if (mesh.BoundingSphere.Intersects(ray) != null)
{
// 球とレイは交差している
this.isHit = true;
break;
}
}
BoundingSphere.Intersects
metodă
Se efectuează detectarea coliziunii între mingea incluzivă și rază.
rază | Rază | Ray pentru a judeca coliziunea cu mingea |
Valori returnate | Nullable<float> | În cazul unei coliziuni, returnează distanța dintre punctul de pornire al razei și punctul de impact cu sfera. Dacă nu există nicio coliziune, se returnează null. |
Toate codurile
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);
}
}
}