Výběr modelu podle jeho pozice na obrazovce

Stránky aktualizovány :
Datum vytvoření stránky :

shrnutí

Umožňuje vybrat model na pozici kurzoru myši. Když umístíte kurzor nad model, text Zásah se změní na Pravda.

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

Provozní prostředí

Požadavky

Podporované verze XNA
  • 4.0
Podporované platformy
  • Windows (XP SP2 nebo novější, Vista, 7)
  • Xbox 360
  • Windows Phone 7
Požadovaná verze Vertex Shader systému Windows 2.0
Windows Požadovaná verze pixel shaderu 2.0

Provozní prostředí

nástupiště
  • Systém Windows 7
  • Xbox 360
  • Emulátor systému Windows Phone 7

Jak pracovat s ukázkou

myš
Klávesnice WorksOvladač Xbox 360 dotyková
Pohyb kurzoru ↑↓←→ Levá páčka Pohyb myši -

hmota

Převod souřadnic obrazovky na 3D prostorové souřadnice

Možná budete chtít vybrat model ve 3D prostoru pomocí myši. V tomto případě je nutné převést dvourozměrný souřadnicový bod na obrazovce na trojrozměrnou souřadnici, kde model existuje, a provést posouzení zásahu.

Aby však bylo možné prvek rozšířit z 2D do 3D, není možné najít bod s 3D souřadnicemi z 2D souřadnic obrazovky pouze X a Y. Pokud si například představíte, že skutečně kliknete na obrazovku, pochopíte, že není možné určit, zda je poloha ve 3D prostoru při kliknutí před objektem, samotným objektem nebo za objektem.

オブジェクトの選択対象の判断

Proto se s pozicí, na kterou se klikne, nezachází jako s tečkou, ale jako s čárou nataženou z pozice kamery ve směru kliknutí. Provedením detekce kolizí mezi linií a objektem je možné vybrat model. Mimochodem, parametry řádků mohou být v XNA zpracovány strukturou zvanou Ray.

Získejte pozici ve 3D prostoru z pozice obrazovky

XNA nemá metodu pro nalezení čáry ve směru kliknutí na obrazovce. Protože je však možné najít bod ve 3D prostoru zadáním souřadnic obrazovky a hloubky, je možné najít čáru spojením polohy kamery a bodu souřadnic 3D prostoru transformovaného v určité hloubce.

Nalezení souřadnic objektového prostoru ze souřadnic prostoru obrazovky lze snadno provést pomocí metody "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);

Prvním argumentem je Vektor3 se souřadnicemi a hloubkou obrazovky. Nastavte X, Y na souřadnice obrazovky a Z na hodnotu hloubky. Hloubka závisí na parametrech "nearPlaneDistance" a "farPlaneDistance" projekční matice, kde můžete zadat 0.0f pro zjištění vzdálenosti od polohy kamery k nearPlaneDistance a 1.0f pro určení vzdálenosti od polohy kamery k farPlaneDistance.

Druhým argumentem je projekční matice a třetím argumentem je matice zobrazení.

Jako návratovou hodnotu můžete najít vektor objektového prostoru.

Viewport.Unproject metoda

Promítne vektor z prostoru obrazovky do prostoru objektů.

zdroj Vektor3 Vektor souřadnic obrazovky pro převod na souřadnice objektového prostoru
projekce Matice Projektivní matice
pohled Matice Zobrazit matici
svět Matice Určuje konečnou transformaci souřadnic globální matice, která má být provedena
Návratové hodnoty Vektor3 Získání vektoru v objektovém prostoru

Vytvoření paprsku

Parametry čar mohou být Ray struktury. Prvním argumentem konstruktoru je počáteční bod polopřímky a druhým argumentem je orientace polopřímky.

Nastavte polohu kamery jako počáteční bod a vypočítejte orientaci odečtením polohy kamery od souřadnic 3D prostoru, které již byly převedeny na orientaci. Orientace je nastavena na jednotkový vektor pomocí metody Vector3.Normalize.

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

Ray konstruktor

Vytvořte instanci struktury "Ray", která obsahuje parametry řádku.

postavení Vektor3 Počáteční bod paprsku
směr Vektor3 Směr paprsku

Míč a paprsek hitbox

Třída ModelMesh načtená z kanálu obsahu obsahuje data koulí, která zahrnují síť, nazývaná vlastnost BoundingSphere. Určením paprsku, který jste právě vytvořili v metodě Intersects této třídy, můžete zkontrolovat, zda se koule a paprsek nesrážejí.

V případě kolize se vrátí vzdálenost mezi začátkem polopřímky a bodem kolize. Pokud nedojde ke kolizi, vrátí se hodnota null, takže ukázka zkontroluje, zda došlo ke kolizi, podle úsudku null.

Tato metoda však předpokládá, že model je umístěn v počátku. Pokud model přesouváte, budete muset transformovat paprsky při pohybu modelu.

Mimochodem, tento vzorový model je koule, takže si myslím, že ji lze přesně určit.

// 球とレイとの当たり判定を行う
this.isHit = false;
foreach (ModelMesh mesh in this.model.Meshes)
{
    if (mesh.BoundingSphere.Intersects(ray) != null)
    {
        // 球とレイは交差している
        this.isHit = true;
        break;
    }
}

BoundingSphere.Intersects metoda

Provádí se detekce kolize mezi přiloženým míčem a paprskem.

paprsek Paprsek Paprsek pro posouzení kolize s míčem
Návratové hodnoty Nullable<float> V případě kolize vrací vzdálenost mezi počátečním bodem paprsku a bodem dopadu na kouli. Pokud nedojde ke kolizi, vrátí se hodnota null.

Všechny kódy

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);
        }
    }
}