MR-Eingabe 213: Motion-Controller

Hinweis

Die Tutorials der Mixed Reality Academy wurden im Hinblick auf HoloLens (1. Gen.) und immersive Mixed Reality-Headsets entworfen. Daher halten wir es für wichtig, diese Tutorials für Entwickler verfügbar zu halten, die noch nach Anleitung beim Entwickeln für diese Geräte suchen. Diese Tutorials werden nicht mit den neuesten Toolsets oder Interaktionen aktualisiert, die für HoloLens 2 verwendet werden. Sie werden gewartet, um weiterhin auf den unterstützten Geräten zu funktionieren. Es wurde eine neue Reihe von Tutorials für HoloLens 2 veröffentlicht.

Motion Controller in der Mixed Reality-Welt fügen eine weitere Ebene der Interaktivität hinzu. Mit Bewegungscontrollern können wir direkt mit Objekten auf natürlichere Weise interagieren, ähnlich wie unsere physischen Interaktionen im realen Leben, was die Immersion und die Freude an Ihrer App-Erfahrung erhöht.

In MR Input 213 untersuchen wir die Eingabeereignisse des Bewegungscontrollers, indem wir eine einfache raumbezogene Malerfahrung erstellen. Mit dieser App können Benutzer im dreidimensionalen Raum mit verschiedenen Pinsel- und Farbtypen malen.

Themen, die in diesem Tutorial behandelt werden

MixedReality213 Topic1 MixedReality213 Topic2 MixedReality213 Topic3
Controllervisualisierung Controllereingabeereignisse Benutzerdefinierter Controller und Benutzeroberfläche
Erfahren Sie, wie Sie Motion Controller-Modelle im Spielmodus und der Runtime von Unity rendern. Grundlegendes zu verschiedenen Arten von Schaltflächenereignissen und deren Anwendungen. Erfahren Sie, wie Sie Ui-Elemente über den Controller überlagern oder vollständig anpassen.

Geräteunterstützung

Kurs HoloLens Immersive Headsets
MR-Eingabe 213: Motion-Controller ✔️

Vorbereitung

Voraussetzungen

Sehen Sie sich die Installationsprüfliste für immersive Headsets auf dieser Seite an.

Projektdateien

  • Laden Sie die für das Projekt erforderlichen Dateien herunter, und extrahieren Sie die Dateien auf den Desktop.

Hinweis

Wenn Sie den Quellcode vor dem Herunterladen durchsehen möchten, ist er auf GitHub verfügbar.

Unity-Einrichtung

Ziele

  • Optimieren von Unity für Windows Mixed Reality Entwicklung
  • Einrichten Mixed Reality Kamera
  • Umgebung einrichten

Anweisungen

  • Starten Sie Unity.

  • Klicken Sie auf Öffnen.

  • Navigieren Sie zu Ihrem Desktop, und suchen Sie den Ordner MixedReality213-master, den Sie zuvor nicht archiviert haben.

  • Klicken Sie auf Ordner auswählen.

  • Sobald Unity das Laden der Projektdateien abgeschlossen hat, können Sie den Unity-Editor sehen.

  • Wählen Sie in Unity Dateibuildeinstellungen >aus.

    MR213_BuildSettings

  • Wählen Sie in der Liste PlattformUniverselle Windows-Plattform aus, und klicken Sie auf die Schaltfläche Plattform wechseln.

  • Festlegen des Zielgeräts auf "Beliebiges Gerät"

  • Legen Sie Buildtyp auf D3D fest

  • Festlegen des SDK auf Zuletzt installiert

  • Überprüfen von Unity C#-Projekten

    • Dadurch können Sie Skriptdateien im Visual Studio-Projekt ändern, ohne das Unity-Projekt neu zu erstellen.
  • Klicken Sie auf Playereinstellungen.

  • Scrollen Sie im Bereich Inspektor nach unten.

  • Aktivieren Sie in den XR-Einstellungen Die Option Virtual Reality wird unterstützt.

  • Wählen Sie unter Virtual Reality SDKs die Option Windows Mixed Reality

    MR213_XRSettings

  • Schließen Sie das Fenster Buildeinstellungen .

Projektstruktur

In diesem Tutorial wird Mixed Reality Toolkit - Unity verwendet. Die Releases finden Sie auf dieser Seite.

ProjectStructure

Abgeschlossene Szenen als Referenz

  • Im Ordner Szenen finden Sie zwei abgeschlossene Unity-Szenen.
    • MixedReality213: Abgeschlossene Szene mit einem einzelnen Pinsel
    • MixedReality213Advanced: Abgeschlossene Szene für erweitertes Design mit mehreren Pinsel

Einrichtung einer neuen Szene für das Tutorial

  • Klicken Sie in Unity auf Neue Dateiszene>.

  • Hauptkamera und Richtungslicht löschen

  • Suchen Sie im Bereich Projekt die folgenden Prefabs, und ziehen Sie sie in den Hierarchiebereich :

    • Assets/HoloToolkit/Input/Prefabs/MixedRealityCamera
    • Assets/AppPrefabs/Environment

    Kamera und Umgebung

  • Es gibt zwei Kamera-Prefabs in Mixed Reality Toolkit:

    • MixedRealityCamera.prefab: Nur Kamera
    • MixedRealityCameraParent.prefab: Kamera + Teleportation + Begrenzung
    • In diesem Tutorial verwenden wir MixedRealityCamera ohne Teleportationsfunktion. Aus diesem Grund haben wir ein einfaches Umgebungs-Prefab hinzugefügt, das einen einfachen Boden enthält, damit sich der Benutzer geerdet fühlt.
    • Weitere Informationen zur Teleportation mit MixedRealityCameraParent finden Sie unter Erweitertes Design – Teleportation und Fortbewegung

