Виберіть модель за її розташуванням на екрані

Сторінка оновлюється :
Дата створення сторінки :

зведення

Вона дозволяє вибрати модель в положенні курсору миші. Коли ви наводите курсор на модель, текст звернення змінюється на True.

スクリーン上の位置からモデルを選択

Робоче середовище

Передумови

Підтримувані версії XNA
  • 4.0
Підтримувані платформи
  • Windows (XP SP2 або новішої версії, Vista, 7)
  • Xbox 360
  • Телефон Windows Phone 7
Потрібна версія Vertex Shader для Windows 2.0
Потрібна версія піксельного шейдера Windows 2.0

Робоче середовище

платформа
  • Вікна 7
  • Xbox 360
  • Емулятор Windows Phone 7

Як працювати зі зразком

Працює клавіатура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);

Першим аргументом є Вектор3 з координатами екрана та глибиною. Встановіть X, Y — координати екрана, а Z — значення глибини. Глибина залежить від параметрів "nearPlaneDistance" та "farPlaneDistance" проекційної матриці, де можна вказати 0.0f для знаходження відстані від позиції камери до nearPlaneDistance, та 1.0f для визначення відстані від положення камери до farPlaneDistance.

Другий аргумент — це проекційна матриця, а третій аргумент — матриця представлення.

Ви можете знайти вектор об'єкт-простір як значення, що повертається.

Viewport.Unproject метод

Проектує вектор з екранного простору в простір об'єктів.

джерело Вектор3 Вектор координат екрана для перетворення в координати об'єктного простору
Проекції Матриця Проективна матриця
вид Матриця Матриця перегляду
світ Матриця Визначає кінцеве перетворення координат світової матриці, яке має бути виконано
Значення, що повертаються Вектор3 Отримати вектор у просторі об'єктів

Створення променя

Лінійні параметри можуть бути променевими структурами. Перший аргумент конструктора – це початкова точка променя, а другий аргумент – орієнтація променя.

Встановіть положення камери як початкову точку та розрахуйте орієнтацію, віднявши положення камери з координат 3D-простору, які вже були перетворені в орієнтацію. Орієнтація встановлюється на одиничний вектор за допомогою методу Vector3.Normalize.

// マークが指す方向へのレイを作成
Ray ray = new Ray(this.cameraPosition,
                    Vector3.Normalize(worldPoint - this.cameraPosition));

Ray будівник

Створити екземпляр структури "Ray", який містить параметри лінії.

позиція Вектор3 Початкова точка променя
напрям Вектор3 Напрямок променя

Хітбокс з м'ячем і променями

Клас ModelMesh, завантажений з конвеєра вмісту, містить дані сфери, які охоплюють сіть, які називаються властивістю BoundingSphere. Вказавши щойно створений вами промінь у методі Intersects цього класу, ви можете перевірити, чи не стикаються сфера та промінь.

У разі зіткнення повертається відстань між початком променя і точкою зіткнення. Якщо колізії немає, повертається 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);
        }
    }
}