یک مدل را از موقعیت آن روی صفحه انتخاب کنید

صفحه به روز شده :
تاریخ ایجاد صفحه :

خلاصه

این به شما امکان می دهد مدل را در موقعیت نشانگر ماوس انتخاب کنید. وقتی نشانگر را روی مدل نگه می دارید، نوشتار Hit به True تغییر می کند.

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

محیط عملیاتی

پیش نیازها

نسخه های XNA پشتیبانی شده
  • 4.0
پلتفرم های پشتیبانی شده
  • ویندوز (XP SP2 یا بالاتر، ویستا، 7)
  • ایکس باکس 360
  • ویندوز فون 7
نسخه Vertex Shader مورد نیاز ویندوز 2.0
ویندوز نسخه Pixel Shader مورد نیاز است 2.0

محیط عملیاتی

بستر
  • ویندوز 7
  • ایکس باکس 360
  • شبیه ساز ویندوز فون 7

نحوه کار با نمونه

صفحه کلید کار می کندکنترل ماوس 360 لمسی ماوس
حرکت مکان نما ↑↓←→ چوب چپ حرکت ماوس -

ماده

تبدیل مختصات صفحه به مختصات فضایی سه بعدی

ممکن است بخواهید مدلی را در فضای سه بعدی با ماوس انتخاب کنید. در این حالت لازم است نقطه مختصات دو بعدی روی صفحه به مختصات سه بعدی که مدل در آن وجود دارد تبدیل شود و قضاوت ضربه ای انجام شود.

با این حال، برای گسترش عنصر از دو بعدی به سه بعدی، نمی توان نقطه ای با مختصات سه بعدی از مختصات صفحه نمایش دو بعدی فقط X و Y پیدا کرد. به عنوان مثال، اگر تصور کنید که واقعا روی صفحه کلیک می کنید، متوجه خواهید شد که نمی توان تعیین کرد که آیا موقعیت در فضای سه بعدی هنگام کلیک کردن در مقابل شی، خود شی یا پشت شی است.

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

بنابراین، به جای نشان دادن موقعیت کلیک شده به عنوان یک نقطه، به عنوان خطی در نظر گرفته می شود که از موقعیت دوربین در جهت کلیک کشیده شده است. با انجام تشخیص برخورد بین خط و جسم، امکان انتخاب مدل وجود دارد. به هر حال، پارامترهای خط را می توان در XNA توسط ساختاری به نام Ray مدیریت کرد.

موقعیت را در فضای سه بعدی از موقعیت صفحه نمایش دریافت کنید

XNA روشی برای یافتن یک خط در جهت کلیک روی صفحه ندارد. با این حال، از آنجایی که می توان با تعیین مختصات و عمق صفحه نمایش، نقطه ای را در فضای سه بعدی پیدا کرد، می توان با اتصال موقعیت دوربین و نقطه مختصات فضای سه بعدی که در عمق خاصی تبدیل شده است، یک خط پیدا کرد.

یافتن مختصات فضای شی از مختصات فضای صفحه نمایش با استفاده از روش "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 دریافت یک بردار در فضای آبجکت

ایجاد یک پرتو

پارامترهای خط می توانند ساختارهای پرتو باشند. اولین استدلال به سازنده نقطه شروع پرتو و استدلال دوم جهت گیری پرتو است.

موقعیت دوربین را به عنوان نقطه شروع تنظیم کنید و با کم کردن موقعیت دوربین از مختصات فضای سه بعدی که قبلا به جهت تبدیل شده اند، جهت را محاسبه کنید. جهت گیری با استفاده از روش Vector3.Normalize روی یک بردار واحد تنظیم می شود.

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

Ray سازنده

یک نمونه از ساختار "Ray" ایجاد کنید که شامل پارامترهای خط باشد.

موقعیت بردار3 نقطه شروع پرتو
جهت بردار3 جهت پرتو

هیت باکس توپ و اشعه

کلاس ModelMesh بارگذاری شده از خط لوله محتوا حاوی داده های کره ای است که مش را در بر می گیرد که ویژگی BoundingSphere نامیده می شود. با مشخص کردن Ray که به تازگی در متد 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);
        }
    }
}