Skybox-Einrichtung

  • Klicken Sie auf Fensterbeleuchtungseinstellungen >>

  • Klicken Sie rechts im Feld Skybox Material auf den Kreis.

  • Geben Sie "grau" ein, und wählen Sie SkyboxGray (Assets/AppPrefabs/Support/Materials/SkyboxGray.mat) aus.

    Festlegen von Skybox

  • Aktivieren Sie die Skybox-Option , um den zugewiesenen grauen Farbverlauf in der Skybox anzuzeigen.

    Option

  • Die Szene mit MixedRealityCamera, Environment und grauer Skybox sieht wie folgt aus.

    MixedReality213-Umgebung

  • Klicken Sie auf Datei > "Szene speichern unter".

  • Speichern Sie Ihre Szene im Ordner "Szenen" mit einem beliebigen Namen.

Kapitel 1: Controllervisualisierung

Ziele

  • Erfahren Sie, wie Sie Motion Controller-Modelle im Unity-Spielmodus und zur Laufzeit rendern.

Windows Mixed Reality stellt ein animiertes Controllermodell für die Controllervisualisierung bereit. Es gibt mehrere Ansätze, die Sie für die Controllervisualisierung in Ihrer App verwenden können:

  • Standard: Verwenden des Standardcontrollers ohne Änderung
  • Hybrid: Verwenden des Standardcontrollers, aber Anpassen einiger elemente oder überlagernder UI-Komponenten
  • Ersatz : Verwenden Ihres eigenen benutzerdefinierten 3D-Modells für den Controller

In diesem Kapitel erfahren Sie mehr über die Beispiele für diese Controlleranpassungen.

Anweisungen

  • Geben Sie im Bereich Projekt den Suchbegriff MotionControllers in das Suchfeld ein. Sie finden es auch unter Assets/HoloToolkit/Input/Prefabs/.
  • Ziehen Sie das MotionControllers-Prefab in den Hierarchiebereich .
  • Klicken Sie im Hierarchiebereich auf das Prefab motionControllers.

MotionControllers Prefab

MotionControllers prefab verfügt über ein MotionControllerVisualizer-Skript , das die Slots für alternative Controllermodelle bereitstellt. Wenn Sie Ihre eigenen benutzerdefinierten 3D-Modelle wie eine Hand oder ein Schwert zuweisen und "Always Use Alternate Left/Right Model" aktivieren, werden diese anstelle des Standardmodells angezeigt. Wir verwenden diesen Slot in Kapitel 4, um das Controllermodell durch einen Pinsel zu ersetzen.

MR213_ControllerVisualizer

Anweisungen

  • Doppelklicken Sie im Bereich Inspector auf MotionControllerVisualizer-Skript , um den Code in Visual Studio anzuzeigen.

MotionControllerVisualizer-Skript

Mit den Klassen MotionControllerVisualizer und MotionControllerInfo können Sie auf die Standardcontrollermodelle zugreifen & ändern. MotionControllerVisualizer abonniert das InteractionSourceDetected-Ereignis von Unity und instanziiert Controllermodelle automatisch, wenn sie gefunden werden.

protected override void Awake()
{
    ...
    InteractionManager.InteractionSourceDetected += InteractionManager_InteractionSourceDetected;
    InteractionManager.InteractionSourceLost += InteractionManager_InteractionSourceLost;
    ...
}

Die Controllermodelle werden nach der glTF-Spezifikation geliefert. Dieses Format wurde erstellt, um ein allgemeines Format bereitzustellen und gleichzeitig den Prozess hinter dem Senden und Entpacken von 3D-Ressourcen zu verbessern. In diesem Fall müssen wir die Controllermodelle zur Laufzeit abrufen und laden, da wir die Benutzerfreundlichkeit so nahtlos wie möglich gestalten möchten, und es ist nicht garantiert, welche Version der Bewegungscontroller der Benutzer möglicherweise verwendet. In diesem Kurs wird über das Mixed Reality Toolkit eine Version des UnityGLTF-Projekts der Khronos-Gruppe verwendet.

Sobald der Controller bereitgestellt wurde, können Skripts MotionControllerInfo verwenden, um die Transformationen für bestimmte Controllerelemente zu finden, damit sie sich richtig positionieren können.

In einem späteren Kapitel erfahren Sie, wie Sie diese Skripts verwenden, um Benutzeroberflächenelemente an die Controller anzufügen.

In einigen Skripts finden Sie Codeblöcke mit #if ! UNITY_EDITOR oder UNITY_WSA. Diese Codeblöcke werden nur auf der UWP-Runtime ausgeführt, wenn Sie die Bereitstellung in Windows durchführen. Dies liegt daran, dass sich die vom Unity-Editor und der UWP-App-Runtime verwendeten APIs unterscheiden.

  • Speichern Sie die Szene, und klicken Sie auf die Wiedergabeschaltfläche .

