Решатели — MRTK3
Решатели — это компоненты, которые упрощают вычисление положения и ориентации объекта в соответствии с предопределенным алгоритмом. Пример: размещение объекта на поверхности, с которой пересекается взгляд пользователя.
Система решателей детерминированно определяет порядок операций для этих вычислений преобразования, так как другого надежного способа указать для Unity порядок обновления компонентов не существует.
Решатели предлагают ряд вариантов поведения для присоединения объектов к другим объектам или системам. Еще один пример — закрепленный объект, который парит перед пользователем (с учетом положения камеры). Решатель можно также подключить к контроллеру и объекту, чтобы закрепить объект у контроллера. Все решатели можно безопасно собрать вместе, например закрепление + поверхностный магнетизм + импульс.
Использование
Система решателей состоит из трех категорий сценариев:
Solver
: базовый абстрактный класс, от которого наследуются все решатели. Он обеспечивает отслеживание состояния, параметры сглаживания и их реализацию, автоматическую интеграцию системы решателей и порядок обновления.SolverHandler
— задает опорный объект для отслеживания (например, трансформация основной камеры, телекинез и т. д.), обеспечивает сбор компонентов решателя и выполняет их обновление в нужном порядке.
Третья категория — это сам решатель. Стандартные блоки для основного поведения предоставляют следующие решатели:
Orbital
: фиксирует определенную позицию и смещение относительно объекта, на который ссылается.ConstantViewSize
: масштабируется, чтобы поддерживать постоянный размер относительно вида с объекта, на который ссылается.RadialView
: удерживает объект в конусе просмотра объекта, на который ссылается.Follow
: удерживает объект внутри границ, заданных пользователем, объекта, на который ссылается.InBetween
: удерживает объект между двумя отслеживаемыми объектами.SurfaceMagnetism
: направляет лучи на поверхности в мире и выравнивает объект по этой поверхности.DirectionalIndicator
: определяет положение и ориентацию объекта как индикатор направления. С исходной позиции отслеживаемой цели SolverHandler этот индикатор будет ориентироваться на указанное целевое направление DirectionalTarget.Momentum
: применяет ускорение, скорость или трение для моделирования импульса и упругости для объекта, перемещаемого другими решателями или компонентами.HandConstraint
: определяет, что объект должен следовать за руками в области, в которой GameObject и руки не пересекаются. Это может быть полезно для ограниченного интерактивного содержимого, такого как меню и т. д. Этот решатель предназначен для работы сXRNode
.HandConstraintPalmUp
: является производным от HandConstraint, но включает логику для проверки того, находится ли ладонь напротив пользователя перед активацией. Этот решатель работает только с контроллерамиXRNode
. С другими типами контроллеров этот решатель будет вести себя так же, как и его базовый класс.Overlap
: перекрывает отслеживаемый объект.
Чтобы использовать систему решателей, просто добавьте один из перечисленных выше компонентов в GameObject. Так как для всех решателей требуется сценарий SolverHandler
, он будет создан Unity автоматически.
Примечание
Примеры использования системы решателей можно найти в файле SolverExamples.scene.
Изменение ссылки на отслеживание
Свойство Tracked Target Type (Тип отслеживаемой цели) компонента SolverHandler
определяет позицию, которую будут использовать все решатели для вычисления своих алгоритмов. Например, тип значения Head
с простым компонентом SurfaceMagnetism
приведет к созданию луча от головы по направлению взгляда пользователя для решения того, на какую поверхность попал луч. Возможные значения свойства TrackedTargetType
:
- *Head: исходная позиция — преобразование основной камеры
- ControllerRay: точка ссылки — это
LinePointer
преобразование на контроллере (т. е. источник указателя на контроллере движения или контроллере руки), указывающее в направлении луча линии- Используйте свойство
TrackedHandedness
для выбора руки (т. е. левая, правая, обе)
- Используйте свойство
- HandJoint: точка ссылки — преобразование определенного сустава руки
- Используйте свойство
TrackedHandedness
для выбора руки (т. е. левая, правая, обе) - Используйте свойство
TrackedHandJoint
, чтобы определить, какое преобразование сустава нужно использовать.
- Используйте свойство
- CustomOverride: исходная позиция из назначенного свойства
TransformOverride
.
Примечание
Для типов ControllerRay и HandJoint решатель сначала попытается обратиться к левому контроллеру или преобразованию руки, а затем к правому, если левая сторона недоступна или если в свойстве TrackedHandedness
не указано иное.
Важно!
Большинство решателей используют вектор, направленный вперед, для отслеживаемой цели преобразования, который предоставляется SolverHandler
. При использовании типа отслеживаемой цели Hand Joint вектор суставов ладони может указывать на пальцы, а не сквозь ладонь. Это зависит от платформы, предоставляющей данные о суставах руки. В имитации входных данных и Windows Mixed Reality вектор вверх проходит сквозь ладонь (т. е. зеленый вектор — вверх, синий вектор — вперед).
Чтобы устранить это, обновите свойство Additional Rotation (Дополнительное вращение), указав для SolverHandler
значение <90, 0, 0>. Это обеспечит направление вектора, подаваемого решателям, вперед через ладонь и обратно от руки.
Кроме того, можно использовать тип отслеживаемой цели Controller Ray, чтобы получить аналогичное поведение при указании направления с помощью рук.
Создание цепочки решателей
Можно добавить несколько компонентов Solver
в один и тот же GameObject, чтобы связать их алгоритмы. Компоненты SolverHandler
обрабатывают обновление всех решателей в одном GameObject. По умолчанию SolverHandler
вызывает GetComponents<Solver>()
при запуске, что будет возвращать решатели в том порядке, в котором они отображаются в инспекторе.
Более того, значение True для свойства преобразования Updated Linked Transform укажет Solver
сохранить его вычисленное положение, ориентацию и масштаб в промежуточную переменную, доступную всем решателям (т. е. GoalPosition
). Если значение равно False, Solver
обновляет преобразование GameObject напрямую. При сохранении свойств преобразования в промежуточном расположении другие решатели могут выполнять вычисления, начиная с промежуточной переменной. Это обусловлено тем, что Unity не допускает обновления gameObject.Transform в стеке внутри одного кадра.
Примечание
Разработчики могут изменить порядок выполнения решателей, задав свойство SolverHandler.Solvers
напрямую.
Создание решателя
Все решатели должны наследоваться от абстрактного базового класса Solver
. Основные требования расширения решателей включают переопределение метода SolverUpdate
. В этом методе разработчики должны обновить унаследованные свойства GoalPosition
, GoalRotation
и GoalScale
до нужных значений. Более того, обычно полезно использовать SolverHandler.TransformTarget
в качестве системы отсчета, необходимой клиенту.
Приведенный ниже код содержит пример нового компонента решателя InFront
, который помещает присоединенный объект на 2 метра вперед от SolverHandler.TransformTarget
. Если объект SolverHandler.TrackedTargetType
задан клиентом как Head
, тогда SolverHandler.TransformTarget
будет преобразованием камеры, а этот решатель разместит присоединенный GameObject на расстоянии 2 м перед каждым кадром.
/// <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;
}
}
}
Руководства по реализации решателей
Общие свойства решателей
Каждый компонент решателя имеет основной набор идентичных свойств, управляющих основным поведением решателя.
Если включен параметр Сглаживание, решатель со временем обновит преобразование GameObject с учетом вычисленных значений. Скорость этого изменения определяется свойством LerpTime каждого компонента преобразования. Например, более высокое значение MoveLerpTime приведет к более медленному приращению движения между кадрами.
Если включен параметр MaintainScale (Соблюдать масштаб), то решатель будет использовать локальный масштаб GameObject по умолчанию.
Orbital
Класс Orbital
является компонентом закрепления, который ведет себя как планеты в солнечной системе. Этот решатель обеспечит для присоединенных GameObject перемещение по орбите вокруг отслеживаемых преобразований. Таким образом, если для типа отслеживаемой целиSolverHandler
задано значение Head
, то GameObject будет перемещаться по орбите вокруг головы пользователя с фиксированным смещением.
Разработчики могут изменить это фиксированное смещение для сохранения меню и других компонентов сцены на уровне взгляда или пояса и т. д. вокруг пользователя. Это можно сделать, изменив свойства локального смещения Local Offset и смещения мира World Offset. Свойство типа ориентации Orientation Type определяет поворот, применяемый к объекту: должен ли всегда сохраняться начальный поворот, поворот в камеру либо поворот на любой объект, определяемый преобразованием.
RadialView
RadialView
— это еще один компонент с закреплением, который хранит определенную часть GameObject в усеченном конусе обзора пользователя.
Свойства Min & Max View Degrees определяют, сколько частей GameObject всегда должно находиться в представлении.
Свойства Min &Max Distance определяют, насколько далеко должен храниться GameObject от пользователя. Например, если подойти к объекту GameObject на расстояние Min Distance в 1 м, то GameObject будет отталкиваться от камеры, потому что он не должен приближаться к пользователю ближе чем на 1 м.
Как правило, RadialView
используется в сочетании с типом отслеживаемой цели со значением Head
, чтобы компонент следовал за взглядом пользователя. Однако этот компонент может сохраняться в представлении любого типа отслеживаемой цели.
Follow
Класс Follow
размещает элемент перед отслеживаемой целью относительно ее локальной оси вперед. Элемент может быть слабо ограничен (то есть закреплен), чтобы он не следовал за отслеживаемой целью, пока она не переместится за пределы, указанные пользователем.
Он работает аналогично решателем RadialView с дополнительными элементами управления для управления максимальными горизонтальными и вертикальными градусами представления и механизмами для изменения ориентации объекта.
InBetween
Класс InBetween
удерживает присоединенный GameObject между двумя преобразованиями. Собственный тип целевого объекта GameObject SolverHandler
и InBetween
свойство второго отслеживаемого целевого типа компонента определяют эти две конечные точки преобразования. Как правило, оба типа получают значение CustomOverride
, а результирующие SolverHandler.TransformOverride
и значения InBetween.SecondTransformOverride
задаются для двух отслеживаемых конечных точек.
Во время выполнения компонент InBetween
создаст другой компонент SolverHandler
на основе типа второй отслеживаемой цели и свойства Переназначение второго преобразования (Second Transform Override).
PartwayOffset
определяет, где на линии между двумя преобразованиями должен располагаться объект: 0,5 — на половине пути, 1,0 — на первом преобразовании, а 0,0 — на втором преобразовании.
SurfaceMagnetism
SurfaceMagnetism
работает путем создания луча к заданной маске LayerMask поверхностей и размещения GameObject в точке контакта.
Surface Normal Offset (Смещение нормали поверхности) помещает GameObject на заданное расстояние на расстоянии в метрах от поверхности в направлении нормали в точке попадания на поверхности.
И наоборот, Surface Ray Offset (Смещение луча поверхности) помещает GameObject на заданное расстояние в метрах от поверхности, но в противоположном направлении создания луча. Таким образом, если луч создается по взгляду пользователя, GameObject будет перемещен ближе по линии от точки попадания на поверхности до камеры.
Orientation Mode (Режим ориентации) определяет тип поворота, который необходимо применить к нормали на поверхности.
- None — поворот не применяется.
- TrackedTarget — объект будет обращен к отслеживаемому преобразованию, движущему луч.
- SurfaceNormal — объект будет выравниваться по нормали в точке попадания на поверхности.
- Blended — объект будет выравниваться на основе нормали в точке попадания на поверхности и на основе обращения к отслеживаемому преобразованию.
Чтобы заставить связанный объект GameObject оставаться в вертикальном положении в любом режиме, кроме None, включите параметр Keep Orientation Vertical (Сохранять вертикальную ориентацию).
Примечание
Свойство Orientation Blend (Смешение ориентации) используется для управления балансом между факторами поворота, если Orientation Mode (Режим ориентации) установлен в значение Blended (Смешанная). При значении 0,0 ориентация будет полностью управляться режимом TrackedTarget, а при значении 1,0 — режимом SurfaceNormal.
перекрытие
Это Overlap
простой решатель, который будет хранить преобразование объекта в той же позиции и повороте, что и целевой SolverHandler's
объект преобразования.
Определение достигаемых поверхностей
При добавлении компонента SurfaceMagnetism
в GameObject важно рассмотреть слой GameObject и его дочерних элементов, если они есть. Компонент работает, выполняя оправку различных типов лучей, чтобы определить, к какой поверхности они должны "примагнититься". Предположим, у решателя GameObject есть коллайдер на одном из слоев, указанных в свойстве MagneticSurfaces
SurfaceMagnetism
. В этом случае луч, скорее всего, попадет в себя, в результате чего GameObject прикрепится к своей собственной точке коллайдера. Такого поведения можно избежать, задав для основного GameObject и всех дочерних элементов слой Ignore Ray cast (Игнорирование луча) или изменив массив MagneticSurfaces
в LayerMask соответствующим образом.
И наоборот, GameObject SurfaceMagnetism
не будет конфликтовать с областями на слое, не перечисленными в свойстве MagneticSurfaces
. Мы рекомендуем размещать все нужные поверхности на выделенном слое (Поверхности) и присваивать свойству MagneticSurfaces
значения только этого слоя. Использование значений default (по умолчанию) или everything (все) может привести к тому, что компоненты пользовательского интерфейса или курсоры будут влиять на решатель.
Наконец, поверхности, расположенные дальше, чем указано в значении свойства MaxRaycastDistance
, будут игнорироваться лучами SurfaceMagnetism
.
DirectionalIndicator
Класс DirectionalIndicator
является компонентом закрепления, который ориентируется в направлении нужной точки в пространстве. Он чаще всего используется, если для типа отслеживаемой целиSolverHandler
задано значение Head
. Таким образом, компонент UX с решателем DirectionalIndicator
будет направлять пользователя на нужную точку в пространстве. Эта точка определяется свойством Directional Target (Целевое направление).
Если целевое направление доступно для просмотра пользователю или в SolverHandler
установлена какая-либо система координат, этот решатель отключит все компоненты Renderer
под ним. Если направление не отображается, на индикаторе будет включено все.
Чем ближе пользователь будет к появлению точки целевого направления в поле зрения, тем меньше будет индикатор.
Min Indicator Scale — минимальный масштаб для объекта индикатора.
Max Indicator Scale — максимальный масштаб для объекта индикатора.
Visibility Scale Factor (Коэффициент шкалы видимости) — это множитель для увеличения или уменьшения поля зрения, который определяет, где становится видна точка целевого направления.
Свойство View Offset (смещение представления) с точки зрения системы координат (т. е. возможно, самой камеры) определяет, насколько далеко в направлении индикатора находится объект из центра окна просмотра.
Сцена с примером индикатора направления (Assets/MRTK/Examples/Demos/Solvers/Scenes/DirectionalIndicatorSolverExample.unity)
Меню руки с HandConstraint и HandConstraintPalmUp
Поведение HandConstraint
предоставляет решателя, который ограничивает отслеживаемый объект регионом, безопасным для содержимого, ограничиваемого руками (например, пользовательского интерфейса руки, меню и т. д.). Безопасные регионы считаются областями, которые не пересекаются с рукой. Производный класс HandConstraint
, вызываемый HandConstraintPalmUp
, также включен для демонстрации общего поведения активации объекта, отслеживаемого решателем, когда ладонь обращена к пользователю.
См. страницу меню руки Hand Menu, на которой приведены примеры использования решателей для ограничения руки (Hand Constraint) для создания меню.