Zum Hauptinhalt springen
Version: 2.2.0

Tutorial zum Transformieren des Gerätearbeitsbereichs

Dieses Tutorial erweitert die Grundlegende Kraft-Rückkopplung Tutorial, in dem gezeigt wird, wie man die Position, die Drehung und die Skalierung des Inverse3 Gerät unter Verwendung seiner Raumtransformationseigenschaften und -methoden.

Einführung

Nachdem Sie die Szene für ein grundlegendes Force-Feedback eingerichtet haben, werden Sie vielleicht feststellen, dass Anpassungen am haptischen Ursprung oder am haptischen Controller - wie z. B.Verschieben, Skalieren oder Drehen - das haptische Feedback nicht wie erwartet beeinflussen. Das Feedback entspricht immer noch einer unsichtbaren Kugel, die sich vor dem Gerät befindet und von diesen Transformationen nicht beeinflusst wird.

arbeitsbereich-transform-fail

Diese Diskrepanz tritt auf, weil die Kraftberechnungen die realen, nicht transformierten Koordinaten des Cursors des Geräts verwenden. Um dieses Problem zu beheben, werden wir thread-sichere, zwischengespeicherte Transformationsmatrizen verwenden, die von der Inverse3 Controller-Komponente bereitgestellt werden und es uns ermöglichen, Weltraumtransformationen auf die Berechnungen des haptischen Feedbacks anzuwenden.

Szene einrichten

Beginnen Sie mit der Szene aus dem Tutorial "Grundlegende Kraftrückmeldung" und stellen Sie sicher, dass die Händigkeit des Inverse3 Controllers zu Ihrem Gerät passt. Drehen Sie den Haptic Controller so, dass das Haply Logo zur Kamera zeigt, und passen Sie die Skala für den Haptic Origin wie gewünscht an, um die Reichweite des Cursors zu erhöhen oder seine Position anzupassen.

inverse3-Rechtshänder-Setup

arbeitsbereich-transform-rechtshänder

Skriptänderungen für Force Feedback

Kopieren Sie die SphereForceFeedback.cs Skript aus dem Tutorium "Grundlegendes Force-Feedback" und nehmen Sie die folgenden Anpassungen in der Datei OnDeviceStateChanged Rückruf:

  • Ersetzen Sie device.CursorLocalPosition mit device.CursorPosition.
  • Ersetzen Sie device.CursorLocalVelocity mit device.CursorVelocity.
  • Ersetzen Sie device.CursorSetLocalForce(force) mit device.CursorSetForce(force).
private void OnDeviceStateChanged(Inverse3 device) {
var force = ForceCalculation(device.CursorPosition, device.CursorVelocity,
_cursorRadius, _ballPosition, _ballRadius);

device.CursorSetForce(force);
}

Gameplay-Erfahrung

Halten Sie den Cursor Inverse3 gedrückt und rufen Sie den Spielmodus auf. Versuchen Sie, die Kugel wie im vorherigen Beispiel zu berühren. Sie sollten nun ein genaues haptisches Feedback erhalten, das die auf den haptischen Ursprung und die haptische Steuerung angewendeten Transformationen widerspiegelt.

cursor-hit-sphere-upscaled

Quelldateien

Die komplette Szene und die zugehörigen Dateien für dieses Beispiel können aus dem Tutorials-Beispiel im Unity-Paketmanager importiert werden.

Die Beispielszene enthält zusätzliche Skripte für Laufzeitanpassungen des Haptic Controllers und des Haptic Origins.

SphereForceFeedback.cs

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

using Haply.Inverse.Unity;
using UnityEngine;
using UnityEngine.Serialization;

namespace Haply.Samples.Tutorials._3_DeviceSpaceTransform
{
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. Using 'device.CursorPosition' instead of 'device.CursorLocalPosition'
// ensures the force calculation considers the device's offset and rotation in world space.
var force = ForceCalculation(device.CursorPosition, device.CursorVelocity,
_cursorRadius, _ballPosition, _ballRadius);

// Apply the calculated force to the cursor. Using 'device.CursorSetForce' instead of
// 'device.CursorSetLocalForce' ensures that the force vector is correctly converted
// from world space to the device's local space.
device.CursorSetForce(force);
}
}
}