Sie können die Szene mit Bewegungscontrollern in Ihrem Headset sehen. Sie können detaillierte Animationen für Schaltflächenklicks, Daumenstickbewegungen und Touchpad-Touchmarkierung sehen.

MR213_Controller Visualisierungsstandard

Kapitel 2: Anfügen von UI-Elementen an den Controller

Ziele

  • Erfahren Sie mehr über die Elemente der Bewegungssteuerungen
  • Erfahren Sie, wie Sie Objekte an bestimmte Teile der Controller anfügen.

In diesem Kapitel erfahren Sie, wie Sie dem Controller Benutzeroberflächenelemente hinzufügen, auf die der Benutzer jederzeit problemlos zugreifen und diese bearbeiten kann. Außerdem erfahren Sie, wie Sie mithilfe der Touchpadeingabe eine einfache Benutzeroberfläche für die Farbauswahl hinzufügen.

Anweisungen

  • Suchen Sie im Projektbereich nach motionControllerInfo-Skript .
  • Doppelklicken Sie im Suchergebnis auf das MotionControllerInfo-Skript , um den Code in Visual Studio anzuzeigen.

MotionControllerInfo-Skript

Der erste Schritt besteht darin, das Element des Controllers auszuwählen, an das die Benutzeroberfläche angefügt werden soll. Diese Elemente werden in ControllerElementEnum in MotionControllerInfo.cs definiert.

MR213 MotionControllerElements

  • Home
  • Menü
  • Begreifen
  • Thumbstick
  • Auswählen
  • Touchpad
  • Pointing Pose : Dieses Element stellt die Spitze des Controllers dar, der nach vorne zeigt.

Anweisungen

  • Suchen Sie im Projektbereichnach AttachToController-Skript .
  • Doppelklicken Sie im Suchergebnis auf AttachToController-Skript , um den Code in Visual Studio anzuzeigen.

AttachToController-Skript

Das AttachToController-Skript bietet eine einfache Möglichkeit zum Anfügen beliebiger Objekte an eine angegebene Controllerhand und ein bestimmtes Element.

In AttachElementToController():

  • Händigkeitsprüfung mit MotionControllerInfo.Handedness
  • Abrufen eines bestimmten Elements des Controllers mithilfe von MotionControllerInfo.TryGetElement()
  • Nachdem Sie die Transformation des Elements aus dem Controllermodell abgerufen haben, zuordnen Sie das Objekt darunter, und legen Sie die lokale Position des Objekts & Drehung auf 0 fest.
public MotionControllerInfo.ControllerElementEnum Element { get { return element; } }

private void AttachElementToController(MotionControllerInfo newController)
{
     if (!IsAttached && newController.Handedness == handedness)
     {
          if (!newController.TryGetElement(element, out elementTransform))
          {
               Debug.LogError("Unable to find element of type " + element + " under controller " + newController.ControllerParent.name + "; not attaching.");
               return;
          }

          controller = newController;

          SetChildrenActive(true);

          // Parent ourselves under the element and set our offsets
          transform.parent = elementTransform;
          transform.localPosition = positionOffset;
          transform.localEulerAngles = rotationOffset;
          if (setScaleOnAttach)
          {
               transform.localScale = scale;
          }

          // Announce that we're attached
          OnAttachToController();
          IsAttached = true;
     }
}

Die einfachste Möglichkeit zum Verwenden des AttachToController-Skripts besteht darin, davon zu erben, wie wir es im Fall von ColorPickerWheel getan haben. Überschreiben Sie einfach die Funktionen OnAttachToController und OnDetachFromController , um Die Einrichtung/Aufschlüsselung durchzuführen, wenn der Controller erkannt/getrennt wird.

Anweisungen

  • Geben Sie im Projektbereich das Suchfeld ColorPickerWheel ein. Sie finden sie auch unter Assets/AppPrefabs/.
  • Ziehen Sie das ColorPickerWheel-Prefab in den Hierarchiebereich .
  • Klicken Sie im Hierarchiebereich auf das Prefab ColorPickerWheel.
  • Doppelklicken Sie im Bereich Inspektor auf ColorPickerWheel Script, um den Code in Visual Studio anzuzeigen.

ColorPickerWheel Prefab

ColorPickerWheel-Skript

Da ColorPickerWheelAttachToController erbt, werden im Bereich InspectorHändigkeit und Element angezeigt. Wir fügen die Benutzeroberfläche an das Touchpad-Element auf dem linken Controller an.

ColorPickerWheel-Skript

ColorPickerWheel setzt onAttachToController und OnDetachFromController außer Kraft, um das Eingabeereignis zu abonnieren, das im nächsten Kapitel für die Farbauswahl mit Touchpadeingabe verwendet wird.

public class ColorPickerWheel : AttachToController, IPointerTarget
{
    protected override void OnAttachToController()
    {
        // Subscribe to input now that we're parented under the controller
        InteractionManager.InteractionSourceUpdated += InteractionSourceUpdated;
    }

    protected override void OnDetachFromController()
    {
        Visible = false;

        // Unsubscribe from input now that we've detached from the controller
        InteractionManager.InteractionSourceUpdated -= InteractionSourceUpdated;
    }
    ...
}
  • Speichern Sie die Szene, und klicken Sie auf die Wiedergabeschaltfläche .

