Aller au contenu principal

Tutoriel sur les bases du retour de force

Ce tutoriel vous guidera dans la création d'une simulation tactile de base, intégrant à la fois la rigidité et l'amortissement, afin de reproduire les propriétés physiques du contact avec un objet statique (par exemple, une sphère). À l'issue de ce tutoriel, vous disposerez d'un programme de simulation capable de détecter la présence d'une sphère et d'ajuster ses propriétés de rigidité et d'amortissement pour obtenir différentes expériences tactiles.

Présentation​

Le principal défi de ce tutoriel consiste à développer une fonction permettant de calculer la force générée lors du contact avec une sphère présentant à la fois une rigidité et un amortissement. Dans ce contexte, la rigidité agit à la manière d'un ressort : plus la compression est importante, plus la force générée est élevée. D'autre part, l'amortissement représente la résistance d'un objet au mouvement : plus l'objet se déplace rapidement, plus la résistance qu'il subit est importante.

Mise en scène​

Tout d'abord, conformément à Guide de démarrage Suivez les instructions fournies pour configurer le Haptic Rig et assurez-vous que Origine haptique La position, la rotation et le zoom sont respectivement définis comme suit : (0, 0, 0) et (1, 1, 1)。

Ensuite, créez un fichier nommé Sphere une sphère, puis définissez les propriétés suivantes :

  • Emplacement :(0, 0, -0.1)(à environ 10 cm à l'avant de l'appareil)
  • Zoom :(0.2, 0.2, 0.2)(correspondant à une sphère de 20 centimètres de diamètre)

Script de retour de force​

pour Sphère Ajoutez un nouveau script C# à l'objet du jeu, nommé SphereForceFeedback.cs。 Ce script calcule la force exercée sur le curseur « Inverse3 » lors d'un contact avec une sphère, en tenant compte de la rigidité et de l'amortissement. Initialisez le script à l'aide des propriétés suivantes :

[SerializeField]
private Inverse3 inverse3;

[Range(0, 800)]
public float stiffness = 300f;
[Range(0, 3)]
public float damping = 1f;

private Vector3 _ballPosition;
private float _ballRadius;
private float _cursorRadius;

Le calcul des forces ne doit être effectué que lorsque le curseur traverse la sphère, afin de simuler la sensation de toucher un objet physique doté d'une rigidité et d'un amortissement spécifiques. ForceCalculation La méthode se chargera de ce calcul, en tenant compte à la fois de la position et de la vitesse du curseur :

private Vector3 ForceCalculation(Vector3 cursorPosition, Vector3 cursorVelocity, float cursorRadius,
Vector3 otherPosition, float otherRadius)
{
var force = Vector3.zero;

var distanceVector = cursorPosition - otherPosition;
var distance = distanceVector.magnitude;
var penetration = otherRadius + cursorRadius - distance;

if (penetration > 0)
{
// Normalize the distance vector to get the direction of the force
var normal = distanceVector.normalized;

// Calculate the force based on penetration
force = normal * penetration * stiffness;

// Apply damping based on the cursor velocity
force -= cursorVelocity * damping;
}

return force;
}

private void OnDeviceStateChanged(object sender, Inverse3EventArgs args)
{
var inverse3 = args.DeviceController;
// Calculate the ball force
var force = ForceCalculation(inverse3.CursorLocalPosition, inverse3.CursorLocalVelocity,
_cursorRadius, _ballPosition, _ballRadius);

inverse3.SetCursorLocalForce(force);
}

à Awake Dans la méthode, initialisation _ballPosition,_ballRadius et _cursorRadius, pour configurer les données de scène :

private void SaveSceneData()
{
var t = transform;
_ballPosition = t.position;
_ballRadius = t.lossyScale.x / 2f;

_cursorRadius = inverse3.Cursor.Radius;
}

private void Awake()
{
SaveSceneData();
}

Veuillez vous assurer de saisir respectivement dans OnEnable et OnDisable Inscription et désinscription dans la méthode OnDeviceStateChanged Une fonction de rappel permettant de gérer correctement le retour de force pendant l'interaction :

