Comment limiter le mouvement click'n'drag à une zone?

11

Je m'excuse pour le titre quelque peu générique. Je ne sais vraiment pas comment accomplir ce que j'essaie de faire, ce qui rend encore plus difficile la recherche d'une solution possible.

J'essaie d'implémenter une sorte de marqueur de chemin (peut-être qu'il y a un nom le plus approprié, mais c'est le meilleur que je puisse trouver).

Devant le joueur, il y aura un marqueur de chemin, qui déterminera comment le joueur se déplacera une fois qu'il aura terminé de planifier son tour. Le joueur peut cliquer et faire glisser le marqueur à la position de son choix, mais le marqueur ne peut être déplacé que dans une zone de travail définie (le bit gris).

Diagramme de marqueur de chemin

Je suis donc maintenant coincé avec deux problèmes:

Tout d'abord, comment définir exactement cette zone de travail? Je peux imaginer peut-être deux vecteurs qui ont le joueur comme point de départ pour former l'angle réalisable, et peut-être que ces deux arcs pourraient provenir de cercles qui ont leur centre où le joueur est, mais je ne sais vraiment pas comment mettre tout cela ensemble.

Et deuxièmement, après avoir défini la zone où le marqueur peut être placé, comment puis-je faire en sorte que le marqueur ne reste que dans cette zone? Par exemple, si le joueur clique et fait glisser le marqueur, il peut se déplacer librement dans la zone de travail, mais ne doit pas quitter les limites de la zone. Ainsi, par exemple, si le joueur commence à faire glisser le marqueur vers le haut, il se déplacera vers le haut jusqu'à ce qu'il touche l'extrémité de la zone de travail (premier diagramme ci-dessous), mais si après cela, le joueur commence à faire glisser latéralement, le marqueur doit suivre la traînée tout en restant dans la zone (deuxième diagramme ci-dessous).

Premier diagramme: monter Deuxième diagramme: suivre la traînée

J'espère que ce n'était pas trop déroutant. Merci les gars.

Edit: au cas où cela ferait une différence, j'utilise C ++ avec Marmalade SDK.