Alternative Methode zum Anfügen von Objekten an die Controller

Es wird empfohlen, dass Ihre Skripts von AttachToController erben und OnAttachToController überschreiben. Dies ist jedoch möglicherweise nicht immer möglich. Eine Alternative ist die Verwendung als eigenständige Komponente. Dies kann nützlich sein, wenn Sie ein vorhandenes Prefab an einen Controller anfügen möchten, ohne Ihre Skripts umzugestalten. Warten Sie einfach, bis IsAttached auf true festgelegt ist, bevor Sie ein Setup ausführen. Die einfachste Möglichkeit hierfür ist die Verwendung einer Coroutine für "Start".

private IEnumerator Start() {
    AttachToController attach = gameObject.GetComponent<AttachToController>();

    while (!attach.IsAttached) {
        yield return null;
    }

    // Perform setup here
}

Kapitel 3: Arbeiten mit Touchpadeingaben

Ziele

  • Informationen zum Abrufen von Touchpadeingabedatenereignissen
  • Hier erfahren Sie, wie Sie Touchpad-Achsenpositionsinformationen für Ihre App verwenden.

Anweisungen

  • Klicken Sie im Hierarchiebereich auf ColorPickerWheel
  • Doppelklicken Sie im Bereich Inspektor unter Animator auf ColorPickerWheelController.
  • Sie können sehen, dass die Registerkarte "Animator " geöffnet ist.

Ein-/Ausblenden der Benutzeroberfläche mit dem Unity-Animationscontroller

Zum Ein- und Ausblenden der ColorPickerWheel-Benutzeroberfläche mit Animation verwenden wir das Animationssystem von Unity. Legen Sie die Visible-Eigenschaft des ColorPickerWheel auf true oder false fest, um Animationstrigger anzeigen und ausblenden. Ein- und Ausblendenparameter sind im ColorPickerWheelController-Animationscontroller definiert.

Unity Animation Controller

Anweisungen

  • Wählen Sie im Bereich Hierarchiedie Option ColorPickerWheel prefab aus.
  • Doppelklicken Sie im Bereich Inspektor auf ColorPickerWheel-Skript , um den Code in Visual Studio anzuzeigen.

ColorPickerWheel-Skript

ColorPickerWheel abonniert das InteractionSourceUpdated-Ereignis von Unity, um auf Touchpadereignisse zu lauschen.

In InteractionSourceUpdated() überprüft das Skript zunächst folgendes:

  • ist tatsächlich ein Touchpadereignis (obj.state.touchpadTouched)
  • stammt vom linken Controller (obj.state.source.Händigkeit)

Wenn beide true sind, wird die Touchpadposition (obj.state) angezeigt.touchpadPosition) wird selectorPosition zugewiesen.

private void InteractionSourceUpdated(InteractionSourceUpdatedEventArgs obj)
{
    if (obj.state.source.handedness == handedness && obj.state.touchpadTouched)
    {
        Visible = true;
        selectorPosition = obj.state.touchpadPosition;
    }
}

In Update()werden basierend auf der sichtbaren Eigenschaft Animationstrigger anzeigen und ausblenden in der Animatorkomponente der Farbauswahl ausgelöst.

if (visible != visibleLastFrame)
{
    if (visible)
    {
        animator.SetTrigger("Show");
    }
    else
    {
        animator.SetTrigger("Hide");
    }
}

In Update()wird selectorPosition verwendet, um einen Strahl am Gitterkolidder des Farbrads zu werfen, wodurch eine UV-Position zurückgegeben wird. Diese Position kann dann verwendet werden, um die Pixelkoordinate und den Farbwert der Textur des Farbrads zu ermitteln. Auf diesen Wert kann über die SelectedColor-Eigenschaft für andere Skripts zugegriffen werden.

Color Picker Wheel Raycasting

...
    // Clamp selector position to a radius of 1
    Vector3 localPosition = new Vector3(selectorPosition.x * inputScale, 0.15f, selectorPosition.y * inputScale);
    if (localPosition.magnitude > 1)
    {
        localPosition = localPosition.normalized;
    }
    selectorTransform.localPosition = localPosition;

    // Raycast the wheel mesh and get its UV coordinates
    Vector3 raycastStart = selectorTransform.position + selectorTransform.up * 0.15f;
    RaycastHit hit;
    Debug.DrawLine(raycastStart, raycastStart - (selectorTransform.up * 0.25f));

    if (Physics.Raycast(raycastStart, -selectorTransform.up, out hit, 0.25f, 1 << colorWheelObject.layer, QueryTriggerInteraction.Ignore))
    {
        // Get pixel from the color wheel texture using UV coordinates
        Vector2 uv = hit.textureCoord;
        int pixelX = Mathf.FloorToInt(colorWheelTexture.width * uv.x);
        int pixelY = Mathf.FloorToInt(colorWheelTexture.height * uv.y);
        selectedColor = colorWheelTexture.GetPixel(pixelX, pixelY);
        selectedColor.a = 1f;
    }
    // Set the selector's color and blend it with white to make it visible on top of the wheel
    selectorRenderer.material.color = Color.Lerp (selectedColor, Color.white, 0.5f);
}

Kapitel 4: Überschreiben des Controllermodells

Ziele

  • Erfahren Sie, wie Sie das Controllermodell mit einem benutzerdefinierten 3D-Modell überschreiben.

