Chọn một mô hình từ vị trí của nó trên màn hình

Trang Cập Nhật :
Ngày tạo trang :

tóm tắt

Nó cho phép bạn chọn mô hình tại vị trí của con trỏ chuột. Khi bạn di con trỏ qua mô hình, văn bản Hit sẽ thay đổi thành True.

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

Môi trường hoạt động

Điều kiện tiên quyết

Các phiên bản XNA được hỗ trợ
  • 4.0
Nền tảng được hỗ trợ
  • Windows (XP SP2 trở lên, Vista, 7)
  • Xbox 360
  • Windows Phone 7
Phiên bản Vertex Shader yêu cầu của Windows 2.0
Phiên bản Pixel Shader yêu cầu của Windows 2.0

Môi trường hoạt động

nền tảng
  • Cửa sổ 7
  • Xbox 360
  • Trình giả lập Windows Phone 7

Cách làm việc với mẫu

điều khiển Xbox
Bàn phím hoạt độngBộ 360cảm ứng chuột
Chuyển động con trỏ ↑↓←→ Gậy trái Chuyển động chuột -

chất

Chuyển đổi tọa độ màn hình thành tọa độ không gian 3D

Bạn có thể muốn chọn một mô hình trong không gian 3D bằng chuột. Trong trường hợp này, cần phải chuyển đổi điểm tọa độ hai chiều trên màn hình thành tọa độ ba chiều nơi mô hình tồn tại và thực hiện đánh giá trúng đích.

Tuy nhiên, để mở rộng phần tử từ 2D sang 3D, không thể tìm thấy điểm có tọa độ 3D từ tọa độ màn hình 2D chỉ X và Y. Ví dụ, nếu bạn tưởng tượng thực sự nhấp vào màn hình, bạn sẽ hiểu rằng không thể xác định xem vị trí trong không gian 3D khi bạn nhấp là phía trước đối tượng, chính đối tượng hay phía sau đối tượng.

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

Do đó, thay vì biểu diễn vị trí được nhấp dưới dạng một dấu chấm, nó được coi là một đường thẳng kéo dài từ vị trí máy ảnh theo hướng nhấp chuột. Bằng cách thực hiện phát hiện va chạm giữa đường thẳng và đối tượng, có thể chọn một mô hình. Nhân tiện, các tham số dòng có thể được xử lý trong XNA bằng một cấu trúc gọi là Ray.

Lấy vị trí trong không gian 3D từ vị trí của màn hình

XNA không có phương pháp để tìm một đường thẳng theo hướng nhấp chuột trên màn hình. Tuy nhiên, vì có thể tìm một điểm trong không gian 3D bằng cách chỉ định tọa độ và độ sâu màn hình, nên có thể tìm một đường thẳng bằng cách kết nối vị trí của máy ảnh và điểm tọa độ không gian 3D được chuyển đổi ở một độ sâu cụ thể.

Tìm tọa độ không gian đối tượng từ tọa độ không gian màn hình rất dễ thực hiện bằng phương thức "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);

Đối số đầu tiên là Vector3 với tọa độ và độ sâu màn hình. Đặt X, Y thành tọa độ của màn hình và Z thành giá trị độ sâu. Độ sâu phụ thuộc vào các tham số "nearPlaneDistance" và "farPlaneDistance" của ma trận chiếu, trong đó bạn có thể chỉ định 0.0f để tìm khoảng cách từ vị trí máy ảnh đến nearPlaneDistance và 1.0f để xác định khoảng cách từ vị trí máy ảnh đến farPlaneDistance.

Đối số thứ hai là ma trận chiếu và đối số thứ ba là ma trận chế độ xem.

Bạn có thể tìm thấy một vectơ không gian đối tượng làm giá trị trả về.

Viewport.Unproject phương pháp

Chiếu một vectơ từ không gian màn hình vào không gian đối tượng.

nguồn Vectơ 3 Vector tọa độ màn hình để chuyển đổi thành tọa độ không gian đối tượng
Chiếu Ma trận Ma trận chiếu
cảnh Ma trận Xem ma trận
thế giới Ma trận Chỉ định biến đổi tọa độ ma trận thế giới cuối cùng sẽ được thực hiện
Giá trị trả về Vectơ 3 Lấy một vectơ trong không gian đối tượng

Tạo một tia

Các thông số đường thẳng có thể là cấu trúc Ray. Đối số đầu tiên cho hàm tạo là điểm bắt đầu của tia và đối số thứ hai là hướng của tia.

Đặt vị trí của máy ảnh làm điểm bắt đầu và tính toán hướng bằng cách trừ vị trí của máy ảnh từ tọa độ không gian 3D đã được chuyển đổi thành hướng. Hướng được đặt thành một vectơ đơn vị bằng phương pháp Vector3.Normalize.

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

Ray Constructor

Tạo một thực thể của cấu trúc "Ray" chứa các tham số của đường thẳng.

vị trí Vectơ 3 Điểm bắt đầu của tia
hướng Vectơ 3 Hướng của tia

Hitbox bóng và tia

Lớp ModelMesh được tải từ quy trình nội dung chứa dữ liệu hình cầu bao gồm lưới, được gọi là thuộc tính BoundingSphere. Bằng cách chỉ định Ray bạn vừa tạo trong phương thức Intersects của lớp này, bạn có thể kiểm tra xem hình cầu và tia có va chạm hay không.

Trong trường hợp va chạm, khoảng cách giữa điểm bắt đầu của tia và điểm va chạm được trả về. Nếu không có va chạm, null được trả về, vì vậy mẫu kiểm tra xem có va chạm hay không bằng phán đoán null.

Tuy nhiên, phương pháp này giả định rằng mô hình nằm ở điểm gốc. Nếu bạn đang di chuyển mô hình, bạn sẽ cần phải biến đổi các tia khi mô hình di chuyển.

Nhân tiện, mô hình mẫu này là một hình cầu, vì vậy tôi nghĩ nó có thể được xác định chính xác.

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

BoundingSphere.Intersects phương pháp

Phát hiện va chạm giữa quả bóng bao gồm và tia được thực hiện.

cá đuối Cá đuối Ray để đánh giá va chạm với quả bóng
Giá trị trả về Nullable<float> Trong trường hợp va chạm, nó trả về khoảng cách giữa điểm bắt đầu của tia và điểm va chạm với hình cầu. Nếu không có va chạm, null sẽ được trả về.

Tất cả mã

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