Espace écran vers l'espace monde

10

J'écris un jeu 2D où mon monde de jeu a un axe x de gauche à droite, un axe y de haut en bas et un axe z hors de l'écran:

entrez la description de l'image ici

Alors que mon univers de jeu est descendant, le jeu est rendu sur une légère inclinaison:

entrez la description de l'image ici

Je travaille sur la projection de l'espace mondial vers l'espace écran, et vice-versa. J'ai l'ancien travaillant comme suit:

var viewport = new Viewport(0, 0, this.ScreenWidth, this.ScreenHeight);
var screenPoint = viewport.Project(worldPoint.NegateY(), this.ProjectionMatrix, this.ViewMatrix, this.WorldMatrix);

La NegateY()méthode d'extension fait exactement ce que cela ressemble, puisque l' axe y de XNA s'étend de bas en haut au lieu de haut en bas. La capture d'écran ci-dessus montre que tout cela fonctionne. Fondamentalement, j'ai un tas de points dans l'espace 3D que je rend ensuite dans l'espace d'écran. Je peux modifier les propriétés de la caméra en temps réel et la voir s'animer à la nouvelle position. Évidemment, mon jeu réel utilisera des sprites plutôt que des points et la position de la caméra sera fixe, mais j'essaie simplement de mettre tous les calculs en place avant d'en arriver là.

Maintenant, j'essaye de me reconvertir dans l'autre sens. Autrement dit, étant donné un point x et y dans l'espace d'écran ci-dessus, déterminez le point correspondant dans l'espace mondial. Donc, si je pointe le curseur vers, disons, en bas à gauche du trapèze vert, je veux obtenir une lecture de l'espace mondial de (0, 480). La coordonnée z n'est pas pertinente. Ou plutôt, la coordonnée z sera toujours nulle lors du mappage vers l'espace mondial. Essentiellement, je veux implémenter cette signature de méthode:

public Vector2 ScreenPointToWorld(Vector2 point)

J'ai essayé plusieurs choses pour que cela fonctionne, mais je n'ai tout simplement pas de chance. Ma dernière pensée est que je dois appeler Viewport.Unprojectdeux fois avec des valeurs z proches / lointaines différentes , calculer la résultante Ray, la normaliser, puis calculer l'intersection de la Rayavec une Planequi représente essentiellement le niveau du sol de mon monde. Cependant, je suis resté coincé dans la dernière étape et je ne savais pas si je compliquais trop les choses.

Quelqu'un peut-il m'indiquer dans la bonne direction comment y parvenir?

moi--
la source

Réponses:

4

Je pense que votre idée est à peu près exacte! Calculez d'abord un rayon pour votre curseur en utilisant à la fois le plan proche et le plan lointain comme valeurs Z pour vos coordonnées 2D (c'est-à-dire utilisez 0 et 1 pour vos coordonnées Z). Voici une méthode d'aide pour gérer cela:

public Ray GetScreenRay(Vector2 screenPosition, Viewport viewport, Matrix projectionMatrix, Matrix viewMatrix, Matrix worldMatrix)
{
    Vector3 nearPoint = viewport.Unproject(new Vector3(screenPosition, 0f), projectionMatrix, viewMatrix, worldMatrix);
    Vector3 farPoint = viewport.Unproject(new Vector3(screenPosition, 1f), projectionMatrix, viewMatrix, worldMatrix);
    return new Ray(nearPoint, Vector3.Normalize(farPoint - nearPoint));
}

Ensuite, vous devrez avoir un Plane instance qui correspond à votre terrain. Le moyen le plus simple de le calculer est d'utiliser le constructeur qui prend trois points sur le plan et de lui passer trois sommets de l'objet au sol. Exemple de calcul du plan du sol:

Plane groundPlane = new Plane(ground.Vertices[0], ground.Vertices[1], ground.Vertices[2]);

Et enfin trouver le point d'intersection entre le rayon et le plan en utilisant cette méthode . Vous pouvez utiliser le paramètre out result afin de calculer les coordonnées d'intersection comme dans l'exemple ci-dessous:

float? result;
mouseRay.Intersects(ref groundPlane, out result);
if(result != null)
    Vector3 worldPoint = mouseRay.Position + mouseRay.Direction * result.Value;

Vous devrez peut-être aussi faire quelque chose à propos de la NegateYméthode, mais je ne sais pas où.

David Gouveia
la source
C'est super utile - merci beaucoup. C'était la dernière ligne qui me manquait. Je ne savais pas quoi faire avec le flotteur une fois que je l'avais! Je pense que je vais devoir avoir ton nom dans mes crédits de jeu :)
moi--
@ user13414 En effet, la documentation est un peu rare sur cette méthode et ne donne aucun exemple d'utilisation de la distance pour récupérer le point d'intersection.
David Gouveia