protected void OnEnable()
{
inverse3.DeviceStateChanged += OnDeviceStateChanged;
}

protected void OnDisable()
{
inverse3.DeviceStateChanged -= OnDeviceStateChanged;
}

Expérience de jeu​

Lorsque vous placez le curseur sur Inverse3 , le mode lecture s'active ; essayez de toucher la sphère. Vous devriez pouvoir percevoir la présence de la sphère et, en ajustant ses propriétés de rigidité et d'amortissement dans l'Inspecteur Unity, obtenir une sensation tactile réaliste lors de l'interaction avec cet objet virtuel.

Le curseur touche la sphère

Fichier source​

Le scénario complet de cet exemple ainsi que tous les fichiers associés peuvent être importés à partir de la section « Tutorials » du gestionnaire de paquets Unity.

SphereForceFeedback.cs​

/*
* Copyright 2024 Haply Robotics Inc. All rights reserved.
*/

using Haply.Inverse.DeviceControllers;
using Haply.Inverse.DeviceData;
using UnityEngine;

namespace Haply.Samples.Tutorials._2_BasicForceFeedback
{
public class SphereForceFeedback : MonoBehaviour
{
public Inverse3Controller inverse3;

[Range(0, 800)]
// Stiffness of the force feedback.
public float stiffness = 300f;

[Range(0, 3)]
public float damping = 1f;

private Vector3 _ballPosition;
private float _ballRadius;
private float _cursorRadius;

/// <summary>
/// Stores the cursor and sphere transform data for access by the haptic thread.
/// </summary>
private void SaveSceneData()
{
var t = transform;
_ballPosition = t.position;
_ballRadius = t.lossyScale.x / 2f;

_cursorRadius = inverse3.Cursor.Radius;
}

/// <summary>
/// Saves the initial scene data cache.
/// </summary>
private void Awake()
{
inverse3 ??= FindObjectOfType<Inverse3Controller>();
inverse3.Ready.AddListener((device, args) => SaveSceneData());
}

/// <summary>
/// Subscribes to the DeviceStateChanged event.
/// </summary>
private void OnEnable()
{
inverse3.DeviceStateChanged += OnDeviceStateChanged;
}

/// <summary>
/// Unsubscribes from the DeviceStateChanged event.
/// </summary>
private void OnDisable()
{
inverse3.DeviceStateChanged -= OnDeviceStateChanged;
inverse3.Release();
}

/// <summary>
/// Calculates the force based on the cursor's position and another sphere position.
/// </summary>
/// <param name="cursorPosition">The position of the cursor.</param>
/// <param name="cursorVelocity">The velocity of the cursor.</param>
/// <param name="cursorRadius">The radius of the cursor.</param>
/// <param name="otherPosition">The position of the other sphere (e.g., ball).</param>
/// <param name="otherRadius">The radius of the other sphere.</param>
/// <returns>The calculated force vector.</returns>
private Vector3 ForceCalculation(Vector3 cursorPosition, Vector3 cursorVelocity, float cursorRadius,
Vector3 otherPosition, float otherRadius)
{
var force = Vector3.zero;

var distanceVector = cursorPosition - otherPosition;
var distance = distanceVector.magnitude;
var penetration = otherRadius + cursorRadius - distance;

if (penetration > 0)
{
// Normalize the distance vector to get the direction of the force
var normal = distanceVector.normalized;

// Calculate the force based on penetration
force = normal * penetration * stiffness;

// Apply damping based on the cursor velocity
force -= cursorVelocity * damping;
}

return force;
}

/// <summary>
/// Event handler that calculates and send the force to the device when the cursor's position changes.
/// </summary>
/// <param name="sender">The Inverse3 data object.</param>
/// <param name="args">The event arguments containing the device data.</param>
private void OnDeviceStateChanged(object sender, Inverse3EventArgs args)
{
var inverse3 = args.DeviceController;
// Calculate the ball force
var force = ForceCalculation(inverse3.CursorLocalPosition, inverse3.CursorLocalVelocity,
_cursorRadius, _ballPosition, _ballRadius);

inverse3.SetCursorLocalForce(force);
}
}
}