Tutoriel sur la navigation dans l'espace de travail
Ce tutoriel montre comment ajuster l'échelle du curseur à l'aide de la poignée, ce qui permet à l'utilisateur de contrôler la précision et la taille de la construction de l'espace de travail navigable. sur la précision et la taille de la construction de l'espace de travail navigable.
Cet exemple s'appuie sur la scène créée dans la section Mise à l'échelle et placement de l'espace de travail en fournissant un contrôle basé sur les poignées sur l'espace de travail haptique. sur l'espace de travail haptique. L'objectif est d'utiliser le bouton de la poignée pour déclencher le décalage de l'espace de travail et d'utiliser la position du curseur pour le modifier, et le second pour déclencher le redimensionnement de l'espace de travail et utiliser l'enroulement de la poignée pour le modifier. l'espace de travail et d'utiliser l'enroulement de la poignée pour le modifier.
À cette fin, lorsque l'utilisateur appuie sur un bouton, le script WorkspaceOffsetController enregistre la dernière position connue du curseur et cesse de mettre à jour l'emplacement de l'avatar. position connue du curseur et arrête la mise à jour de l'emplacement de l'avatar. Alternativement, le WorkspaceScaleController enregistre l'échelle de l'espace de travail et l'orientation actuelle de la poignée lorsqu'un bouton est enfoncé. Grâce à l'utilisation conjointe des deux scripts, le déplacement du curseur créera un nouveau décalage du curseur, tandis que la rotation de la poignée créera un nouveau décalage du curseur. tandis que la rotation de la poignée modifie l'échelle de l'espace de travail. Dans cette implémentation, une rotation CCW autour de l'axe Z correspond à une réduction de l'échelle de l'espace de travail et vice versa. Lorsque l'utilisateur relâche le bouton, les deux décalages cessent de changer et l'avatar du curseur se déplace à nouveau. une fois de plus.
Configuration de la scène
Commencez par ajouter le composant HandleTread au GameObject de l 'espace de travail haptique. Dans la vue inspecteur, définissez le curseur comme HandleThread Avatar, puis ajoutez un cube comme enfant du curseur pour visualiser sa rotation (voir le Guide de démarrage rapide pour plus de détails). pour visualiser sa rotation (voir le Guide de démarrage rapide pour plus de détails).
Le composant WorkspaceOffsetController
Créer un nouveau script WorkspaceOffsetController.cs
et l'ajouter au Espace de travail haptique
GameObject. Ajoutez ensuite une référence au curseur contrôlé par la fonction Fil haptique
private Transform m_cursor;
private void Awake()
{
m_cursor = GetComponent<HapticThread>().avatar;
}
Les propriétés suivantes s'appliquent alors :
private Vector3 m_basePosition;
private Vector3 m_cursorBasePosition;
m_basePosition
sauvegarde la position de l'espace de travail avant toute modification,
tandis que m_cursorBasePosition
enregistre la position du curseur.
Ensuite, ajoutez l'élément active
champ encapsulé correspondant à l'état du bouton qui initialise les champs précédents et permet le décalage de l'espace de travail à chaque mise à jour.
champs précédents et active le décalage de l'espace de travail à chaque mise à jour :
public bool active
{
get => m_active;
set
{
if (value)
{
m_basePosition = transform.localPosition;
m_cursorBasePosition = m_cursor.localPosition;
}
m_active = value;
}
}
private bool m_active;
Enfin, ajouter Update
qui, lorsqu'elle est active, modifie le décalage de l'espace de travail en calculant le changement de la position du curseur par rapport à la position de base.
position du curseur par rapport à la position de base. Notez que le changement de position doit être mis à l'échelle par la transformation de l'espace de travail pour rester précis.
la transformation de l'espace de travail pour rester précis.
private void Update()
{
if (active)
{
// Move cursor offset relative to cursor position
transform.position = m_basePosition - Vector3.Scale(m_cursor.localPosition - m_cursorBasePosition, transform.lossyScale);
}
}
Pour lier le bouton de la poignée au champ actif, sélectionnez l'option Espace de travail haptique et dans le panneau de l'inspecteur
ajouter (Espace de travail haptique)WorkspaceOffsetController.active = false
à OnButtonUp()
et (Espace de travail haptique)WorkspaceOffsetController.active = true
à OnButtonDown()
.
Optionnel : Lier le mouvement de la caméra
La modification du décalage du curseur peut être rendue intuitive en déplaçant la caméra le long de l'espace de travail. Ainsi, même si le décalage est important, le curseur ne sortira jamais du cadre. Ainsi, même si le décalage est important, le curseur ne sortira jamais du cadre.
Pour ce faire, ajoutez un composant de contrainte de position à la caméra principale, définissez le paramètre * Espace de travail haptique* comme source, et appuyez sur le bouton Activer.
Le composant WorkspaceScaleController
Créer un nouveau script WorkspaceScaleController.cs
et l'ajouter au Espace de travail haptique GameObject.
Ajoutez ensuite une référence au curseur contrôlé par la fonction HandleThread
private Transform m_cursor;
private void Awake()
{
m_cursor = GetComponent<HandleThread>().avatar;
}
Ajoutez ensuite les paramètres suivants :
public float scalingFactor = 0.25f;
public float minimumScale = 1f;
public float maximumScale = 20f;
scalingFactor
convertit la rotation de la poignée en degrés en une échelle numérique qui est délimitée par l'échelle des degrés.
par la minimumScale
et le maximumScale
.
Ajoutez ensuite les champs suivants :
private float m_baseScale;
private float m_cursorBaseAngle;
m_baseScale
sauvegarde l'échelle du paysage avant toute modification, tandis que m_cursorBaseAngle
enregistre
l'orientation du curseur sur l'axe Y.
Ajouter ensuite GetTotalDegrees
méthode :
private float m_cursorPreviousAngle;
private int m_rotationCount;
private float GetTotalDegrees(float currentAngle, float baseAngle)
{
if (currentAngle - m_cursorPreviousAngle > 330)
m_rotationCount--;
else if (m_cursorPreviousAngle - currentAngle > 330)
m_rotationCount++;
m_cursorPreviousAngle = currentAngle;
return 360f * m_rotationCount + (currentAngle - baseAngle);
}
La poignée peut tourner plus d'une fois autour de son propre axe, ce qui peut entraîner des sauts soudains dans le déplacement lors du franchissement de 0°. déplacement lors du franchissement de 0°. Cette fonction vérifie l'angle actuel par rapport à l'angle précédent et détecte quand 0° a été franchi et dans quelle direction, ce qui garantit que le décalage d'angle renvoyé est exact, même si plus d'une rotation complète a eu lieu. est exact, même si plus d'une rotation complète a eu lieu.
Ensuite, le active
champ encapsulé, correspondant à l'état du bouton, qui initialise les champs précédents et permet la mise à l'échelle de l'espace de travail à chaque mise à jour.
et active la mise à l'échelle de l'espace de travail à chaque mise à jour :
public bool active
{
get => m_active;
set
{
if (value)
{
m_rotationCount = 0;
m_baseScale = transform.localScale.z;
m_cursorPreviousAngle = m_cursorBaseAngle = m_cursor.localEulerAngles.z;
}
m_active = value;
}
}
private bool m_active;
Enfin, ajouter Update
qui, s'il est actif, modifie les transformées de l'élément Espace de travail haptique en fonction de
l'angle de la poignée.
private void Update()
{
if (active)
{
// Calculate scale relative to cursor roll on Z-axis rotation
var totalDegrees = GetTotalDegrees(m_cursor.localEulerAngles.z, m_cursorBaseAngle);
var scale = m_baseScale - totalDegrees * scalingFactor / 100f;
// Limit between minimumScale and maximumScale
scale = Mathf.Clamp(scale, minimumScale, maximumScale);
// Set cursor offset scale (same on each axis)
transform.localScale = Vector3.one * scale;
// Invert cursor scale to keep its original size
m_cursor.localScale = Vector3.one / scale;
}
}
Bouton de la poignée de reliure
Comme précédemment, pour lier le bouton de la poignée à l'icône active
sélectionner le champ Espace de travail haptique et dans le
dans le panneau de l'inspecteur, ajoutez (Espace de travail haptique)WorkspaceScaleController.active = false
à OnButtonUp()
et (Espace de travail haptique)WorkspaceScaleController.active = true
à OnButtonDown()
.
Résultat
Vous pouvez maintenant naviguer facilement dans la scène en mettant à l'échelle et en déplaçant l'espace de travail en appuyant sur le bouton Handle en appuyant sur le bouton "Handle".
Fichiers sources
La scène finale et tous les fichiers associés utilisés dans cet exemple peuvent être importés depuis l'échantillon Basic Force Feedback and Workspace Control dans le gestionnaire de paquets de Unity. L'exemple Unity contient des améliorations de la qualité de vie qui sortent du cadre de ce tutoriel :
- Une bulle transparente visualisant la taille de l'espace de travail
- Raccourcis clavier
- Appuyer sur
M
La touche permet de déplacer l'espace de travail uniquement - Appuyer sur
S
La touche permet de mettre l'espace de travail à l'échelle uniquement
- Appuyer sur
- Une interface utilisateur pour afficher les valeurs actuelles de décalage et d'échelle
WorkspaceOffsetController.cs
using Haply.HardwareAPI.Unity;
using UnityEngine;
public class WorkspaceOffsetController : MonoBehaviour
{
// Movable cursor with position controlled by Haptic Thread
private Transform m_cursor;
// Saved workspace and cursor values at transformation beginning
private Vector3 m_basePosition;
private Vector3 m_cursorBasePosition;
// If true, the workspace offset is set relatively to the cursor position on each Update() loop
public bool active
{
get => m_active;
set
{
if (value)
{
m_basePosition = transform.localPosition;
m_cursorBasePosition = m_cursor.localPosition;
}
m_active = value;
}
}
private bool m_active;
private void Awake()
{
// Get the moving cursor from the HapticThread
m_cursor = GetComponent<HapticThread>().avatar;
}
private void Update()
{
if (active)
{
// Update the workspace offset relative to cursor position
transform.position = m_basePosition - Vector3.Scale(m_cursor.localPosition - m_cursorBasePosition, transform.lossyScale);
}
}
}
WorkspaceScaleController.cs
using System;
using Haply.HardwareAPI.Unity;
using UnityEngine;
public class WorkspaceScaleController : MonoBehaviour
{
// Movable cursor with rotation controlled by Handle Thread
private Transform m_cursor;
[Tooltip("Sensitivity of scaling on handle rotation")]
public float scalingFactor = 3f;
public float minimumScale = 1f;
public float maximumScale = 5f;
// Saved workspace and cursor values at transformation beginning
private float m_baseScale;
private float m_cursorBaseAngle;
private float m_cursorPreviousAngle;
private int m_rotationCount;
// If enabled the workspace will be uniformly scaled relatively to cursor roll (Z-axis rotation) on each Update() loop
public bool active
{
get => m_active;
set
{
if (value)
{
m_rotationCount = 0;
m_baseScale = transform.localScale.z;
m_cursorPreviousAngle = m_cursorBaseAngle = m_cursor.localEulerAngles.z;
}
m_active = value;
}
}
private bool m_active;
private void Awake()
{
// Get the rotating cursor from the HandleThread
m_cursor = GetComponent<HandleThread>().avatar;
}
private void Update()
{
if (active)
{
// Calculate scale relative to cursor roll on Z-axis rotation
var totalDegrees = GetTotalDegrees(m_cursor.localEulerAngles.z, m_cursorBaseAngle);
var scale = m_baseScale - totalDegrees * scalingFactor / 100f;
// Limit between minimumScale and maximumScale
scale = Mathf.Clamp(scale, minimumScale, maximumScale);
// Set cursor offset scale (same on each axis)
transform.localScale = Vector3.one * scale;
// Invert cursor scale to keep its original size
m_cursor.localScale = Vector3.one / scale;
}
}
// Return the total degrees between baseAngle and currentAngle over the 360 degrees limitation
private float GetTotalDegrees(float currentAngle, float baseAngle)
{
if (currentAngle - m_cursorPreviousAngle > 330)
m_rotationCount--;
else if (m_cursorPreviousAngle - currentAngle > 330)
m_rotationCount++;
m_cursorPreviousAngle = currentAngle;
return 360f * m_rotationCount + (currentAngle - baseAngle);
}
}