Выберите модель из ее положения на экране
сводка
Он позволяет выбрать модель по положению курсора мыши. При наведении курсора на модель текст Hit изменяется на True.
Операционная среда
Необходимые условия
Поддерживаемые версии XNA |
|
Поддерживаемые платформы |
|
Windows Требуемая версия вершинного шейдера | 2.0 |
Требуемая версия пиксельного шейдера Windows | 2.0 |
Операционная среда
платформа |
|
Как работать с образцом
Работает клавиатураXbox | 360 контроллер мыши | touch | ||
---|---|---|---|---|
Перемещение курсора | ↑↓←→ | Левый джойстик | Движение мыши | - |
вещество
Преобразование экранных координат в пространственные 3D координаты
Вы можете выбрать модель в 3D-пространстве с помощью мыши. В этом случае необходимо преобразовать двумерную координатную точку на экране в трехмерную координату, в которой существует модель, и выполнить оценку попадания.
Однако для того, чтобы расширить элемент из 2D в 3D, невозможно найти точку с 3D координатами из 2D экранных координат только X и Y. Например, если вы представите себе реальный клик по экрану, вы поймете, что невозможно определить, находится ли положение в 3D-пространстве при клике перед объектом, самим объектом или позади объекта.
Таким образом, вместо того, чтобы представлять положение щелчка в виде точки, оно рассматривается как линия, протянутая от положения камеры в направлении щелчка. Выполняя обнаружение коллизий между линией и объектом, можно выбрать модель. Кстати, параметры линий могут обрабатываться в XNA структурой под названием Ray.
Получение положения в 3D-пространстве из положения экрана
В XNA нет метода для поиска линии в направлении клика на экране. Однако, поскольку можно найти точку в 3D пространстве, указав координаты экрана и глубину, можно найти линию, соединив положение камеры и точку координат 3D пространства, преобразованную на определенной глубине.
Найти координаты пространства объекта по координатам экранного пространства легко с помощью метода "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);
Первый аргумент — это Vector3 с экранными координатами и глубиной. Установите X, Y в качестве координат экрана, а Z в качестве значения глубины. Глубина зависит от параметров "nearPlaneDistance" и "farPlaneDistance" матрицы проекции, где можно указать 0.0f для определения расстояния от положения камеры до nearPlaneDistance, и 1.0f для определения расстояния от положения камеры до farPlaneDistance.
Второй аргумент — это матрица проекции, а третий аргумент — матрица представления.
В качестве возвращаемого значения можно найти вектор пространства объектов.
Viewport.Unproject
метод
Проецирует вектор из экранного пространства в пространство объекта.
источник | Вектор3 | Вектор экранных координат для преобразования в координаты пространства-объекта |
проекция | Матрица | Проективная матрица |
вид | Матрица | Посмотреть матрицу |
мир | Матрица | Указывает окончательное преобразование координат мировой матрицы, которое должно быть выполнено |
Возвращаемые значения | Вектор3 | Получение вектора в пространстве объектов |
Создание луча
Параметры линии могут быть структурами Ray. Первый аргумент конструктора — это начальная точка луча, а второй аргумент — ориентация луча.
Установите положение камеры в качестве начальной точки и рассчитайте ориентацию, вычитая положение камеры из координат 3D-пространства, которые уже были преобразованы в ориентацию. Ориентация задается в виде единичного вектора с помощью метода Vector3.Normalize.
// マークが指す方向へのレイを作成
Ray ray = new Ray(this.cameraPosition,
Vector3.Normalize(worldPoint - this.cameraPosition));
Ray
конструктор
Создаем экземпляр структуры "Луч", который содержит параметры линии.
позиция | Вектор3 | Начальная точка луча |
направление | Вектор3 | Направление луча |
Хитбокс шара и луча
Класс ModelMesh, загруженный из конвейера содержимого, содержит данные сферы, охватывающие сетку, и называется свойством BoundingSphere. Указав только что созданный луч в методе Intersects этого класса, вы можете проверить, не сталкиваются ли сфера и луч.
В случае столкновения возвращается расстояние между началом луча и точкой столкновения. Если коллизии нет, возвращается null, поэтому образец проверяет, есть ли коллизия по суждению null.
Однако этот метод предполагает, что модель находится в начале координат. Если вы перемещаете модель, вам потребуется преобразовывать лучи при движении модели.
Кстати, этот образец модели представляет собой сферу, поэтому я думаю, что ее можно точно определить.
// 球とレイとの当たり判定を行う
this.isHit = false;
foreach (ModelMesh mesh in this.model.Meshes)
{
if (mesh.BoundingSphere.Intersects(ray) != null)
{
// 球とレイは交差している
this.isHit = true;
break;
}
}
BoundingSphere.Intersects
метод
Выполняется обнаружение столкновений между включенным шаром и лучом.
луч | Луч | Рэй судит столкновение с мячом |
Возвращаемые значения | Nullable<float> | В случае столкновения он возвращает расстояние между начальной точкой луча и точкой удара со сферой. Если коллизии нет, возвращается null. |
Все коды
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);
}
}
}