Zum Hauptinhalt springen
Version: 2.0.0

Grundlegendes Force-Feedback-Tutorial

Dieses Tutorial führt Sie durch die Erstellung einer grundlegenden haptischen Simulation, die sowohl die Steifigkeit als auch die Dämpfung berücksichtigt und die physikalischen Eigenschaften des Kontakts mit einem statischen Objekt, z. B. einer Kugel, simuliert. Am Ende dieses Tutorials verfügen Sie über eine Simulation, mit der Sie das Vorhandensein einer Kugel spüren und ihre Steifigkeits- und Dämpfungseigenschaften für verschiedene haptische Erfahrungen anpassen können.

Einführung

Die Hauptaufgabe dieses Tutorials besteht darin, eine Funktion zu entwickeln, die in der Lage ist, die Kräfte zu berechnen, die sich aus dem Kontakt mit einer Kugel ergeben, die sowohl Steifigkeit als auch Dämpfung aufweist. Die Steifigkeit verhält sich in diesem Zusammenhang wie eine Feder, die umso mehr Kraft erzeugt, je stärker sie zusammengedrückt wird. Die Dämpfung hingegen stellt den Bewegungswiderstand eines Objekts dar, das umso mehr Widerstand bietet, je schneller es bewegt wird.

Szene einrichten

Beginnen Sie damit, ein Haptic Rig einzurichten, wie es im Abschnitt Schnellstart-Anleitungund gewährleistet die Haptischer UrsprungPosition, Drehung und Skalierung des Objekts werden auf (0, 0, 0) und (1, 1, 1) beziehungsweise.

Erstellen Sie dann eine Kugel mit dem Namen Sphere mit den folgenden Eigenschaften:

  • Position: (0, 0, -0.1) (etwa 10 cm vor dem Gerät)
  • Maßstab: (0.2, 0.2, 0.2) (entspricht einer Kugel mit 20 cm Durchmesser)

Skript zur Kraftrückkopplung

Fügen Sie ein neues C#-Skript zum Sphäre GameObject benannt SphereForceFeedback.cs. Dieses Skript berechnet die Kräfte, die auf den Inverse3 Cursor beim Kontakt mit der Kugel wirken, und berücksichtigt dabei sowohl die Steifigkeit als auch die Dämpfung. Initialisieren Sie das Skript mit den folgenden Eigenschaften:

[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;

Die Kraftberechnung sollte nur erfolgen, wenn der Cursor in die Kugel eindringt, um das Gefühl zu simulieren, ein physisches Objekt mit definierter Steifigkeit und Dämpfung zu berühren. Die ForceCalculation Methode, die sowohl die Position als auch die Geschwindigkeit des Cursors berücksichtigt:

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.LocalPosition, device.LocalVelocity,
_cursorRadius, _ballPosition, _ballRadius);

device.CursorSetLocalForce(force);
}

In der Awake Methode, initialisieren _ballPosition, _ballRadiusund _cursorRadius um die Szenendaten einzurichten:

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();
}

Stellen Sie sicher, dass die Registrierung und Aufhebung der OnDeviceStateChanged Rückruf in der OnEnable und OnDisable Methoden, um die Kraftrückmeldung während der Interaktion richtig zu handhaben:

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

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

Gameplay-Erfahrung

Halten Sie den Cursor Inverse3 gedrückt, rufen Sie den Spielmodus auf und versuchen Sie, die Kugel zu berühren. Sie sollten in der Lage sein, das Vorhandensein der Kugel zu spüren und ihre Steifigkeits- und Dämpfungseigenschaften über den Unity Inspector zu manipulieren, um ein greifbares Gefühl der Interaktion mit einem virtuellen Objekt zu erhalten.

Cursor trifft Kugel

Quelldateien

Die komplette Szene und alle zugehörigen Dateien für dieses Beispiel stehen im Unity-Paketmanager im Tutorials-Beispiel zum Import bereit.

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.LocalPosition, device.LocalVelocity,
_cursorRadius, _ballPosition, _ballRadius);

device.CursorSetLocalForce(force);
}
}
}