MR213_BrushToolOverride

Anweisungen

  • Klicken Sie im Hierarchiebereich auf MotionControllers.
  • Klicken Sie rechts neben dem Feld Alternativer Rechtscontroller auf den Kreis.
  • Geben Sie "BrushController" ein, und wählen Sie das Prefab aus dem Ergebnis aus. Sie finden sie unter Assets/AppPrefabs/BrushController.
  • Überprüfen Sie immer alternatives rechtes Modell verwenden.

MR213_BrushToolOverrideSlot

Das BrushController-Prefab muss nicht im Hierarchiebereich enthalten sein. Um jedoch die untergeordneten Komponenten auszuchecken:

  • Geben Sie im ProjektbereichBrushController ein, und ziehen Sie das BrushController-Prefab in den Hierarchiebereich .

MR213_BrushTool_Prefab2

Sie finden die Tip-Komponente in BrushController. Wir verwenden die Transformation, um das Zeichnen von Linien zu starten/zu beenden.

  • Löschen Sie den BrushController aus dem Hierarchiebereich .
  • Speichern Sie die Szene, und klicken Sie auf die Wiedergabeschaltfläche . Sie können sehen, wie das Pinselmodell den rechten Bewegungscontroller ersetzt hat.

Kapitel 5: Malen mit Eingabe auswählen

Ziele

  • Erfahren Sie, wie Sie das Schaltflächenauswahlereignis verwenden, um eine Linienzeichnung zu starten und zu beenden.

Anweisungen

  • Suchen Sie im Projektbereichnach BrushController-Prefab.
  • Doppelklicken Sie im Bereich Inspektor auf BrushController-Skript , um den Code in Visual Studio anzuzeigen.

BrushController-Skript

BrushController abonniert die InteractionSourcePressed - und InteractionSourceReleased-Ereignisse von InteractionManager . Wenn das InteractionSourcePressed-Ereignis ausgelöst wird, wird die Draw-Eigenschaft des Pinsels auf true festgelegt. wenn das InteractionSourceReleased-Ereignis ausgelöst wird, wird die Draw-Eigenschaft des Pinsels auf false festgelegt.

private void InteractionSourcePressed(InteractionSourcePressedEventArgs obj)
{
    if (obj.state.source.handedness == InteractionSourceHandedness.Right && obj.pressType == InteractionSourcePressType.Select)
    {
        Draw = true;
    }
}

private void InteractionSourceReleased(InteractionSourceReleasedEventArgs obj)
{
    if (obj.state.source.handedness == InteractionSourceHandedness.Right && obj.pressType == InteractionSourcePressType.Select)
    {
        Draw = false;
    }
}

Während Zeichnen auf true festgelegt ist, generiert der Pinsel Punkte in einem instanziierten Unity LineRenderer. Ein Verweis auf dieses Prefab wird im Strich-Prefab-Feld des Pinsels beibehalten.

private IEnumerator DrawOverTime()
{
    // Get the position of the tip
    Vector3 lastPointPosition = tip.position;

    ...

    // Create a new brush stroke
    GameObject newStroke = Instantiate(strokePrefab);
    LineRenderer line = newStroke.GetComponent<LineRenderer>();
    newStroke.transform.position = startPosition;
    line.SetPosition(0, tip.position);
    float initialWidth = line.widthMultiplier;

    // Generate points in an instantiated Unity LineRenderer
    while (draw)
    {
        // Move the last point to the draw point position
        line.SetPosition(line.positionCount - 1, tip.position);
        line.material.color = colorPicker.SelectedColor;
        brushRenderer.material.color = colorPicker.SelectedColor;
        lastPointAddedTime = Time.unscaledTime;
        // Adjust the width between 1x and 2x width based on strength of trigger pull
        line.widthMultiplier = Mathf.Lerp(initialWidth, initialWidth * 2, width);

        if (Vector3.Distance(lastPointPosition, tip.position) > minPositionDelta || Time.unscaledTime > lastPointAddedTime + maxTimeDelta)
        {
            // Spawn a new point
            lastPointAddedTime = Time.unscaledTime;
            lastPointPosition = tip.position;
            line.positionCount += 1;
            line.SetPosition(line.positionCount - 1, lastPointPosition);
        }
        yield return null;
    }
}

Um die aktuell ausgewählte Farbe aus der Benutzeroberfläche des Farbauswahlrads zu verwenden, muss BrushController über einen Verweis auf das ColorPickerWheel-Objekt verfügen. Da das BrushController-Prefab zur Laufzeit als Ersatzcontroller instanziiert wird, müssen alle Verweise auf Objekte in der Szene zur Laufzeit festgelegt werden. In diesem Fall verwenden wir GameObject.FindObjectOfType , um das ColorPickerWheel zu suchen:

private void OnEnable()
{
    // Locate the ColorPickerWheel
    colorPicker = FindObjectOfType<ColorPickerWheel>();

    // Assign currently selected color to the brush’s material color
    brushRenderer.material.color = colorPicker.SelectedColor;
    ...
}
  • Speichern Sie die Szene, und klicken Sie auf die Wiedergabeschaltfläche . Sie können die Linien zeichnen und malen, indem Sie die Schaltfläche Auswählen auf dem rechten Controller verwenden.

