Résolveurs — MRTK3

Résolveur principal

Les résolveurs sont des composants qui facilitent le calcul de la position et de l’orientation d’un objet en fonction d’un algorithme prédéfini. Exemple : placer un objet sur la surface actuellement visée par le raycast du pointage de regard de l’utilisateur.

En outre, le système de résolveur définit de manière déterministe un ordre des opérations pour ces calculs de transformation, car il n’existe pas de méthode fiable pour indiquer à Unity l’ordre de mise à jour des composants.

Les résolveurs offrent un éventail de comportements pour attacher des objets à d’autres objets ou systèmes. Un autre exemple est un objet de balise qui pointe vers l’avant de l’utilisateur (en fonction de l’appareil photo). Un résolveur peut également être attaché à un contrôleur et à un objet pour faire de l’objet suive le mouvement du contrôleur. Tous les résolveurs peuvent être empilés en toute sécurité, par exemple un comportement avec objet tag-along + aimantation de surface + dynamisme.

Procédure d'utilisation

Le système du résolveur se compose de trois catégories de scripts :

  • Solver : classe abstraite de base dont dérivent tous les résolveurs. Elle fournit le suivi d’état, les paramètres de lissage et l’implémentation, l’intégration du système de résolveur automatique et l’ordre des mises à jour.
  • SolverHandler : définit l’objet de référence suivi (par exemple, la transformation de la caméra principale, le rayon émanant de la main, etc.), gère la collecte des composants du résolveur, et exécute leur mise à jour dans l’ordre approprié.

La troisième catégorie est le résolveur lui-même. Les résolveurs suivants fournissent les blocs de construction pour le comportement de base :

  • Orbital : verrouille sur une position et un offset spécifiés à partir de l’objet référencé.
  • ConstantViewSize : met à l’échelle pour conserver une taille constante relative à la vue de l’objet référencé.
  • RadialView : conserve l’objet dans une vue de type cône converti par l’objet référencé.
  • Follow : conserve l’objet dans un ensemble de limites définies par l’utilisateur de l’objet référencé.
  • InBetween : conserve un objet entre deux objets suivis.
  • SurfaceMagnetism : convertit les rayons en surface réelle et aligne l’objet sur cette surface.
  • DirectionalIndicator : détermine la position et l’orientation d’un objet sous la forme d’un indicateur directionnel. À partir du point de référence de la cible suivie SolverHandler, cet indicateur va orienter vers la DirectionalTarget fournie.
  • Momentum : applique l’accélération/la vélocité/la friction pour simuler le dynamisme et les souplesse d’un objet déplacé par d’autres résolveurs/composants.
  • HandConstraint : contraint l’objet à suivre les mains dans une région qui n’entrecroise pas le GameObject avec les mains. Utile pour du contenu interactif contraint à la main, comme des menus. Ce résolveur est conçu pour fonctionner avec XRNode.
  • HandConstraintPalmUp : dérive de HandConstraint mais comprend une logique permettant de tester si la paume fait face à l’utilisateur avant l’activation. Ce résolveur fonctionne uniquement avec les contrôleurs XRNode. Avec d’autres types de contrôleur, ce résolveur se comporte comme sa classe de base.
  • Overlap: chevauche l’objet suivi.

Pour utiliser le système du résolveur, ajoutez l’un des composants répertoriés ci-dessus à un GameObject. Étant donné que tous les résolveurs requièrent un(e) SolverHandler, il y en a un(e) qui est créé(e) automatiquement par Unity.

Remarque

Vous trouverez des exemples d’utilisation du système de résolveurs dans le fichier SolverExamples.scene.

Comment modifier la référence de suivi

La propriété Tracked Target Type du composant SolverHandler définit le point de référence que tous les résolveurs utiliseront pour calculer leurs algorithmes. Par exemple, un type de valeur Head avec un composant SurfaceMagnetism simple se traduira par une projection de rayon à partir de la tête et dans la direction du pointage du regard de l’utilisateur pour déterminer la surface atteinte. Les valeurs potentielles pour la propriété TrackedTargetType sont :

  • *Head : le point de référence est la transformation de la caméra principale
  • ControllerRay : le point de référence est la transformation LinePointer sur un contrôleur (c’est-à-dire l’origine du pointeur sur un contrôleur de mouvement ou un contrôleur de main) qui pointe dans la direction du rayon rectiligne.
    • Utilisez la propriété TrackedHandedness pour sélectionner la préférence de main (c’est-à-dire Gauche, Droite, Les deux)
  • HandJoint : le point de référence est la transformation d’une articulation de la main spécifique
    • Utilisez la propriété TrackedHandedness pour sélectionner la préférence de main (c’est-à-dire Gauche, Droite, Les deux)
    • Utilisez la propriété TrackedHandJoint pour déterminer la transformation d’articulation à utiliser
  • CustomOverride : point de référence depuis le/la TransformOverride affecté(e)

