Zum Hauptinhalt springen
Version: neueste

Tutorial zur Arbeitsbereich-Navigation

Dieses Tutorial zeigt, wie man die Skalierung des Cursors mit Hilfe des Griffs anpasst, um dem Benutzer die Kontrolle über die Genauigkeit und Größe des navigierbaren Arbeitsbereichs.

Dieses Beispiel baut auf der Szene auf, die in Arbeitsbereichskalierung und -platzierung, indem es eine griffbasierte Steuerung über den haptischen Arbeitsbereich. Ziel ist es, mit der Grifftaste eine Verschiebung des Arbeitsbereichs auszulösen auszulösen und die Cursorposition zu verwenden, um sie zu ändern, und die zweite, um die Größenänderung des Arbeitsbereichs auszulösen und mit Hilfe der Handle-Rolle zu ändern.

Zu diesem Zweck speichert das Skript WorkspaceOffsetController die letzte bekannte Cursorposition, wenn der Benutzer eine Taste drückt. bekannte Cursorposition und stoppt die Aktualisierung der Avatarposition. Alternativ dazu speichert das Skript WorkspaceScaleController die Skalierung des Arbeitsbereichs und die aktuelle Griffausrichtung, wenn eine Taste gedrückt wird. Wenn die beiden Skripte zusammen verwendet werden, wird durch das Bewegen des Cursors ein neuer Cursor-Offset, während das Drehen des Griffs die Skalierung des Arbeitsbereichs ändert. In dieser Implementierung, entspricht eine Drehung um die Z-Achse im Uhrzeigersinn einer Verkleinerung der Arbeitsbereichsskalierung und vice versa. Wenn der Benutzer die Taste loslässt, ändern sich die beiden Offsets nicht mehr und der Cursor-Avatar bewegt sich noch einmal.

Szene einrichten

Fügen Sie zunächst die Komponente HandleTread zum GameObject Haptic Workspace hinzu. In der Inspektoransicht den Cursor als HandleThread Avatar ein und fügen Sie dann einen Würfel als Kind des Cursors einen Würfel hinzu, um seine Drehung zu visualisieren (siehe Schnellstartanleitung für weitere Details).

Griffgewinde

Die Komponente WorkspaceOffsetController

Ein neues Skript erstellen WorkspaceOffsetController.cs und fügen Sie sie dem Haptischer Arbeitsbereich GameObject. Fügen Sie dann einen Verweis auf den Cursor hinzu, der von der Option HapticThread

    private Transform m_cursor;

private void Awake()
{
m_cursor = GetComponent<HapticThread>().avatar;
}

Dann gelten die folgenden Eigenschaften:

private Vector3 m_basePosition;
private Vector3 m_cursorBasePosition;

m_basePosition speichert die Position der Arbeitsfläche vor jeder Änderung, während m_cursorBasePosition speichert die Cursorposition.

Als nächstes fügen Sie die active gekapseltes Feld, das dem Schaltflächenstatus entspricht und die vorherigen Felder initialisiert vorherige Felder initialisiert und die Verschiebung des Arbeitsbereichs bei jeder Aktualisierung aktiviert:

public bool active
{
get => m_active;
set
{
if (value)
{
m_basePosition = transform.localPosition;
m_cursorBasePosition = m_cursor.localPosition;
}
m_active = value;
}
}
private bool m_active;

Schließlich fügen Sie Update die, wenn sie aktiv ist, den Offset des Arbeitsbereichs durch Berechnung der Änderung der der Cursorposition relativ zur Basisposition berechnet. Beachten Sie, dass die Positionsänderung durch die Arbeitsbereichstransformation skaliert werden muss die Arbeitsbereichstransformation skaliert werden muss, um genau zu sein.

private void Update()
{
if (active)
{
// Move cursor offset relative to cursor position
transform.position = m_basePosition - Vector3.Scale(m_cursor.localPosition - m_cursorBasePosition, transform.lossyScale);
}
}

Um die Griffschaltfläche an das aktive Feld zu binden, wählen Sie die Schaltfläche Haptischer Arbeitsbereich und in der Inspektor-Panel, fügen Sie (Haptischer Arbeitsbereich)WorkspaceOffsetController.active = false zu OnButtonUp() Ereignis und (Haptischer Arbeitsbereich)WorkspaceOffsetController.active = true zu OnButtonDown().

