Est-ce une mauvaise idée de «cartographier» la position de la souris sur l'écran afin que la détection de collision fonctionne quelle que soit la résolution?

16

Prenons un jeu dont la résolution par défaut est 800x600. Les objets avec des masques de collision sont placés dans un monde de jeu de taille 800x600. Les masques de collision peuvent détecter quand la souris entre en collision avec eux.

Considérez maintenant que nous dimensionnons le jeu jusqu'à 1024x768 (supposons que nous redimensionnons les graphiques en rendant simplement tout sur un calque, puis en redimensionnant le calque entier à la fois). Nous avons deux options pour que les collisions avec la souris fonctionnent correctement dans cette nouvelle résolution:

A.) Faites évoluer le monde à 1024x768 et mettez à l'échelle le masque de collision de chaque objet en conséquence.

B.) "Carte" de la position de la souris sur le monde d'origine (800x600).

Par "carte", j'entends simplement mettre à l'échelle la position de la souris sur le monde original de 800x600. Ainsi, par exemple, si la position de la souris sur l' écran est (1024, 768), alors la position de la souris dans le monde est (800, 600).

Maintenant, évidemment, l'option B nécessite beaucoup moins de calculs et est probablement moins sujette aux erreurs géométriques, mais elle me semble également un peu "hackish", comme il y a des conséquences imprévues à utiliser cette méthode qui sera un enfer à corriger plus tard.

Avec quelle méthode dois-je choisir: A, B ou autre chose?

user3002473
la source
2
Comme l'a souligné Classic Thunder, les coordonnées d'affichage ne doivent pas être les mêmes que les coordonnées du monde ou les coordonnées d'objet. En règle générale, vous voulez avoir des transformations (essentiellement, comme vous le dites, les mappages , mais généralement avec des mathématiques matricielles).
Dan

Réponses:

38

En règle générale (même pour les jeux 2D), il existe un système de coordonnées distinct pour le jeu qui est indépendant de la résolution, ce qui est généralement appelé l'espace mondial. Cela vous permet de vous adapter à une résolution arbitraire.

La façon la plus simple d'y parvenir est d'utiliser une caméra 2D, qui est essentiellement une matrice qui définit la transition de l'espace mondial (vos unités arbitraires) à l'espace d'écran (les pixels à l'écran). Cela rend le traitement des mathématiques trivial.

Jetez un œil à la section ci-dessous dans XNA 2d Camera Scrolling - pourquoi utiliser la transformation matricielle?

Il facilite la conversion entre les définitions de système de coordonnées

Pour passer de l'écran à l'espace mondial, utilisez simplement Vector2.Transform. Ceci est couramment utilisé pour obtenir l'emplacement de la souris dans le monde pour la cueillette d'objets.

Vector2.Transform(mouseLocation, Matrix.Invert(Camera.TransformMatrix));

Pour passer du monde à l'espace écran, faites simplement le contraire.

Vector2.Transform(mouseLocation, Camera.TransformMatrix);

Il n'y a aucun inconvénient à utiliser une matrice autre que cela demande un peu d'apprentissage.

ClassicThunder
la source
... et en effet, de manière générale, le matériel moderne est conçu pour les opérations matricielles via des architectures de vectorisation telles que SSE, NEON, etc.
Ingénieur
1
Clause de non-responsabilité: XNA a été abandonné par Microsoft, mais Monogame utilise la même API.
Pharap
1
Bien sûr, il est judicieux d'utiliser des matrices pour traduire entre les deux systèmes de coordonnées. Mais comment cela répond-il à la question? Vous devez toujours décider, si vous voulez mapper du système A au système B ou de B à A et quelles sont les conséquences, le cas échéant.
mastov
"Est-ce une mauvaise idée de" cartographier "la position de la souris sur l'écran afin que la détection de collision fonctionne quelle que soit la résolution?" est répondu par "il est intelligent d'utiliser des matrices pour traduire entre les deux systèmes de coordonnées" ...
ClassicThunder
Je réponds également "Vous devez toujours décider, si vous voulez mapper du système A au système B ou de B à A et quelles sont les conséquences, s'il y en a." en disant que vous devez utiliser un arbitraire non lié à la résolution et à l'échelle de celui-ci. Donc C -> A ou C -> B selon que A ou B est la résolution. Bien sûr, vous pouvez avoir C égal à une résolution de base que vous concevez et mettez à l'échelle à partir de là. Le point est que toutes les mathématiques se produisent dans le même système de coordonnées et vous ne mettez à l'échelle que le rendu.
ClassicThunder
2

