Solver — MRTK3
Solver sind Komponenten, welche die Berechnung der Position und Ausrichtung eines Objekts gemäß einem vordefinierten Algorithmus erleichtern. Beispiel: Platzieren eines Objekts auf der Oberfläche, mit welcher sich der Anvisieren-Raycast des Benutzers schneidet.
Das Solver-System definiert bestimmungsgemäß eine Abfolge von Vorgängen für diese Transformationsberechnungen, da es keine zuverlässige Möglichkeit gibt, Unity die Aktualisierungsreihenfolge für Komponenten anzugeben.
Solver bieten eine Reihe von Verhaltensweisen zum Anfügen von Objekten an andere Objekte oder Systeme. Ein weiteres Beispiel wäre ein folgendes („tag-along“) Objekt, das basierend auf der Kamera vor dem Benutzer schwebt. Ein Solver könnte auch an einen Controller und ein Objekt angefügt werden, damit das Objekt dem Controller folgt. Alle Solver können sicher gestapelt werden--z. B. ein Folgeverhalten plus Oberflächenmagnetismus plus Momentum.
Verwendung
Das Solver-System besteht aus drei Kategorien von Skripts:
Solver
: Die abstrakte Basisklasse, von der alle Solver abgeleitet werden. Sie bietet Zustandsverfolgung, Glättungsparameter und Implementierung, automatische Solver-Systemintegration und Aktualisierungsreihenfolge.SolverHandler
: Legt das zu verfolgende Bezugsobjekt fest (z. B. die Hauptkameratransformation, Handstrahl usw.), verarbeitet das Sammeln von Solver-Komponenten und führt deren Aktualisierung in der richtigen Reihenfolge aus.
Die dritte Kategorie ist der Solver selbst. Die folgenden Solver stellen die Bausteine für das grundlegende Verhalten bereit:
Orbital
: Wird an einer angegebenen Position und mit einem Offset vom Bezugszobjekt verankert.ConstantViewSize
: Wird so skaliert, dass eine konstante Größe relativ zur Ansicht des Bezugsobjekts beibehalten wird.RadialView
: Hält das Objekt innerhalb eines Sichtkegels, der vom Bezugsobjekt geworfen wird.Follow
: Hält das Objekt innerhalb einer Gruppe benutzerseitig definierter Grenzen vom Bezugsobjekt.InBetween
: Hält ein Objekt zwischen zwei verfolgten Objekten.SurfaceMagnetism
: Wirft Strahlen auf Oberflächen in der Welt und richtet das Objekt an dieser Oberfläche aus.DirectionalIndicator
: Bestimmt die Position und Ausrichtung eines Objekts als Richtungsindikator. Ab dem Bezugspunkt des vom SolverHandler verfolgten Ziels richtet sich dieser Indikator auf das angegebene DirectionalTarget aus.Momentum
: Wendet Beschleunigung/Geschwindigkeit/Reibung an, um Momentum und Elastizität für ein Objekt zu simulieren, das von anderen Solvern/Komponenten bewegt wird.HandConstraint
: Schränkt das Objekt auf die Verfolgung von Händen in einem Bereich ein, in dem sich das GameObject nicht mit den Händen überschneidet. Nützlich für auf Hände eingeschränkte interaktive Inhalte wie Menüs usw. Dieser Solver ist für die Verwendung mitXRNode
vorgesehen.HandConstraintPalmUp
: Wird von HandConstraint abgeleitet, umfasst aber Logik zum Testen vor der Aktivierung, ob die Handfläche dem Benutzer zugewandt ist. Dieser Solver funktioniert nur mitXRNode
-Controllern und verhält sich genau wie seine Basisklasse mit anderen Controllern.Overlap
: Überlappt das nachverfolgte Objekt.
Zum Verwenden des Solver-Systems fügen Sie einem GameObject einfach eine der oben aufgeführten Komponenten hinzu. Da alle Solver einen SolverHandler
erfordern, wird einer automatisch von Unity erstellt.
Hinweis
Beispiele für die Verwendung des Solver-Systems finden Sie in der Datei SolverExamples.scene.
Ändern des Verfolgungsbezugs
Die Eigenschaft Tracked Target Type (Typ des verfolgten Ziels) der SolverHandler
-Komponente definiert den Bezugspunkt, den alle Solver verwenden, um ihre Algorithmen zu berechnen. Beispielsweise führt ein Werttyp von Head
mit einer einfachen SurfaceMagnetism
-Komponente zu einem Raycast vom Kopf des Benutzers aus und in dessen Anvisierrichtung, um aufzulösen, welche Oberfläche getroffen wird. Potenzielle Werte für die TrackedTargetType
-Eigenschaft sind:
- *Head (Kopf): Bezugspunkt ist die Transformation der Hauptkamera.
- ControllerRay: Bezugspunkt ist die
LinePointer
Transformation auf einem Controller (d. a. Zeigerurur auf einem Bewegungscontroller oder Handcontroller), der in Richtung des Linienstrahls zeigt- Verwenden Sie die
TrackedHandedness
-Eigenschaft, um die bevorzugte Händigkeit auszuwählen (d. h. Links, Rechts, beide)
- Verwenden Sie die
- HandJoint: Bezugspunkt ist die Transformation eines bestimmten Handgelenks
- Verwenden Sie die
TrackedHandedness
-Eigenschaft, um die bevorzugte Händigkeit auszuwählen (d. h. Links, Rechts, beide) - Verwenden Sie die
TrackedHandJoint
-Eigenschaft, um die zu verwendende Gelenktransformation zu bestimmen.
- Verwenden Sie die
- CustomOverride (benutzerdefinierte Außerkraftsetzung): Bezugspunkt von der zugewiesenen
TransformOverride
aus.
Hinweis
Für beide Typen, also ControllerRay und HandJoint, versucht der Solver-Handler zuerst, die linke Controller-/Handtransformation bereitzustellen, und dann die rechte, wenn die erste nicht verfügbar ist, oder wenn die TrackedHandedness
-Eigenschaft etwas anderes angibt.
Wichtig
Die meisten Solver verwenden den Vorwärtsvektor des verfolgten Transformationsziels, das vom SolverHandler
bereitgestellt wird. Bei Verwendung des Typs Handgelenk für ein verfolgtes Ziel kann es sein, dass der Vorwärtsvektor des Hand(flächen)gelenks durch die Finger und nicht durch die Handfläche zeigt. Dies hängt von der Plattform ab, die die Handgelenksdaten liefert. Für die Eingabesimulation und Windows Mixed Reality zeigt der Aufwärtsvektor durch die Handfläche nach oben (in anderen Worten heißt das, der grüner Vektor bedeutet aufwärts, der blaue Vektor vorwärts).
Um dies zu umgehen, aktualisieren Sie die Eigenschaft Additional Rotation (Zusätzliche Drehung) des SolverHandler
auf <90, 0, 0>. Dadurch wird sichergestellt, dass der an die Solver übergebene Vorwärtsvektor durch die Handfläche und nach außen, von der Hand weg zeigt.
Alternativ können Sie den Typ Controller Ray (Controllerstrahl) für ein verfolgtes Ziel verwenden, um ein ähnliches Verhalten für das Zeigen mit den Händen zu erhalten.
Verketten von Solvern
Es ist möglich, dem selben GameObject mehrere Solver
-Komponenten hinzuzufügen und so deren Algorithmen zu verketten. Die SolverHandler
-Komponenten übernehmen die Aktualisierung aller Solver auf desselben GameObject. Standardmäßig ruft der SolverHandler
beim Starten GetComponents<Solver>()
auf, wodurch die Solver in der Reihenfolge zurückgegeben werden, in der sie im Inspektor vorkommen.
Darüber hinaus weist das Festlegen der Eigenschaft Updated Linked Transform (Aktualisierte verknüpfte Transformation) auf „true“ den Solver
an, seine berechnete Position, Ausrichtung und Skalierung in einer Zwischenvariablen zu speichern, auf die alle Solver zugreifen können (d. h. GoalPosition
). „false“ gibt an, dass der Solver
die Transformation des GameObject direkt aktualisiert. Durch das Speichern der Transformationseigenschaften an einem Zwischenspeicherort können andere Solver ihre Berechnungen ab der Zwischenvariablen ausführen. Dies liegt daran, dass Unity nicht zulässt, dass Aktualisierungen von „gameObject.transform“ innerhalb desselben Frames gestapelt werden.
Hinweis
Entwickler können die Ausführungsreihenfolge von Solvern ändern, indem sie die SolverHandler.Solvers
-Eigenschaft direkt festlegen.
Erstellen eines neuen Solvers
Alle Solver müssen von der abstrakten Basisklasse Solver
erben. Die primären Anforderungen einer Solver-Erweiterung umfassen das Außerkraftsetzen der SolverUpdate
-Methode. Bei dieser Methode sollten Entwickler die geerbten Eigenschaften GoalPosition
, GoalRotation
und GoalScale
auf die gewünschten Werte aktualisieren. Darüber hinaus ist es nützlich, SolverHandler.TransformTarget
als Bezugsframe zu nutzen, der vom Consumer gewünscht wird.
Der unten angegebene Code enthält ein Beispiel für eine neue Solver-Komponente namens InFront
, die das angefügte Objekt 2 m vor dem SolverHandler.TransformTarget
platziert. Der Consumer legt SolverHandler.TrackedTargetType
als Head
fest, dann ist SolverHandler.TransformTarget
die Kameratransformation, weshalb dieser Solver das angefügte GameObject in jedem Frame 2 m vor dem Anvisieren des Benutzers platziert.
/// <summary>
/// InFront solver positions an object 2m in front of the tracked transform target
/// </summary>
public class InFront : Solver
{
...
public override void SolverUpdate()
{
if (SolverHandler != null && SolverHandler.TransformTarget != null)
{
var target = SolverHandler.TransformTarget;
GoalPosition = target.position + target.forward * 2.0f;
}
}
}
Richtlinien für die Solver-Implementierung
Allgemeine Solver-Eigenschaften
Jede Solver-Komponente verfügt über einen Kernsatz identischer Eigenschaften, die das Kernverhalten des Solvers steuern.
Wenn Smoothing (Glättung) aktiviert ist, aktualisiert der Solver die Transformation des GameObject im Laufe der Zeit schrittweise auf die berechneten Werte. Die Geschwindigkeit dieser Änderung wird durch die Eigenschaft LerpTime der jeweiligen Transformationskomponente bestimmt. Ein höherer MoveLerpTime-Wert führt beispielsweise zu langsameren Inkrementen bei der Bewegung zwischen Frames.
Wenn MaintainScale aktiviert ist, verwendet der Solver die lokale Standardskalierung des GameObject.
Orbitale
Die Orbital
-Klasse ist eine Komponente mit Folgeverhalten, die sich wie Planeten in einem Sonnensystem verhält. Dieser Solver stellt sicher, dass das angefügte GameObject die verfolgte Transformation umkreist. Wenn also der Tracked Target Type (Typ des verfolgten Ziels) des SolverHandler
auf Head
festgelegt ist, umkreist das GameObject den Kopf des Benutzers mit einem festen Offset.
Entwickler können diesen festen Offset ändern, um Menüs oder andere Szenenkomponenten auf Augen- oder Hüfthöhe usw. um einen Benutzer herum zu halten. Dies erfolgt durch Ändern der Eigenschaften Local Offset (Lokaler Offset) und World Offset (Weltoffset). Die Eigenschaft Orientation Type (Ausrichtungstyp) bestimmt bei auf das Objekt angewendeter Drehung, ob es seine ursprüngliche Drehung beibehalten oder immer der Kamera oder dem Gesicht zugewandt sein soll, egal welche Transformation seine Position bestimmt.
RadialView
RadialView
ist eine weitere Komponente mit Folgeverhalten, die einen bestimmten Teil eines GameObject im Frustum des Sichtfelds des Benutzers hält.
Die Min & Max View Degrees -Eigenschaften bestimmen, wie viel von einem Teil des GameObject immer angezeigt werden muss.
Die Min & Max Distance-Eigenschaften bestimmen, wie weit das GameObject vom Benutzer aufbewahrt werden soll. Wenn Sie beispielsweise mit einer Min Distance (Mindestabstand) von 1 m auf das GameObject zugehen, wird das GameObject weggestoßen, um sicherzustellen, dass es dem Benutzer nie näher als 1 m kommt.
Im Allgemeinen wird die RadialView
in Verbindung mit dem auf Head
festgelegten Tracked Target Type (Typ des verfolgten Ziels) verwendet, damit die Komponente dem Anvisieren des Benutzers folgt. Diese Komponente kann jedoch so funktionieren, dass sie immer im "Sichtfeld" jedes Tracked Target Type (Typ des verfolgten Ziels) gehalten wird.
Follow
Die Follow
-Klasse positioniert ein Element vor dem verfolgten Ziel, relativ zu seiner lokalen Vorwärtsachse. Das Element kann lose eingeschränkt werden (auch als „tag-along“ bezeichnet), sodass es erst folgt, wenn das nachverfolgte Ziel die benutzerdefinierten Grenzen überschreitet.
Es funktioniert ähnlich wie der RadialView-Solver mit zusätzlichen Steuerelementen zum Verwalten von Graden der maximalen horizontalen und vertikalen Ansicht und Mechanismen zum Ändern der Ausrichtung des Objekts.
InBetween
Die InBetween
-Klasse hält das angefügte GameObject zwischen zwei Transformationen. Der eigene SolverHandler
Tracked Target Type und die Second Tracked Target Type-Eigenschaft der InBetween
Komponente definieren diese beiden Transformationsendpunkte. Im Allgemeinen werden beide Typen auf CustomOverride
und die resultierenden SolverHandler.TransformOverride
- und InBetween.SecondTransformOverride
-Werte auf die zwei verfolgten Endpunkte festgelegt.
Zur Laufzeit erstellt die InBetween
-Komponente eine weitere SolverHandler
-Komponente, die auf den Eigenschaften Second Tracked Target Type (Zweiter Typ des verfolgten Ziels) und Second Transform Override (Zweite Transformationsaußerkraftsetzung) basiert.
Der PartwayOffset
definiert, wo entlang der Linie zwischen zwei Transformationen das Objekt platziert wird, wobei 0,5 auf halber Strecke, 1,0 bei der ersten Transformation und 0,0 bei der zweiten Transformation ist.
SurfaceMagnetism
SurfaceMagnetism
funktioniert durch Ausführen eines Raycasts gegen eine festgelegte LayerMask von Oberflächen und Platzieren des GameObjects an diesem Kontaktpunkt.
Der Surface Normal Offset (Offset zur Oberflächennormale) platziert das GameObject in einem festgelegten Abstand in Metern von der Oberfläche in Richtung der Normalen am Punkt des Auftreffens auf der Oberfläche.
Umgekehrt platziert der Surface Ray Offset (Offset zum Oberflächenstrahl) das GameObject in einem festgelegten Abstand in Metern von der Oberfläche weg, aber in entgegengesetzter Richtung des ausgeführten Raycasts. Wenn der Raycast also das Anvisieren des Benutzers ist, bewegt sich das GameObject näher entlang der Linie zwischen dem Auftreffpunkt auf der Oberfläche und der Kamera.
Der Orientation Mode (Ausrichtungsmodus) bestimmt den Typ der Drehung, die in Bezug auf die Normale auf der Oberfläche angewendet werden soll.
- None (Keine): Keine Drehung angewendet.
- TrackedTarget (Verfolgtes Ziel): Das Objekt wird der verfolgten Transformation, die den Raycast steuert, zugewandt.
- SurfaceNormal (Oberflächennormale): Das Objekt wird auf Grundlage der Normalen am Punkt des Auftreffens auf der Oberfläche ausgerichtet.
- Blended (Gemischt): Das Objekt wird basierend auf der Normalen am Punkt des Auftreffens auf der Oberfläche UND der verfolgten Transformation zugewandt ausgerichtet.
Um zu erzwingen, dass das zugeordnete GameObject in jedem anderen Modus als None vertikal bleibt, aktivieren Sie Keep Orientation Vertical (Ausrichtung vertikal halten).
Hinweis
Verwenden Sie die Eigenschaft Orientation Blend (Gemischte Ausrichtung), um das Gleichgewicht zwischen Drehfaktoren zu steuern, wenn der Orientation Mode (Ausrichtungsmodus) auf Blended (Gemischt) festgelegt ist. Beim Wert 0,0 wird die Ausrichtung vollständig vom TrackedTarget-Modus gesteuert, und beim Wert 1,0 wird die Ausrichtung vollständig von SurfaceNormal gesteuert.
Überlappung
Dies Overlap
ist ein einfacher Solver, der die Transformation des Objekts an derselben Position und Drehung wie das SolverHandler's
Transformationsziel behält.
Bestimmen, welche Oberflächen erreicht werden können
Beim Hinzufügen einer SurfaceMagnetism
-Komponente zu einem GameObject ist es wichtig, die Ebene des GameObject und seine untergeordneten Elemente zu berücksichtigen, sofern diese Collider aufweisen. Die Komponente funktioniert so, dass sie verschiedene Raycasts ausführt, um zu bestimmen, an welcher Oberfläche sie sich selbst „magnetisch anheften“ soll. Angenommen, das GameObject des Solvers weist einen Collider auf einer der in der Eigenschaft MagneticSurfaces
von SurfaceMagnetism
aufgelisteten Ebenen auf. In diesem Fall trifft der Raycast wahrscheinlich auf sich selbst, was dazu führt, dass das GameObject an seinen eigenen Kollisionspunkt angefügt wird. Dieses seltsame Verhalten kann vermieden werden, indem das Haupt-GameObject und alle seine untergeordneten Elemente auf die Ebene Ignore Ray cast (Raycast ignorieren) festgelegt werden oder das MagneticSurfaces
-LayerMask-Array entsprechend geändert wird.
Umgekehrt wird ein SurfaceMagnetism
GameObject nicht mit Oberflächen auf einer Ebene kollidieren, die nicht in der MagneticSurfaces
-Eigenschaft aufgeführt ist. Wir raten dazu, dass Sie alle gewünschten Oberflächen auf einer dedizierten Ebene platzieren (d. h. Oberflächen) und die MagneticSurfaces
-Eigenschaft auf eben diese Ebene festlegen. Die Verwendung von default (Standard) oder everything (alles) kann dazu führen, dass Benutzeroberflächenkomponenten oder Cursor zum Solver beitragen.
Schließlich werden Oberflächen, die weiter als die Einstellung der Eigenschaft MaxRaycastDistance
entfernt liegen, von den SurfaceMagnetism
-Raycasts ignoriert.
DirectionalIndicator
Die DirectionalIndicator
-Klasse ist eine Komponente mit Folgeverhalten, die sich selbst an der Richtung des gewünschten Punkts im Raum orientiert. Wird am häufigsten verwendet, wenn der Typ des verfolgten Ziels des SolverHandler
auf Head
festgelegt ist. Auf diese Weise weist eine UX-Komponente einen Benutzer mit dem DirectionalIndicator
-Solver an, auf den gewünschten Punkt im Raum zu sehen. Dieser Punkt wird durch die Eigenschaft Richtungsweisendes Ziel bestimmt.
Wenn das gerichtete Ziel für den Benutzer sichtbar ist oder egal welcher Bezugsframe im SolverHandler
festgelegt ist, deaktiviert dieser Solver alle darunter liegenden Renderer
-Komponenten. Wenn es nicht sichtbar ist, wird alles für den Indikator aktiviert.
Der Indikator wird immer kleiner, je näher der Benutzer dem Erfassen des Directional Target in seinem Sichtfeld (FOV) kommt.
Min Indicator Scale (Minimale Indikatorskala): Die minimale Skalierung für das Indikatorobjekt.
Max Indicator Scale (Maximale Indikatorskala): Die maximale Skalierung für das Indikatorobjekt.
Skalierungsfaktor für Sichtbarkeit - Multiplikator zum Erhöhen oder Verringern des Sichtfelds, der bestimmt, ob der Punkt des Directional Target sichtbar ist oder nicht
View Offset (Sichtoffset): Vom Blickpunkt des Bezugsrahmens (d. h. möglicherweise Kamera) und in Indikatorrichtung definiert diese Eigenschaft, wie weit sich das Objekt von der Mitte des Viewports entfernt befindet.
Directional Indicator-Beispielszene (Richtungsindikator) (Assets/MRTK/Examples/Demos/Solvers/Scenes/DirectionalIndicatorSolverExample.unity)
Handmenü mit HandConstraint und HandConstraintPalmUp
Das HandConstraint
-Verhalten bietet einen Solver, der das nachverfolgte Objekt auf einen Bereich beschränkt, der für auf die Hände eingeschränkte Inhalte (z. B. Handbenutzeroberfläche, Menüs usw.) sicher ist. Sichere Regionen gelten als Bereiche, die sich nicht mit der Hand überschneiden. Eine abgeleitete Klasse von HandConstraint
namens HandConstraintPalmUp
ist ebenfalls enthalten, um ein allgemeines Verhalten der Aktivierung des vom Solver verfolgten Objekts zu veranschaulichen, wenn die Handfläche dem Benutzer zugewandt ist.
In der Dokumentation zum Handmenü finden Sie Beispiele für die Verwendung des Handeinschränkungs-Solvers zum Erstellen von Handmenüs.