Arbeitsbereich Offset-Handle-Ereignisse

Optional: Binden der Kamerabewegung

Die Änderung des Cursor-Offsets kann intuitiv erfolgen, indem die Kamera mit dem Arbeitsbereich mitbewegt wird. Das hat zur Folge wird der Cursor auch bei einem großen Versatz nicht aus dem Rahmen fallen.

keine Einschränkung der Kamera mit Kamerazwang

Um dies zu erreichen, fügen Sie der Hauptkamera eine Positionsbeschränkungskomponente hinzu, setzen Sie die * Haptischen Arbeitsbereich* als Quelle ein und drücken Sie die Schaltfläche Aktivieren.

Kamerazwang

Die Komponente WorkspaceScaleController

Ein neues Skript erstellen WorkspaceScaleController.cs und fügen Sie sie dem Haptischer Arbeitsbereich GameObject. Fügen Sie dann einen Verweis auf den Cursor hinzu, der von der Option HandleThread

private Transform m_cursor;

private void Awake()
{
m_cursor = GetComponent<HandleThread>().avatar;
}

Fügen Sie dann die folgenden Einstellungen hinzu:

public float scalingFactor = 0.25f;
public float minimumScale = 1f;
public float maximumScale = 20f;

Kamerazwang

scalingFactor wandelt die Drehung des Griffs in Grad in eine numerische Skala um, die an durch den minimumScale und die maximumScale.

Fügen Sie dann die folgenden Felder hinzu:

private float m_baseScale;
private float m_cursorBaseAngle;

m_baseScale speichert den Maßstab der Arbeitslandschaft vor jeder Änderung, während m_cursorBaseAngle speichert die Ausrichtung des Cursors auf der Y-Achse.

Als nächstes fügen Sie GetTotalDegrees Methode:

private float m_cursorPreviousAngle;
private int m_rotationCount;

private float GetTotalDegrees(float currentAngle, float baseAngle)
{
if (currentAngle - m_cursorPreviousAngle > 330)
m_rotationCount--;
else if (m_cursorPreviousAngle - currentAngle > 330)
m_rotationCount++;

m_cursorPreviousAngle = currentAngle;

return 360f * m_rotationCount + (currentAngle - baseAngle);
}

Der Griff kann sich mehr als einmal um seine eigene Achse drehen, was zu plötzlichen Sprüngen in der Verschiebung beim Überschreiten von 0° führen kann. Diese Funktion prüft den aktuellen Winkel mit dem vorherigen Winkel und erkennt, wann und in welcher Richtung 0° überschritten wurde und garantiert, dass die zurückgegebene Winkelverschiebung genau ist, auch wenn mehr als eine vollständige Drehung stattgefunden hat.

Als nächstes wird die active gekapseltes Feld, das dem Zustand der Schaltfläche entspricht und die vorherigen Felder initialisiert und die Skalierung des Arbeitsbereichs bei jeder Aktualisierung aktiviert:

public bool active
{
get => m_active;
set
{
if (value)
{
m_rotationCount = 0;
m_baseScale = transform.localScale.z;
m_cursorPreviousAngle = m_cursorBaseAngle = m_cursor.localEulerAngles.z;

}
m_active = value;
}
}
private bool m_active;

Schließlich fügen Sie Update die, wenn sie aktiv ist, die Transformationen der Haptischer Arbeitsbereich basierend auf dem Griffwinkel.

private void Update()
{
if (active)
{
// Calculate scale relative to cursor roll on Z-axis rotation
var totalDegrees = GetTotalDegrees(m_cursor.localEulerAngles.z, m_cursorBaseAngle);
var scale = m_baseScale - totalDegrees * scalingFactor / 100f;

// Limit between minimumScale and maximumScale
scale = Mathf.Clamp(scale, minimumScale, maximumScale);

// Set cursor offset scale (same on each axis)
transform.localScale = Vector3.one * scale;
// Invert cursor scale to keep its original size
m_cursor.localScale = Vector3.one / scale;
}
}

Knopf für den Bindegriff

Um die Grifftaste an die Schaltfläche zu binden, müssen Sie wie bisher active wählen Sie das Feld Haptischer Arbeitsbereich und im fügen Sie im Inspektionsbereich (Haptischer Arbeitsbereich)WorkspaceScaleController.active = false zu OnButtonUp() Ereignis und (Haptischer Arbeitsbereich)WorkspaceScaleController.active = true zu OnButtonDown().

