Editor de jogos - Quando a tela é clicada, como você identifica o objeto clicado?

7

Estou tentando criar um editor de jogos, atualmente apenas colocando diferentes tipos de formas e tal. Estou fazendo isso no Windows Forms enquanto desenha o 3D com o XNA. Portanto, se eu tiver algumas Formas na tela e clicar na tela, quero poder identificar "em qual" desses objetos você clicou.

Qual é o melhor método para isso? Como ter dois objetos um atrás do outro, ele deve ser capaz de reconhecer o que está na frente e não o que está atrás e também se eu girar a câmera e clicar no objeto por trás dela - ele deve identificá-lo e não o primeiro.

Existem maneiras inteligentes de fazer isso?

Deukalion
fonte
6
O processo de determinar em qual objeto foi clicado é geralmente chamado de 'picking'. Algumas pesquisas no Google por algo como 'xna picking' devem fornecer alguns resultados úteis.
Gyan aka Gary Buyn

Respostas:

2

Neste vídeo, mostro como fazê-lo com código ... a projeção / exibição é isométrica, mas todos os cálculos são feitos em 3D ...

http://www.youtube.com/watch?v=axHp1f3RlHM

Quando calculo o raio do mouse, verifico a colisão em um avião ... mas o procedimento é analógico para verificar uma lista de esferas delimitadoras associadas aos seus objetos.

O algoritmo deve ser semelhante a este:

1. Calculate mouse ray
2. for every object
3.     check if ray intersecs with it
4.         if does then save the object and the distance in a sorted list by distance
5. choose from sorted list the nearest object.
Blau
fonte
Seu exemplo foi basicamente o que estou procurando, talvez não como isométrico. Como você renderiza a grade e o contorno de um cubo? Precisa de algo assim para testar onde o mouse está localizado no 3D Space.
precisa
Eu uso uma classe própria para desenhar linhas ... é fácil de implementar através desta frase GraphicsDevice.DrawUserPrimitives <VertexPositionColor> (PrimitiveType.LineList, data.Vertices, 0, data.Index / 2);
Blau
Sim, tentei algo semelhante, mas não consigo fazê-lo funcionar. Eu uso um método matemático para isso, mas meu entendimento do método não é o que eu acho que é. Minha pergunta sobre isso: gamedev.stackexchange.com/questions/31424/…
Deukalion
Aqui está o código para desenhá-lo pastebin.com/YpCKAHcF é fácil ... só que desenhar linhas verticais e horizontais
Blau
Você chama DrawLine3D, mas não há código disso. É apenas DrawUserPrimitives (LineList, Data.Vertices, 0, data.Index / 2)? data.Index / 2 é o que?
precisa
9

A primeira coisa que você precisa entender é transformar pontos 3D em coordenadas de tela 2D e vice-versa. Aqui estão algumas respostas que eu dei sobre esse assunto; Entendendo 3D para tela , 3D para 2D .

Agora, para ir da coordenada do mouse em 2D para 3D, você precisa fazer uma transformação 3D em 2D para trás. Isto é bastante simples.

Uma coordenada objeto / local é transformada no espaço da tela multiplicando-a pela matriz mundial (obtenha a coordenada mundial), depois pela matriz de visualização (faça a coordenada mundial em relação à câmera) e depois pela matriz de projeção (obtenha a coordenada da tela) .

Fazer isso de trás para frente é fácil. Inverta todas as matrizes. Em seguida, multiplique pela projeção invertida e pela matriz de vista invertida. Agora, você não terá uma coordenada 3D para o mouse. O motivo é que estava faltando uma dimensão de dados. Então, o que você tem é um raio. Mas isso é bom. O raio terá uma coordenada de definição e será apontado na mesma direção que a câmera olha para o vetor. Você também pode perceber que não precisa inverter a matriz mundial de nenhum objeto. Isso é apenas porque você realmente não quer o raio no objeto / espaço local, não faz sentido, você provavelmente também tem seus volumes de colisão definidos no espaço do mundo.

Agora tudo o que você precisa fazer é o teste de interseção círculo / caixa / raio poli para descobrir se o raio se cruza com um objeto.

Encontre todos os objetos que estão cruzados e mantenha uma lista. Em seguida, calcule a distância de cada objeto da câmera. O mais próximo da câmera é aquele que o usuário deseja escolher.