Notes

Pour les types ControllerRay et HandJoint, le gestionnaire de résolveur tentera de fournir d’abord la transformation du contrôleur/main gauche, puis du droit si le premier est indisponible, ou à moins que la propriété TrackedHandedness spécifie autre chose.

Important

La plupart des résolveurs utilisent le vecteur vers l’avant de la cible de transformation suivie fournie par SolverHandler. Lors de l’utilisation d’un type de cible Articulation de la main suivi, le vecteur avant de l’articulation de la paume peut pointer avec les doigts et non avec la paume. Cela dépend de la plateforme fournissant les données jointes à la main. Pour la simulation d’entrée et Windows Mixed Reality, le vecteur vers le haut pointe à travers la paume (vecteur vert vers le haut, vecteur bleu vers l’avant)

Pour remédier à cela, mettez à jour la propriété Additional Rotation sur le SolverHandler avec les valeurs <90, 0, 0>. Cela garantit que le vecteur vers l’avant fourni aux résolveurs pointe via la paume et vers l’extérieur de la main.

Vous pouvez également utiliser le type de cible Ray Controller suivi pour obtenir un comportement similaire pour le pointage avec les mains.

Comment joindre des résolveurs

Il est possible d’ajouter plusieurs composants Solver au même GameObject, et ainsi de joindre leurs algorithmes. Les composants SolverHandler gèrent la mise à jour de tous les résolveurs sur le même GameObject. Par défaut, les SolverHandler appelle GetComponents<Solver>() au démarrage, se qui a pour effet de retourner les résolveurs dans l’ordre dans lequel ils apparaissent dans l’inspecteur.

En outre, l’affectation de la valeur true à la propriété Updated Linked Transform indique au Solver d’enregistrer sa position calculée, son orientation et son échelle dans une variable intermédiaire accessible par tous les résolveurs (c.-à-d. GoalPosition). Quand la valeur est false, le/la Solver met directement à jour la transformation de GameObject. En enregistrant les propriétés de transformation dans un emplacement intermédiaire, les autres résolveurs peuvent effectuer leurs calculs à partir de la variable intermédiaire. En effet, Unity ne permet pas aux mises à jour de gameObject.transform de s’empiler dans la même trame.

Remarque

Les développeurs peuvent modifier l’ordre d’exécution des résolveurs en définissant la propriété SolverHandler.Solvers directement.

Création d'un résolveur

Tous les résolveurs doivent hériter de la classe de base abstraite, Solver . Les exigences principales d’une extension du résolveur impliquent le remplacement de la méthode SolverUpdate. Dans cette méthode, les développeurs doivent mettre à jour les propriétés héritées GoalPosition, GoalRotation et GoalScale avec les valeurs souhaitées. En outre, il est utile de tirer parti de SolverHandler.TransformTarget comme cadre de référence souhaité par le consommateur.

Le code fourni ci-dessous donne un exemple d’un nouveau composant de résolveur appelé InFront qui place l’objet attaché à deux mètres devant la SolverHandler.TransformTarget. Le consommateur définit SolverHandler.TrackedTargetType en tant que Head, puis SolverHandler.TransformTarget sera la transformation de la caméra et, par conséquent, ce résolveur placera le GameObject attaché à deux mètres devant chaque trame de pointage du regard de l’utilisateur.

/// <summary>
/// InFront solver positions an object 2m in front of the tracked transform target
/// </summary>
public class InFront : Solver
{
    ...

    public override void SolverUpdate()
    {
        if (SolverHandler != null && SolverHandler.TransformTarget != null)
        {
            var target = SolverHandler.TransformTarget;
            GoalPosition = target.position + target.forward * 2.0f;
        }
    }
}

Guides d’implémentation du résolveur

Propriétés courantes du résolveur

Chaque composant du résolveur possède un ensemble de propriétés identiques qui contrôlent le comportement du résolveur principal.