Vexille
la source
Je pense que le terme que vous recherchez est "waypoint", et pouvez-vous définir mathématiquement la zone grise? La solution à votre problème sera probablement beaucoup plus facile si elle l'est.
John McDonald
Les waypoints ne sont-ils pas liés à la recherche de chemins et autres? De plus, définir mathématiquement la zone grise est l'un de mes problèmes: PI pense que je pourrais comprendre quelque chose comme un rectangle ou même un cercle, mais ce genre de forme, avec deux arcs pour les côtés et deux autres côtés sur un angle ... C'est un peu au-dessus de ma tête.
Vexille
Vous devrez également spécifier la langue et / ou la boîte à outils que vous utilisez, car les solutions dépendent principalement de la plate-forme.
Raceimaztion
Vraiment? J'ai pensé qu'une solution algorhithmique ou même un pseudocode aiderait. Je vais quand même éditer la question, juste au cas où.
Vexille
Définissez la forme en coordonnées polaires (angle max de l'angle des caractères et distance min / max du caractère). Ensuite, ne mettez à jour le waypoint à l'endroit où se trouve la souris que si la souris se trouve dans ces limites. S'il dépasse l'un de ces éléments, accrochez-le à la valeur la plus élevée possible. Commencez à mettre à jour lorsque vous cliquez sur l'intérieur de la zone et arrêtez lorsque la souris est relâchée.
ClassicThunder

Réponses:

8

Vous pouvez définir une zone réalisable comme celle de votre question avec trois valeurs:

float innerRadius;
float outerRadius;
float maxAngle;

Ces valeurs seront basées sur un point central qui peut ou non être la position du joueur. La forme de la zone de travail dépend de l'endroit où vous placez ce point.

entrez la description de l'image ici

Dans l'exemple ci-dessus, la position centrale se trouve à une certaine distance (disons 50 unités) derrière le joueur. Cela pourrait être facilement calculé comme suit:

float offset = -50;
Vector2 centerPosition = playerPosition + offset * playerForward;

Pour limiter la position du marqueur à cette zone réalisable, déplacez d' abord le marqueur comme vous le feriez normalement. Validez ensuite la distance entre le point central et le marqueur:

Vector2 direction = markerPosition - centerPosition;
float distance = direction.Length();
direction.Normalize();
markerPosition = centerPosition + direction * clamp(distance, innerRadius, outerRadius);

Enfin, validez l'angle du marqueur par rapport à la plage spécifiée. Je vais utiliser un pseudocode pour celui-ci:

- Find angle between vector C->M and vector playerForward
- If abs(angle) <= maxAngle Then do nothing
- Else If angle > 0 Then rotate M around C by maxAngle-angle
- Else If angle < 0 Then rotate M around C by -maxAngle-angle

Regardez comment faire pivoter un point autour d'un autre. Cela peut être fait soit par trigonométrie, soit par une matrice de transformation.

Vous pouvez également prendre en compte la taille du marqueur et réduire légèrement le rayon et l'angle pour compenser.

Edit: Après réflexion, il pourrait sembler plus naturel si vous validez d'abord l'angle, puis la distance, alors essayez les deux alternatives!

David Gouveia
la source
Excellente solution! J'ai rencontré quelque chose qui impliquait deux cercles et un triangle, mais votre solutiong simplifie élégamment cela. À propos de la validation de l'angle, j'avais pensé à quelque chose dans le sens d'avoir un vecteur normalisé qui se trouvait à maxAngle (et un autre à -maxAngle) de playerForward, qui pourrait être multiplié par la longueur de C-> M au cas où ce serait des limites , en angle. Je suppose que votre solution de rotation de M autour de C serait moins coûteuse, n'est-ce pas?
Vexille
@Vexille Eh bien, la rotation implique une coset une sinopération, donc je ne suis pas sûr. Mais pour calculer ces deux vecteurs, vous devez également les faire pivoter, bien que vous ne deviez le faire que lorsque le vecteur avant change. Si cela n'a pas beaucoup d'importance de toute façon, choisissez celui que vous préférez mettre en œuvre.
David Gouveia
10

Je pensais comment le problème pourrait être résolu si la forme était irrégulière, et on ne pouvait pas le définir mathématiquement. Attention: c'est une solution sale, pas pour les faibles de cœur.

1. Prenez votre région:

entrez la description de l'image ici

2. Et convertissez-le en un bitmap monochromatique:

entrez la description de l'image ici et nommez-le scale_0

3. Clonez le bitmap et réduisez-le à 50%:

entrez la description de l'image ici et nommez-le scale_1

4. Et ainsi de suite, jusqu'à ce qu'il y ait un bitmap inférieur à 4 pixels de large / haut:

entrez la description de l'image ici entrez la description de l'image ici entrez la description de l'image ici entrez la description de l'image ici entrez la description de l'image ici échelle: 2, 3, 4, 5, 6

5. Nous avons maintenant notre zone sous forme d'images bitmap monochromes de différentes résolutions: entrez la description de l'image ici

6. Prenez la dernière image (ici "scale_6") et parcourez tous ses pixels.

  • traduire les coordonnées de chaque pixel en coordonnées d'écran: x = Math.pow ( 2, scale_level );où scale_level est le nombre que nous avons ajouté après "scale_". Nous pourrions également l'appeler un niveau d'arbre quadruple, bien que nous ne travaillions pas vraiment avec un arbre quadruple. Faites de même avec y.
  • vérifiez si le pixel des x et y traduits est noir. Sinon, cela ne fait pas partie de la forme, et vous devriez simplement continuepasser à l'étape suivante de la boucle
  • vérifiez si le pixel est plus proche du curseur de la souris que le pixel précédemment vérifié - si oui, enregistrez les coordonnées du pixel - utilisez les coordonnées avant la traduction, c'est-à-dire les coordonnées à l'intérieur du bitmap.
  • à la fin de la boucle, multipliez ces coordonnées par 2: x *= 2; y*=2;pour les traduire en coordonnées dans l'image suivante (échelle précédente)

7. Prenez l'image précédente (ici "scale_5"), mais ne parcourez pas tous les pixels; commencez par x = sauvé_x et terminez par x = sauvé_x + 2, de même avec y. C'est-à-dire que maintenant vous ne bouclerez que 4 pixels pour chaque niveau! Le reste est comme à la p. 6.

8. Prenez la première image (la plus grande = celle avec la plus grande résolution), bouclez à nouveau sur 4 pixels, et vous avez enfin le pixel le plus proche du curseur de la souris:

entrez la description de l'image ici

entrez la description de l'image ici

entrez la description de l'image ici

9. Cependant, je traite "M" comme un point ici. Si vous voulez que ce soit un cercle qui s'adapte complètement, vous devez d'abord contracter (rétrécir) la forme en circle.radiuspixels.

Je pensais ajouter que cet algorithme ne fonctionnera que si vous utiliserez des images non monochromatiques mais en niveaux de gris et traiterez un pixel comme "plein" s'il n'est pas blanc et comme "vide" s'il est exactement blanc ... OU si vous redimensionnez l'algorithme change chaque groupe de 4 pixels en 1 pixel noir à chaque fois quand au moins un de ces 4 pixels n'était pas blanc.

Markus von Broady
la source
2
+1 pour une réponse pour les formes difficiles (voire impossibles) à exprimer mathématiquement.
Cypher
Wow, très intéressant. +1 aussi: D
Vexille
Je l'ai implémenté en vrai projet, et je dois dire que certains problèmes sont apparus. Fondamentalement, vous devez faire une liste des cellules de la grille, où vous prenez la cellule de la grille la plus proche nommée closest, et vérifiez la distance jusqu'au point le plus éloigné de closest- nommons la distance furthest_dist. Vous devez maintenant supprimer de la liste toutes les cellules qui ont leur point le plus proche plus loin que le furthest_distniveau et aller plus loin. Donc, au lieu de quelque chose comme ça: i.imgur.com/4UuFo.png C'est quelque chose comme ça: i.imgur.com/dyTT3.png
Markus von Broady