Skip to main content
Version: 2.2.0

Basic Force and Position Tutorial

This guide provides a simple demonstration of applying forces and visualizing the movement of an Inverse3 cursor. By the end, the Inverse3 will simulate the sensation of being tethered to its starting position with a virtual rubber band, while a spherical GameObject displays the cursor's position.

Introduction

The Quick Start Guide introduced the Inverse3 object, its functionality, and the method to generate constant forces. Our objective here is to simulate a rubber band effect on the cursor. A rubber band behaves similarly to a spring, meaning its force is influenced by both the stiffness and the distance between its two endpoints. Thus, we aim to devise a function that, given a position and stiffness, produces a force causing the cursor to resist movement away from the origin.

Scene Setup

Initiate by creating a Haptic Rig (one hand) via the GameObjects > Haply menu.

ForceAndPosition Component

Select the Haptic Origin GameObject and add a new script named ForceAndPosition.cs and populate the ForceAndPosition class with the following code:

[SerializeField]
private Inverse3 inverse3 = null;

[SerializeField, Range(0, 400)]
private float stiffness = 100;

private Vector3 _initialPosition = Vector3.zero;

private Vector3 ForceCalculation(in Vector3 position)
{
if (_initialPosition == Vector3.zero)
{
// Save the initial device effector position
_initialPosition = position;
}
// Return force opposing movement from the initial position
return (_initialPosition - position) * stiffness;
}

This segment sets the stiffness at 100 Newtons per meter (N/m), simulating a relatively soft spring. It also introduces _initialPosition, a vector that captures the cursor's starting position. The ForceCalculation method records the initial position during its first execution and subsequently computes the force output opposing the cursor's movement.

Incorporate the OnDeviceStateChanged callbacks in the OnEnable and OnDisable methods, as detailed in the Quick Start Guide:

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

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

Gameplay

Hold the Inverse3 cursor, activate Play Mode, and attempt to maneuver the device. You'll observe that displacing the cursor generates a force. The further the cursor is moved from its starting position, the more pronounced this force becomes.

cursor move

Source files

The final scene and all associated files used by this example can be imported from the Tutorials sample in Unity's package manager.

ForceAndPosition.cs

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

using Haply.Inverse.Unity;
using UnityEngine;

namespace Haply.Samples.Tutorials._1_ForceAndPosition
{
/// <summary>
/// Demonstrates the application of force to maintain the cursor at its initial position.
/// </summary>
public class ForceAndPosition : MonoBehaviour
{
// Must be assigned in inspector
public Inverse3 inverse3;

[Range(0, 400)]
// Stiffness of the force feedback.
public float stiffness = 100;

// Stores the initial position of the cursor.
private Vector3 _initialPosition = Vector3.zero;

/// <summary>
/// Subscribes to the DeviceStateChanged event when the component is enabled.
/// </summary>
protected void OnEnable()
{
inverse3.DeviceStateChanged += OnDeviceStateChanged;
}

/// <summary>
/// Unsubscribes from the DeviceStateChanged event and reset the force when the component is disabled.
/// </summary>
protected void OnDisable()
{
inverse3.DeviceStateChanged -= OnDeviceStateChanged;
}

/// <summary>
/// Calculates the force required to maintain the cursor at its initial position.
/// </summary>
/// <param name="position">The current position of the cursor.</param>
/// <returns>The calculated force vector.</returns>
private Vector3 ForceCalculation(in Vector3 position)
{
if (_initialPosition == Vector3.zero)
{
// save the first device effector position
_initialPosition = position;
}
// return opposite force to stay at initial position
return (_initialPosition - position) * stiffness;
}

/// <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 force.
var force = ForceCalculation(device.CursorLocalPosition);

// Apply the force to the cursor.
inverse3.CursorSetLocalForce(force);
}
}
} // namespace Haply.Samples.Tutorials._1_ForceAndPosition