Si le lissage est activé, le résolveur met progressivement à jour la transformation du GameObject au fil du temps sur les valeurs calculées. La propriété LerpTime de chaque composant de transformation détermine la vitesse de sa modification. Par exemple, une valeur MoveLerpTime supérieure entraîne des incréments plus lents entre les cadres.

Si MaintainScale est activé, le résolveur utilisera l’échelle locale par défaut de GameObject.

Orbital

La classe Orbital est un composant tag-along qui se comporte comme des planètes dans un système solaire. Ce solveur garantit que le GameObject joint orbite autour de la transformation suivie. Ainsi, si le Tracked Target Type du/de la SolverHandler est défini sur Head, le GameObject orbite autour de la tête de l’utilisateur avec un décalage fixe appliqué.

Les développeurs peuvent modifier ce décalage fixe pour conserver des menus ou d’autres composants de scène au niveau de l’œil ou au niveau de la taille, etc. autour d’un utilisateur. Cela est effectué par la modification des propriétés Local Offset et World Offset . La propriété Orientation Type détermine la rotation appliquée à l’objet s’il doit conserver sa rotation d’origine, ou s’il doit toujours faire face à la caméra ou à la transformation qui dirige sa position.

RadialView

Le/la RadialView est un autre composant tag-along qui conserve une portion spécifique d’un GameObject dans le tronc de cône de la vue de l’utilisateur.

Les propriétés Degrés d’affichage maximum min & déterminent la quantité d’une partie du GameObject qui doit toujours être visible.

Les propriétés Min & Max Distance déterminent la distance à laquelle l’objet GameObject doit être conservé de l’utilisateur. Par exemple, si vous parcourez le GameObject avec une Distance minimaled’un mètre, vous éloignez le GameObject pour vous assurer qu’il n’est jamais plus proche qu’un mètre par rapport à l’utilisateur.

En règle générale, la RadialView est utilisée conjointement avec la propriété Tracked Target Type définie sur Head afin que le composant suive le pointage du regard de l’utilisateur. Toutefois, ce composant peut fonctionner pour être conservé dans la « vue » de n’importe quel Tracked Target Type.

Suivi

La classe Follow positionne un élément devant la cible suivie par rapport à son axe de transfert local. L’élément peut être faiblement contraint (également appelé « tag-along ») afin qu’il ne suive pas jusqu’à ce que la cible suivie se déplace au-delà des limites définies par l’utilisateur.

Il fonctionne de la même façon que le solveur RadialView, avec des contrôles supplémentaires pour gérer max horizontal & degrés d’affichage vertical et les mécanismes pour modifier l’orientation de l’objet.

InBetween

La classe InBetween conserve le gameobject joint entre deux transformations. Ces deux points de terminaison de transformation sont définis par le SolverHandler Tracked Target Type du GameObject et la propriété Second Tracked Target Type du composant InBetween. En règle générale, les deux types sont définis sur CustomOverride, et les valeurs résultantes SolverHandler.TransformOverride et InBetween.SecondTransformOverride sur les deux points de terminaison suivis.

Le composant InBetween créera un autre composant SolverHandler lors de l’exécution en fonction des propriétés Second Tracked Target Type et Second Transform Override.

Le long de la ligne entre deux transformations, le PartwayOffset définit l’emplacement où l’objet sera être placé avec 0,5 à mi-chemin, 1,0 à la première transformation et 0,0 à la deuxième transformation.

SurfaceMagnetism

La SurfaceMagnetism fonctionne en effectuant une projection de rayon sur un LayerMask défini de surfaces, et en plaçant le GameObject à ce point de contact.

La propriété Surface Normal Offset place le GameObject à une distance définie en mètres à l’extérieur de la surface dans la direction normale par rapport au point d’accès de la surface.

À l’inverse, la propriété Surface Ray Offset place le GameObject à une distance définie en mètres par rapport à la surface, mais dans la direction opposée du raycast effectué. Par conséquent, si le raycast est le pointage du regard de l’utilisateur, le GameObject se rapprochera le long de la ligne du point d’accès de la surface vers la caméra.

La propriété Orientation Mode détermine le type de rotation à appliquer par rapport à la normale sur la surface.

  • None - aucune rotation appliquée
  • TrackedTarget - L’objet va faire face à la transformation suivie qui dirige le raycast
  • SurfaceNormal - L’objet est aligné normalement en fonction du point d’accès sur la surface
  • Blended - L’objet est aligné en fonction de la normale sur le point d’accès sur la surface ET en se basant sur la transformation suivie.

