Skip to main content
Version : 2.1.1

Tutoriel de base sur le retour de force

Ce didacticiel vous guide dans la création d'une simulation haptique de base qui intègre à la fois la rigidité et l'amortissement, en simulant les propriétés physiques du contact avec un objet statique, tel qu'une sphère. À la fin de ce tutoriel, vous disposerez d'une simulation qui vous permettra de ressentir 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 haptiques.

Introduction

Le défi principal de ce tutoriel est de développer une fonction capable de calculer les forces résultant du contact avec une sphère qui présente à la fois de la rigidité et de l'amortissement. Dans ce contexte, la rigidité agit comme un ressort, générant une force d'autant plus grande qu'elle est comprimée. L'amortissement, quant à lui, représente la résistance d'un objet au mouvement, offrant d'autant plus de résistance qu'il est déplacé rapidement.

Configuration de la scène

Commencez par mettre en place une plate-forme haptique, comme indiqué dans la rubrique Guide de démarrage rapide, en veillant à ce que les Origine haptiqueLa position, la rotation et l'échelle de l'image sont réglées sur (0, 0, 0) et (1, 1, 1) respectivement.

Créez ensuite une sphère nommée Sphere avec les propriétés suivantes :

  • Poste : (0, 0, -0.1) (environ 10 cm devant l'appareil)
  • Échelle : (0.2, 0.2, 0.2) (correspondant à une sphère de 20 cm de diamètre)

Script de retour de force

Ajouter un nouveau script C# à la page Sphère GameObject nommé SphereForceFeedback.cs. Ce script calculera les forces appliquées au curseur Inverse3 lors du contact avec la sphère, en tenant compte de la rigidité et de l'amortissement. Initialiser le script avec les 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 de la force ne doit se produire que lorsque le curseur pénètre dans la sphère, ce qui simule la sensation de toucher un objet physique avec une rigidité et un amortissement définis. Le calcul de la force ne doit se produire que lorsque le curseur pénètre dans la sphère. ForceCalculation se chargera de cette tâche, en tenant compte 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(Inverse3 device)
{
// Calculate the ball force
var force = ForceCalculation(device.CursorLocalPosition, device.CursorLocalVelocity,
_cursorRadius, _ballPosition, _ballRadius);

device.CursorSetLocalForce(force);
}

Dans le cadre de la Awake initialiser la méthode _ballPosition, _ballRadiuset _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.Model.transform.lossyScale.x / 2f;
}

private void Awake()
{
SaveSceneData();
}

S'assurer d'enregistrer et de désenregistrer le OnDeviceStateChanged dans le fichier OnEnable et OnDisable respectivement, pour 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

Tout en maintenant le curseur Inverse3, passez en mode lecture et essayez de toucher la sphère. Vous devriez pouvoir sentir la présence de la sphère et manipuler ses propriétés de rigidité et d'amortissement dans l'inspecteur Unity, ce qui vous donnera un sentiment tangible d'interaction avec un objet virtuel.

le curseur touche la sphère

Fichiers sources

La scène complète et tous les fichiers associés à cet exemple peuvent être importés à partir de l'échantillon Tutorials dans le Unity Package Manager.

SphereForceFeedback.cs

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

using Haply.Inverse.Unity;
using UnityEngine;

namespace Haply.Samples.Tutorials._2_BasicForceFeedback
{
public class SphereForceFeedback : MonoBehaviour
{
// must assign in inspector
public Inverse3 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.Model.transform.lossyScale.x / 2f;
}

/// <summary>
/// Saves the initial scene data cache.
/// </summary>
private void Awake()
{
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;
}

/// <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="device">The Inverse3 device instance.</param>
private void OnDeviceStateChanged(Inverse3 device)
{
// Calculate the ball force
var force = ForceCalculation(device.CursorLocalPosition, device.CursorLocalVelocity,
_cursorRadius, _ballPosition, _ballRadius);

device.CursorSetLocalForce(force);
}
}
}