Kapitel 6: Objekt-Spawning mit Eingabe auswählen

Ziele

  • Hier erfahren Sie, wie Sie Eingabeereignisse für Schaltflächen auswählen und erfassen verwenden.
  • Hier erfahren Sie, wie Sie Objekte instanziieren.

Anweisungen

  • Geben Sie im Bereich Projekt den Suchbegriff ObjectSpawner in das Suchfeld ein. Sie finden es auch unter Assets/AppPrefabs/

  • Ziehen Sie das ObjectSpawner-Prefab in den Hierarchiebereich .

  • Klicken Sie im Hierarchiebereich auf ObjectSpawner.

  • ObjectSpawner verfügt über ein Feld namens Color Source.

  • Ziehen Sie im Hierarchiebereich den ColorPickerWheel-Verweis in dieses Feld.

    Objekt-Spawner-Inspektor

  • Klicken Sie im Bereich Hierarchie auf das Prefab ObjectSpawner.

  • Doppelklicken Sie im Bereich Inspector auf ObjectSpawner Script, um den Code in Visual Studio anzuzeigen.

ObjectSpawner-Skript

Der ObjectSpawner instanziiert Kopien eines primitiven Gitters (Würfel, Kugel, Zylinder) in den Raum. Wenn ein InteractionSourcePressed erkannt wird, überprüft es die Händigkeit und ob es sich um ein InteractionSourcePressType.Grasp - oder InteractionSourcePressType.Select-Ereignis handelt.

Bei einem Grasp-Ereignis wird der Index des aktuellen Gittertyps (Kugel, Würfel, Zylinder) erhöht.

private void InteractionSourcePressed(InteractionSourcePressedEventArgs obj)
{
    // Check handedness, see if it is left controller
    if (obj.state.source.handedness == handedness)
    {
        switch (obj.pressType)
        {
            // If it is Select button event, spawn object
            case InteractionSourcePressType.Select:
                if (state == StateEnum.Idle)
                {
                    // We've pressed the grasp - enter spawning state
                    state = StateEnum.Spawning;
                    SpawnObject();
                }
                break;

            // If it is Grasp button event
            case InteractionSourcePressType.Grasp:

                // Increment the index of current mesh type (sphere, cube, cylinder)
                meshIndex++;
                if (meshIndex >= NumAvailableMeshes)
                {
                    meshIndex = 0;
                }
                break;

            default:
                break;
        }
    }
}

Bei einem Select-Ereignis wird in SpawnObject()ein neues Objekt instanziiert, nicht übergeordnet und in die Welt freigegeben.

private void SpawnObject()
{
    // Instantiate the spawned object
    GameObject newObject = Instantiate(displayObject.gameObject, spawnParent);
    // Detach the newly spawned object
    newObject.transform.parent = null;
    // Reset the scale transform to 1
    scaleParent.localScale = Vector3.one;
    // Set its material color so its material gets instantiated
    newObject.GetComponent<Renderer>().material.color = colorSource.SelectedColor;
}

Der ObjectSpawner verwendet den ColorPickerWheel , um die Farbe des Materials des Anzeigeobjekts festzulegen. Spawns-Objekte erhalten eine instance dieses Materials, damit sie ihre Farbe behalten.

  • Speichern Sie die Szene, und klicken Sie auf die Wiedergabeschaltfläche .

Sie können die Objekte mit der Schaltfläche Grasp ändern und Objekte mit der Schaltfläche Auswählen spawnen.

Erstellen und Bereitstellen einer App in Mixed Reality Portal

  • Wählen Sie in Unity Dateibuildeinstellungen >aus.
  • Klicken Sie auf Offene Szenen hinzufügen , um die aktuelle Szene zu Szenen im Build hinzuzufügen.
  • Klicken Sie auf Erstellen.
  • Erstellen Sie einen neuen Ordner mit dem Namen "App".
  • Klicken Sie mit einem Klick auf den Ordner App .
  • Klicken Sie auf Ordner auswählen.
  • Wenn Unity fertig ist, wird ein Explorer Fenster angezeigt.
  • Öffnen Sie den Ordner App .
  • Doppelklicken Sie auf YourSceneName.sln Visual Studio Projektmappendatei .
  • Ändern Sie mithilfe der oberen Symbolleiste in Visual Studio das Ziel von Debuggen in Release und von ARM in X64.
  • Klicken Sie auf den Dropdownpfeil neben der Schaltfläche Gerät, und wählen Sie Lokaler Computer aus.
  • Klicken Sie im Menü auf Debuggen –> Ohne Debuggen starten , oder drücken Sie STRG + F5.

Jetzt wird die App in Mixed Reality Portal erstellt und installiert. Sie können es über das Startmenü im Mixed Reality Portal erneut starten.

Erweitertes Design – Pinseltools mit radialem Layout

MixedReality213 Main

In diesem Kapitel erfahren Sie, wie Sie das Standardmodell des Bewegungscontrollers durch eine benutzerdefinierte Pinseltoolsammlung ersetzen. Als Referenz finden Sie die abgeschlossene Szene MixedReality213Advanced im Ordner Szenen .