Pour forcer le GameObject associé à rester vertical dans n’importe quel mode autre que None, activez Keep Orientation Vertical.

Remarque

Utilisez la propriété Orientation Blend pour contrôler l’équilibre entre les facteurs de rotation lorsque le mode d’orientation a la valeur Blended. La valeur 0,0 aura une orientation entièrement pilotée par le mode TrackedTarget et la valeur 1,0 aura une orientation entièrement pilotée par SurfaceNormal.

Chevauchement

Est Overlap un solveur simple qui maintient la transformation de l’objet à la même position et à la même rotation que la cible de SolverHandler's transformation.

Détermination des surfaces qui peuvent être atteintes

Lorsque vous ajoutez un composant SurfaceMagnetism à un GameObject, il est important de tenir compte de la couche du GameObject et de ses enfants, en présence de colliders. Le composant fonctionne en exécutant plusieurs raycasts pour déterminer la surface à laquelle s’aimanter. Supposons que le résolveur GameObject ait un collider sur l’une des couches répertoriées dans la propriété MagneticSurfaces de SurfaceMagnetism. Dans ce cas, le raycast va probablement se frapper lui-même, ce qui entraîne l’attachement du GameObject à son propre point de collider. Ce comportement étrange peut être évité en définissant le GameObject principal et tous les enfants sur la couche Ignorer la projection de rayon, ou en modifiant le tableau LayerMask MagneticSurfaces de manière appropriée.

À l’inverse, un GameObject SurfaceMagnetism n’entrera pas en collision avec des surfaces sur une couche non répertoriée dans la propriété MagneticSurfaces. Il est recommandé de placer toutes les surfaces souhaitées sur une couche dédiée (à savoir Surfaces) et de définir la propriété MagneticSurfaces sur cette seule couche. L’utilisation de la default ou de everything peut entraîner des composants de l’interface utilisateur ou des curseurs contribuant au résolveur.

Enfin, les surfaces plus éloignées que le paramètre de propriété MaxRaycastDistance seront ignorées par les raycasts SurfaceMagnetism.

DirectionalIndicator

La classe DirectionalIndicator est un composant tag-along qui s’oriente dans la direction du point souhaité dans l’espace. La plus couramment utilisée lorsque la propriété Tracked Target Type du/de la SolverHandler est défini(e) sur Head. De cette manière, un composant d’expérience utilisateur avec le résolveur DirectionalIndicator indiquera à l’utilisateur de regarder le point souhaité dans l’espace. Ce point est déterminé par la propriété Directional Target .

Si la cible directionnelle est affichable par l’utilisateur, ou quel que soit le cadre de référence défini dans le SolverHandler, ce résolveur désactive tous les composants Renderer qui le précèdent. S’il n’est pas visible, tout sera activé sur l’indicateur.

La taille de l’indicateur diminuera à mesure que l’utilisateur est proche de capturer la Directional Target dans son champ de vue.

  • Min Indicator Scale - L’échelle minimale pour l’objet indicateur

  • Max Indicator Scale - L’échelle maximale pour l’objet indicateur

  • Visibility Scale Factor - Multiplicateur pour augmenter ou diminuer le champ de vue qui détermine si le point Directional Target est affichable ou non

  • View Offset - Du point de vue de la trame de référence (c’est-à-dire possiblement la caméra) et dans la direction de l’indicateur, cette propriété définit la distance à laquelle l’objet doit être du centre de la fenêtre d’affichage.

Exemple de scène de l’indicateur directionnel Assets/MRTK/Examples/Demos/Solvers/Scenes/DirectionalIndicatorSolverExample.unity)

Menu Main avec HandConstraint et HandConstraintPalmUp

Le comportement HandConstraint fournit un résolveur qui limite l’objet suivi à une région sécurisée pour le contenu contraint à la main (par exemple, interface utilisateur et menus Main, etc.). Les régions sûres sont considérées comme des zones qui ne présentent pas d’intersection avec la main. Une classe dérivée de HandConstraint appelée HandConstraintPalmUp est également incluse pour illustrer un comportement courant de l’activation de l’objet suivi du résolveur lorsque la paume est orientée vers l’utilisateur.

Pour des exemples d’utilisation du résolveur de contraintes de main pour créer des menus de main, consultez la documentation sur le menu Main.