Aqui está o código que você precisaria fazer isso no XNA. Essa solução também deve funcionar para outros tipos de projetos (desde que você a converta primeiro). Se você está renderizando apenas sprites 2D, provavelmente não está usando uma matriz de projeção, então entregue a função Matrix.Identity.

using System;
using Microsoft.Xna.Framework;

namespace FluxPrototype
{
    /// <summary>
    /// Contains functions useful in working with XNA Vectors.
    /// </summary>
    static class VectorHelper
    {
        /// <summary>
        /// Converts a Vector2 position into a 3D ray.
        /// </summary>
        /// <param name="Position">The 2D position.</param>
        /// <param name="View">The view matrix for the camera.</param>
        /// <param name="Projection">The projection matrix for the camera.</param>
        /// <param name="Point">The returned point defining part of the 3D ray.</param>
        /// <param name="Direction">The direction of the 3D ray.</param>
        public static void Unproject(Vector2 Position, Matrix View, Matrix Projection, out Vector3 Point, out Vector3 Direction)
        {
            if (Position == null)
                Position = Vector2.Zero;

            // Create two 3D points from the position. The first one we will return.
            Point = new Vector3(Position, 0);
            Vector3 Point2 = new Vector3(Position, 1);

            // Transform the points.
            Matrix InvertedProjection = Matrix.Invert(Projection);
            Point = Vector3.Transform(Point, InvertedProjection);
            Point2 = Vector3.Transform(Point2, InvertedProjection);

            Matrix InvertedView = Matrix.Invert(View);
            Point = Vector3.Transform(Point, InvertedView);
            Point2 = Vector3.Transform(Point2, InvertedView);

            // Use the difference between the points to define the ray direction.
            // This will only be the camera direction if the view frustum is orthographic.
            Direction = (Point2 - Point);
            Direction.Normalize();
        }
    }
}
OriginalDaemon
fonte
Está bem. Eu vou dar uma olhada. Gostaria de explicar como inverter matrizes? Não tenho ideia de como.
Deukalion
11
Matriz InvertedView = Matrix.Invert (Visualizar);
OriginalDaemon
Este método funciona ao colocar itens no Editor? Como isso é mais "importante" do que selecioná-los. Digamos que, se eu quiser adicionar um cubo à tela, clique onde quiser - esse método também é compatível?
precisa
Isso é o mais longe que eu vim e não tenho certeza se está correto: Vector2 screenPosition = new Vector2 (PointToClient (MousePosition) .X, PointToClient (MousePosition) .Y); Matriz m = posição da tela * matrix.Invert (Camera.MatrixWorld) * Matrix.Invert (Camera.MatrixView) * Matrix.Invert (Camera.MatrixWorld);
precisa
... e eu crio minhas matrizes (na classe Camera) dessa maneira: matrix_world = Matrix.CreateRotationZ (MathHelper.ToRadians (world_rotation.Z)) * Matrix.CreateRotationY (MathHelper.ToRadians (world_rotation.Y)) * Matrix.CreateRotationX (MathHelper.ToRadians (world_rotation.X)) * Matrix.CreateTranslation (local_do_mundo) * Matrix.CreateScale (world_zoom); matrix_view = Matrix.CreateLookAt (view_position, view_lookat, Vector3.Up); matrix_projection = Matrix.CreatePerspectiveFieldOfView (projection_fieldofview, projection_aspectratio, projection_clipstart, projection_clipend);
precisa
2

Normalmente, criarei um volume delimitador do mouse. A origem é onde eu cliquei. Em seguida, conclua um teste de interseção fazendo um loop em todos os objetos. Em seguida, classifique a lista que você voltar com base nos critérios desejados e selecione o elemento 0.

Vaughan Hilts
fonte
Entendi. Mas como faço com o valor Z? Ou profundidade, porque se eu apenas clicar na tela, recebo as coordenadas X, Y e que mais tarde podem corresponder a um ponto no mundo 3D em que clico, mas o valor da profundidade não recebo. Quão "perto" da câmera é ou a que distância?
precisa
1

Uma ideia que considerei, mas ainda não tentei:

Desenhe uma imagem fora da tela igual à imagem real, mas sem texturas etc. Simplesmente desenhe seus objetos, cada objeto em uma "cor" diferente, onde a "cor" é realmente apenas um número de objeto. Para descobrir o que há sob o mouse, basta olhar para a cor do pixel na imagem alternativa.

É claro que isso está desperdiçando muito trabalho, mas é a GPU e não a CPU, e com certeza resolverá as coisas exatamente pelas mesmas regras.

Loren Pechtel
fonte