Anweisungen

  • Geben Sie im Bereich Projekt den Suchbegriff BrushSelector in das Suchfeld ein. Sie finden es auch unter Assets/AppPrefabs/

  • Ziehen Sie das Prefab BrushSelector in den Hierarchiebereich .

  • Erstellen Sie für organization ein leeres GameObject namens Brushes.

  • Ziehen Sie die folgenden Prefabs aus dem Projektbereich in Pinsel

    • Assets/AppPrefabs/BrushFat
    • Assets/AppPrefabs/BrushThin
    • Assets/AppPrefabs/Radierer
    • Assets/AppPrefabs/MarkerFat
    • Assets/AppPrefabs/MarkerThin
    • Assets/AppPrefabs/Pencil

    Pinsel

  • Klicken Sie im Hierarchiebereich auf MotionControllers prefab.

  • Deaktivieren Sie im Bereich Inspektor die Option Always Use Alternate Right Model in the Motion Controller Visualizer

  • Klicken Sie im Hierarchiebereich auf BrushSelector

  • BrushSelector verfügt über ein Feld namens ColorPicker

  • Ziehen Sie im Bereich Hierarchie das ColorPickerWheel-Feld im Bereich Inspektor in das Feld ColorPicker.

    Zuweisen von ColorPickerWheel zur Pinselauswahl

  • Wählen Sie im Hierarchiebereich unter BrushSelector Prefab das Menu-Objekt aus.

  • Öffnen Sie im Bereich Inspector unter der Komponente LineObjectCollection das Dropdownmenü Objekte . Es werden 6 leere Slots angezeigt.

  • Ziehen Sie aus dem Hierarchiebereich alle Prefabs, die unter dem Brushes GameObject-Element übergeordnet sind, in beliebiger Reihenfolge in diese Slots. (Stellen Sie sicher, dass Sie die Prefabs aus der Szene ziehen, nicht die Prefabs im Projektordner.)

Pinselauswahl

BrushSelector Prefab

Da der BrushSelectorAttachToController erbt, werden im Bereich Inspektordie Optionen "Händigkeit" und "Element" angezeigt. Wir haben rechts und zeigende Pose ausgewählt, um Pinseltools mit Vorwärtsrichtung am rechten Controller anzubringen.

Der BrushSelector verwendet zwei Hilfsprogramme:

  • Ellipse: Wird verwendet, um Punkte im Raum entlang einer Ellipseform zu generieren.
  • LineObjectCollection: Verteilt Objekte mithilfe der Von jeder Line-Klasse generierten Punkte (z. B. Ellipse). Dies ist das, was wir verwenden, um unsere Pinsel entlang der Ellipse-Form zu platzieren.

In Kombination können diese Hilfsprogramme verwendet werden, um ein radiales Menü zu erstellen.

LineObjectCollection-Skript

LineObjectCollection verfügt über Steuerelemente für die Größe, Position und Drehung von Objekten, die entlang ihrer Linie verteilt sind. Dies ist nützlich, um radiale Menüs wie die Pinselauswahl zu erstellen. Um die Darstellung von Pinsel zu erstellen, die aus dem Nichts hochskaliert werden, während sie sich der ausgewählten Position in der Mitte nähern, spitzen die ObjectScale-Kurve in der Mitte und verjüngt sich an den Rändern.

BrushSelector-Skript

Im Fall von BrushSelector haben wir uns für die Verwendung der prozeduralen Animation entschieden. Zunächst werden Pinselmodelle in einer Ellipse vom LineObjectCollection-Skript verteilt. Anschließend ist jeder Pinsel dafür verantwortlich, seine Position in der Hand des Benutzers basierend auf seinem DisplayMode-Wert beizubehalten, der sich basierend auf der Auswahl ändert. Wir haben einen prozeduralen Ansatz gewählt, da die Wahrscheinlichkeit, dass Pinselpositionsübergänge unterbrochen werden, wenn der Benutzer Pinsel auswählt. Mecanim-Animationen können Unterbrechungen problemlos verarbeiten, sind aber in der Regel komplizierter als ein einfacher Lerp-Vorgang.

BrushSelector verwendet eine Kombination aus beidem. Wenn Touchpadeingaben erkannt werden, werden Pinseloptionen sichtbar und im Radialmenü hochskaliert. Nach einem Timeoutzeitraum (der angibt, dass der Benutzer eine Auswahl getroffen hat) werden die Pinseloptionen wieder herunterskaliert, sodass nur der ausgewählte Pinsel übrig bleibt.

Visualisieren von Touchpadeingaben

Auch in Fällen, in denen das Controllermodell vollständig ersetzt wurde, kann es hilfreich sein, Eingaben für die ursprünglichen Modelleingaben anzuzeigen. Dies hilft, die Aktionen des Benutzers in der Realität zu erden. Für den BrushSelector haben wir uns dafür entschieden, das Touchpad kurz sichtbar zu machen, wenn die Eingabe empfangen wird. Dazu wurde das Touchpad-Element vom Controller abgerufen, dessen Material durch ein benutzerdefiniertes Material ersetzt und dann basierend auf der letzten Touchpadeingabe ein Farbverlauf auf die Farbe dieses Materials angewendet.