Arbeitsbereich Skalierung Ereignisse behandeln

Ergebnis

Jetzt können Sie ganz einfach in der Szene navigieren, indem Sie den Arbeitsbereich skalieren und verschieben, indem Sie die Taste Handle Taste.

Arbeitsbereich Skalierung Ereignisse behandeln

Quelldateien

Die endgültige Szene und alle zugehörigen Dateien, die in diesem Beispiel verwendet werden, können aus dem Basic Force Feedback and Workspace Control-Beispiel im Paketmanager von Unity importiert werden. Das Unity-Beispiel enthält zusätzliche Verbesserungen der Lebensqualität, die außerhalb des Rahmens dieses Tutorials lagen:

  • Eine transparente Blase zur Visualisierung der Größe des Arbeitsbereichs
  • Tastaturkurzbefehle
    • Drücken Sie M Taste bewegt nur den Arbeitsbereich
    • Drücken Sie S Taste lässt den Arbeitsbereich nur skalieren
  • Eine Benutzeroberfläche zur Anzeige der aktuellen Offset- und Skalenwerte

WorkspaceOffsetController.cs

using Haply.HardwareAPI.Unity;
using UnityEngine;

public class WorkspaceOffsetController : MonoBehaviour
{
// Movable cursor with position controlled by Haptic Thread
private Transform m_cursor;

// Saved workspace and cursor values at transformation beginning
private Vector3 m_basePosition;
private Vector3 m_cursorBasePosition;

// If true, the workspace offset is set relatively to the cursor position on each Update() loop
public bool active
{
get => m_active;
set
{
if (value)
{
m_basePosition = transform.localPosition;
m_cursorBasePosition = m_cursor.localPosition;
}
m_active = value;
}
}

private bool m_active;

private void Awake()
{
// Get the moving cursor from the HapticThread
m_cursor = GetComponent<HapticThread>().avatar;
}

private void Update()
{
if (active)
{
// Update the workspace offset relative to cursor position
transform.position = m_basePosition - Vector3.Scale(m_cursor.localPosition - m_cursorBasePosition, transform.lossyScale);
}
}
}

WorkspaceScaleController.cs

using System;
using Haply.HardwareAPI.Unity;
using UnityEngine;

public class WorkspaceScaleController : MonoBehaviour
{
// Movable cursor with rotation controlled by Handle Thread
private Transform m_cursor;

[Tooltip("Sensitivity of scaling on handle rotation")]
public float scalingFactor = 3f;
public float minimumScale = 1f;
public float maximumScale = 5f;

// Saved workspace and cursor values at transformation beginning
private float m_baseScale;
private float m_cursorBaseAngle;
private float m_cursorPreviousAngle;
private int m_rotationCount;

// If enabled the workspace will be uniformly scaled relatively to cursor roll (Z-axis rotation) on each Update() loop
public bool active
{
get => m_active;
set
{
if (value)
{
m_rotationCount = 0;
m_baseScale = transform.localScale.z;
m_cursorPreviousAngle = m_cursorBaseAngle = m_cursor.localEulerAngles.z;
}
m_active = value;
}
}
private bool m_active;

private void Awake()
{
// Get the rotating cursor from the HandleThread
m_cursor = GetComponent<HandleThread>().avatar;
}

private void Update()
{
if (active)
{
// Calculate scale relative to cursor roll on Z-axis rotation
var totalDegrees = GetTotalDegrees(m_cursor.localEulerAngles.z, m_cursorBaseAngle);
var scale = m_baseScale - totalDegrees * scalingFactor / 100f;

// Limit between minimumScale and maximumScale
scale = Mathf.Clamp(scale, minimumScale, maximumScale);

// Set cursor offset scale (same on each axis)
transform.localScale = Vector3.one * scale;
// Invert cursor scale to keep its original size
m_cursor.localScale = Vector3.one / scale;
}
}

// Return the total degrees between baseAngle and currentAngle over the 360 degrees limitation
private float GetTotalDegrees(float currentAngle, float baseAngle)
{
if (currentAngle - m_cursorPreviousAngle > 330)
m_rotationCount--;
else if (m_cursorPreviousAngle - currentAngle > 330)
m_rotationCount++;

m_cursorPreviousAngle = currentAngle;

return 360f * m_rotationCount + (currentAngle - baseAngle);
}
}