Une autre option serait la suivante: à chaque événement de mouvement de la souris en entrée, déplacez le curseur de la souris dans le jeu du nombre de pixels du jeu correspondant au nombre de pixels de l'événement de la souris. Cela vient naturellement pour les jeux 3D qui verrouillent le véritable pointeur de la souris au centre et font pivoter la direction de visée d'une quantité correspondant au mouvement de la souris en entrée, mais vous pouvez le faire de la même manière en déplaçant un sprite représentant le curseur de la souris dans le jeu.

Vous devez évidemment ignorer tous les événements de mouvement d'entrée de la souris provoqués par la déformation au centre, et si votre jeu a un retard d'entrée, la non-réponse du curseur sera l'aspect le plus discordant et le plus visible.

En partie, la solution que vous utilisez dépend de l'importance de la position de la souris pour le gameplay. S'il s'agit d'un RTS et que le joueur clique simplement pour sélectionner des unités, vous pouvez probablement simplement choisir celui de vos A ou B qui est le plus facile. Si c'est un jeu de tir de haut en bas et que la souris contrôle directement les mouvements du personnage, alors vous voudrez probablement une solution plus approfondie qui limite la quantité de variabilité dans la façon dont le personnage peut se déplacer en fonction non seulement de la résolution mais aussi de choses comme le mouvement de la souris la vitesse. Si la souris contrôle la direction de ciblage à la place, alors vous voudrez une solution différente, etc.

Aléatoire832
la source
0

La réponse de ClassicThunder est correcte, mais je voudrais fournir un exemple d'un moyen alternatif / plus simple d'obtenir l'effet souhaité. Il s'agit d'une solution plus simple pour le prototypage rapide, les cas où vous n'avez pas accès à une bibliothèque complète, ou pour les cas où vous n'avez pas accès à un GPU (par exemple dans les systèmes embarqués).

Pour effectuer ce mappage, vous pouvez utiliser la fonction suivante (supposez qu'elle soit définie dans la classe statique Helper):

static float Map(float value, float fromLow, float fromHigh, float toLow, float toHigh)
{
    return ((value - fromLow) / (fromHigh - fromLow) * (toHigh - toLow)) + toLow;
}

(Vous n'avez pas spécifié de langue, mais je vois que vous avez une certaine connaissance de C #, donc mon exemple est en C #.)

Vous pouvez ensuite utiliser cette fonction comme ceci:

float mouseXInWorld = Helper.Map(Mouse.X, 0, Screen.Width - 1, camera.Bounds.X, camera.Bounds.X + camera.Bounds.Width - 1);
float mouseYInWorld = Helper.Map(Mouse.Y, 0, Screen.Height - 1, camera.Bounds.Y, camera.Bounds.Y + camera.Bounds.Height - 1);

Où se camera.Boundstrouve un rectangle représentant la zone du monde que la caméra peut voir (c'est-à-dire la zone projetée sur l'écran).

Si vous avez une classe Vectorou Point, vous pouvez simplifier davantage ce processus en créant un équivalent 2D de la fonction de carte, comme ceci:

static Vector Map(Vector value, Rectangle fromArea, Rectangle toArea)
{
    Vector result = new Vector();
    result.X = Map(value.X, fromArea.X, fromArea.X + fromArea.Width - 1, toArea.X, toArea.X + toArea.Width - 1);
    result.Y = Map(value.Y, fromArea.Y, fromArea.Y + fromArea.Height - 1, toArea.Y, toArea.Y + toArea.Height - 1);
    return result;
}

Ce qui ferait de votre code de cartographie une simple ligne:

Vector mousePosInWorld = Map(Mouse.Pos, Screen.Bounds, camera.Bounds);
Pharap
la source
-1

Option (C): redéfinissez la résolution de l'écran sur 800x600.

Même si vous ne le faites pas, considérez-le comme une expérience de pensée. Dans ce cas, il est de la responsabilité de l'écran de redimensionner les graphiques pour qu'ils correspondent à sa taille physique, puis du système d'exploitation de vous donner des événements de pointeur à une résolution de 800x600.

Je pense que tout dépend si vos graphiques sont bitmap ou vectoriels. S'ils sont bitmap et que vous effectuez un rendu dans un tampon 800x600, alors oui, il est beaucoup plus facile de remapper la souris dans l'espace d'écran et d'ignorer la résolution d'écran réelle. Cependant, le gros inconvénient est que la mise à l'échelle peut sembler moche, surtout si vous faites des graphiques de style "8 bits" en bloc.

pjc50
la source