protected override void OnAttachToController()
{
    // Turn off the default controller's renderers
    controller.SetRenderersVisible(false);

    // Get the touchpad and assign our custom material to it
    Transform touchpad;
    if (controller.TryGetElement(MotionControllerInfo.ControllerElementEnum.Touchpad, out touchpad))
    {
        touchpadRenderer = touchpad.GetComponentInChildren<MeshRenderer>();
        originalTouchpadMaterial = touchpadRenderer.material;
        touchpadRenderer.material = touchpadMaterial;
        touchpadRenderer.enabled = true;
    }

    // Subscribe to input now that we're parented under the controller
    InteractionManager.InteractionSourceUpdated += InteractionSourceUpdated;
}

private void Update()
{
    ...
    // Update our touchpad material
    Color glowColor = touchpadColor.Evaluate((Time.unscaledTime - touchpadTouchTime) / touchpadGlowLossTime);
    touchpadMaterial.SetColor("_EmissionColor", glowColor);
    touchpadMaterial.SetColor("_Color", glowColor);
    ...
}

Pinseltoolauswahl mit Touchpadeingabe

Wenn der Pinselauswahl die gedrückte Eingabe des Touchpads erkennt, überprüft er die Position der Eingabe, um zu bestimmen, ob sie links oder rechts war.

Strichstärke mit selectPressedAmount

Anstelle des InteractionSourcePressType.Select-Ereignisses in InteractionSourcePressed() können Sie den analogen Wert des gedrückten Betrags über selectPressedAmount abrufen. Dieser Wert kann in InteractionSourceUpdated()abgerufen werden.

private void InteractionSourceUpdated(InteractionSourceUpdatedEventArgs obj)
{
    if (obj.state.source.handedness == handedness)
    {
        if (obj.state.touchpadPressed)
        {
            // Check which side we clicked
            if (obj.state.touchpadPosition.x < 0)
            {
                currentAction = SwipeEnum.Left;
            }
            else
            {
                currentAction = SwipeEnum.Right;
            }

            // Ping the touchpad material so it gets bright
            touchpadTouchTime = Time.unscaledTime;
        }

        if (activeBrush != null)
        {
            // If the pressed amount is greater than our threshold, draw
            if (obj.state.selectPressedAmount >= selectPressedDrawThreshold)
            {
                activeBrush.Draw = true;
                activeBrush.Width = ProcessSelectPressedAmount(obj.state.selectPressedAmount);
            }
            else
            {
                // Otherwise, stop drawing
                activeBrush.Draw = false;
                selectPressedSmooth = 0f;
            }
        }
    }
}

Radiererskript

Radierer ist ein spezieller Pinseltyp, der die DrawOverTime()-Funktion des Basispinsels außer Kraft setzt. Während Draw true ist, überprüft der Radierer, ob sich seine Spitze mit vorhandenen Pinselstrichen überschneidet. Wenn dies der Fall ist, werden sie einer Warteschlange hinzugefügt, um verkleinert und gelöscht zu werden.

Erweitertes Design – Teleportation und Fortbewegung

Wenn Sie dem Benutzer erlauben möchten, sich mit Teleportation mithilfe des Thumbsticks in der Szene zu bewegen, verwenden Sie MixedRealityCameraParent anstelle von MixedRealityCamera. Außerdem müssen Sie InputManager und DefaultCursor hinzufügen. Da MixedRealityCameraParent bereits MotionControllers und Boundary als untergeordnete Komponenten enthält, sollten Sie vorhandene MotionController- und Environment-Prefabs entfernen.

Anweisungen

  • Löschen Sie im HierarchiebereichMixedRealityCamera, Environment und MotionControllers.

  • Suchen Sie im Bereich Projekt die folgenden Prefabs, und ziehen Sie sie in den Hierarchiebereich :

    • Assets/AppPrefabs/Input/Prefabs/MixedRealityCameraParent
    • Assets/AppPrefabs/Input/Prefabs/InputManager
    • Assets/AppPrefabs/Input/Prefabs/Cursor/DefaultCursor

    Mixed Reality Übergeordnete Kamera

  • Klicken Sie im Hierarchiebereich auf Eingabe-Manager.

  • Scrollen Sie im Bereich Inspektor nach unten zum Abschnitt Einfache Zeigerauswahl .

  • Ziehen Sie im HierarchiebereichDefaultCursor in das Cursorfeld .

    Zuweisen von DefaultCursor

  • Speichern Sie die Szene, und klicken Sie auf die Wiedergabeschaltfläche . Sie können den Fingerabdruck zum Drehen nach links/rechts oder teleportieren.

Das Ende

Und das ist das Ende dieses Tutorials! Sie haben Folgendes gelernt:

  • So arbeiten Sie mit Motion Controller-Modellen im Spielmodus und der Runtime von Unity.
  • Verwenden verschiedener Arten von Schaltflächenereignissen und deren Anwendungen
  • Hier erfahren Sie, wie Sie Benutzeroberflächenelemente über den Controller überlagern oder vollständig anpassen.

Sie können jetzt mit der Erstellung Ihrer eigenen immersiven Erfahrung mit Bewegungscontrollern beginnen!

Abgeschlossene Szenen

  • Klicken Sie im Projektbereich von Unity auf den Ordner Szenen .
  • Sie finden zwei Unity-Szenen MixedReality213 und MixedReality213Advanced.
    • MixedReality213: Abgeschlossene Szene mit einem einzelnen Pinsel
    • MixedReality213Advanced: Abgeschlossene Szene mit mehreren Pinsel mit dem Drücken der Schaltfläche "Select" (Beispiel)

Weitere Informationen