Solucionadores – MRTK3

Solucionador Principal

Solucionadores são componentes que facilitam o cálculo da posição e da orientação de um objeto de acordo com um algoritmo predefinido. Exemplo: colocar um objeto na superfície com o qual o raio de foco do usuário se cruza.

Além disso, o sistema Solucionador define, de maneira determinista, uma ordem de operações para esses cálculos de transformação, pois não há uma forma confiável de especificar ao Unity a ordem de atualização dos componentes.

Os solucionadores oferecem diversos comportamentos para anexar objetos a outros objetos ou sistemas. Um outro exemplo é um objeto com marcação que faz a focalização na frente do usuário com base na câmera. Um solucionador também pode ser anexado a um controlador e a um objeto para que o objeto seja marcado no controlador. Todos os solucionadores podem ser empilhados com segurança. Por exemplo, um comportamento com marcação, mais magnetismo de superfície mais impulso.

Como usar

O sistema Solucionador consiste em três categorias de scripts:

  • Solver: a classe abstrata de base da qual todos os solucionadores derivam. Ela fornece acompanhamento de estado, suavização de parâmetros e implementação, integração automática do sistema do solucionador e ordem de atualização.
  • SolverHandler: define o objeto de referência a ser rastreado (por exemplo: a transformação da câmera principal, o raio de mão etc.), processa a coleta de componentes do solucionador e atualiza-os na ordem adequada.

A terceira categoria é o próprio solucionador. Os seguintes solucionadores fornecem os blocos de construção para o comportamento básico:

  • Orbital: bloqueia em uma posição e deslocamento específicos do objeto referenciado.
  • ConstantViewSize: dimensiona para manter um tamanho constante em relação à exibição do objeto referenciado.
  • RadialView: mantém o objeto em uma conversão de cone de exibição pelo objeto referenciado.
  • Follow: mantém o objeto dentro de um conjunto de limites definidos pelo usuário do objeto referenciado.
  • InBetween: mantém um objeto entre dois objetos rastreados.
  • SurfaceMagnetism: lança raios em superfícies no mundo e alinha o objeto a essa superfície.
  • DirectionalIndicator: determina a posição e a orientação de um objeto como um indicador direcional. Do ponto de referência do Destino Controlado do SolverHandler, esse indicador ficará em direção ao DirectionalTarget fornecido.
  • Momentum: aplica aceleração/velocidade/atrito para simular o impulso e a elasticidade de um objeto que está sendo movido por outros solucionadores/componentes.
  • HandConstraint: restringe o objeto seguindo as mãos em uma região que não cruza o GameObject com as mãos. Útil para conteúdo interativo restrito à mão, como menus etc. Esse solucionador destina-se a trabalhar com XRNode.
  • HandConstraintPalmUp: deriva do HandConstraint, mas inclui lógica para testar se a palma da mão está voltada para o usuário antes da ativação. Esse solucionador funciona apenas com controladores XRNode. Com outros tipos de controlador, se comporta exatamente como sua classe base.
  • Overlap: sobrepõe o objeto rastreado.

Para usar o sistema do Solucionador, adicione um dos componentes listados acima a um GameObject. Como todos os Solucionadores exigem SolverHandler, um será criado automaticamente pelo Unity.

Observação

Exemplos de como usar o sistema de Solucionadores podem ser encontrados no arquivo SolverExamples.scene.

Como alterar a referência de acompanhamento

A propriedade Tipo de Destino Controlado do componente SolverHandler define o ponto de referência que todos os solucionadores usarão para calcular os respectivos algoritmos. Por exemplo, o tipo de valor Head com um componente SurfaceMagnetism simples, resultará em um raio partindo da cabeça na direção do foco do usuário para resolver qual superfície é atingida. Valores potenciais para a propriedade TrackedTargetType são:

  • *Head: o ponto de referência é a transformação da câmera principal
  • ControllerRay: o ponto de referência é a transformação LinePointer em um controlador (ou seja, origem do ponteiro em um controlador de movimentos ou um controlador de mão) apontando na direção do raio de linha
    • Use a propriedade TrackedHandedness para selecionar a preferência de destro/canhoto (ou seja, Canhoto, Destro, Ambos)
  • HandJoint: o ponto de referência é a transformação de uma articulação da mão específica
    • Use a propriedade TrackedHandedness para selecionar a preferência de destro/canhoto (ou seja, Canhoto, Destro, Ambos)
    • Use a propriedade TrackedHandJoint para determinar a transformação conjunta a ser utilizada
  • CustomOverride: ponto de referência do TransformOverride atribuído

Observação

Para os tipos ControllerRay e HandJoint, o manipulador do solucionador tentará fornecer primeiro a transformação do controlador/mão esquerda e, em seguida, a direita se a primeira estiver indisponível ou a menos que a propriedade TrackedHandedness especifique o contrário.

Importante

A maioria dos solucionadores usa o vetor de destino da transformação acompanhada fornecido por SolverHandler. Ao usar um tipo de destino acompanhado da Articulação da Mão, o vetor de avanço da articulação da palma da mão pode apontar pelos dedos e não pela palma da mão. Isso depende da plataforma que fornece os dados da articulação da mão. Para a simulação de entrada e o Windows Mixed Reality, o vetor de ascensão aponta para cima pela palma da mão (ou seja, o vetor verde vai para cima e o vetor azul vai para frente).

Para resolver isso, atualize a propriedade de Rotação Adicional no SolverHandler para <90, 0, 0>. Isso garante que o vetor de avanço fornecido aos solucionadores aponte pela palma e na direção contrária da mão.

Como alternativa, use o tipo de destino controlado Raio do Controlador para obter um comportamento semelhante ao apontar com as mãos.

Como encadear solucionadores

É possível adicionar vários componentes Solver ao mesmo GameObject encadeando seus algoritmos. Os componentes SolverHandler lidam com a atualização de todos os solucionadores no mesmo GameObject. Por padrão, as SolverHandler chamadas GetComponents<Solver>() em Iniciar retornarão os Solucionadores na ordem em que aparecem no inspetor.

Além disso, a definição da propriedade Transformação Vinculada Atualizada como true, instrui Solver a salvar a posição, a orientação e escala calculadas em uma variável intermediária acessível a todos os Solucionadores (ou seja, GoalPosition). Se definida como falso, Solver atualizará a transformação do GameObject diretamente. Ao salvar as propriedades de transformação em um local intermediário, outros Solucionadores podem executar seus cálculos começando na variável intermediária. Isso acontece porque o Unity não permite que as atualizações no gameObject.transform sejam empilhadas dentro do mesmo quadro.

Observação

Os desenvolvedores podem modificar a ordem de execução dos Solucionadores definindo a propriedade SolverHandler.Solvers diretamente.

Como criar um novo solucionador

Todos os solucionadores devem herdar da classe base abstrata, Solver. Os principais requisitos de uma extensão do Solucionador envolvem a substituição do método SolverUpdate. Nesse método, os desenvolvedores devem atualizar as propriedades herdadas GoalPosition, GoalRotation e GoalScale para os valores desejados. Além disso, é valioso aproveitar SolverHandler.TransformTarget como o quadro de referência desejado pelo consumidor.

O código fornecido abaixo fornece um exemplo de um novo componente Solucionador chamado InFront que coloca o objeto anexado 2m na frente do SolverHandler.TransformTarget. Se o consumidor definir SolverHandler.TrackedTargetType como Head, o SolverHandler.TransformTarget será a transformação da câmera e, portanto, esse Solucionador colocará o GameObject anexado a 2m na frente do olhar dos usuários a cada quadro.

/// <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;
        }
    }
}

Guias de implementação do solucionador

Propriedades comuns do solucionador

Cada componente do Solucionador tem um conjunto principal de propriedades idênticas que controlam o comportamento principal do Solucionador.

Se a Suavização estiver habilitada, o Solucionador atualizará gradualmente a transformação do GameObject ao longo do tempo de acordo com os valores calculados. A propriedade LerpTime de cada componente da transformação determina a velocidade dessa alteração. Por exemplo, um valor MoveLerpTime mais alto resultará em incrementos mais lentos na movimentação entre quadros.

Se MaintainScale estiver habilitado, o Solucionador utilizará a escala local padrão do GameObject.

Orbital

A classe Orbital é um componente de marcação que se comporta como planetas em um sistema solar. Esse Solucionador garantirá que o GameObject anexado orbite ao redor da transformação acompanhada. Portanto, se o Tipo de Destino Acompanhado do SolverHandler estiver definido como Head, o GameObject irá orbitar ao redor da cabeça do usuário com um deslocamento fixo aplicado.

Os desenvolvedores podem modificar esse deslocamento fixo para manter os menus ou outros componentes de cena no nível dos olhos, da cintura e outros em relação ao usuário. Isso é feito alterando as propriedades Deslocamento Local e Deslocamento Mundial. A propriedade Tipo de Orientação determina a rotação aplicada ao objeto se ele mantiver sua rotação original ou sempre ficar voltado para a câmera ou para qualquer transformação que estiver impulsionando sua posição.

RadialView

O RadialView é outro componente de marcação que mantém uma parte específica de um GameObject dentro do tronco de exibição do usuário.

As propriedades Min & Max View Degrees determinam quanto de uma parte do GameObject deve estar sempre em exibição.

As propriedades Min & Max Distance determinam até que ponto o GameObject deve ser mantido do usuário. Por exemplo, andar na direção do GameObject com uma Distância Mínima de 1m fará com que o GameObject nunca esteja a menos de 1m do usuário.

Geralmente, o RadialView é usado em conjunto com o Tipo de Destino Controlado definido como Head para que o componente siga o olhar do usuário. No entanto, esse componente pode funcionar para ser mantido em "exibição" de qualquer Tipo de Destino Controlado.

Seguir

A classe Follow posiciona um elemento na frente do destino controlado em relação ao eixo dianteiro local. O elemento pode ser restringido livremente (o que também é conhecido como "com marcação") para que ele não siga até que o destino rastreado seja movido para além dos limites definidos pelo usuário.

Ele funciona de forma semelhante ao solucionador RadialView, com controles adicionais para gerenciar Max Horizontal & Graus de Exibição Vertical e mecanismos para alterar a Orientação do objeto.

InBetween

A classe InBetween manterá o GameObject anexado entre duas transformações. O próprio SolverHandlerTipo de Destino Controlado do GameObject e a propriedade do Segundo Tipo de Destino Controlado do componente InBetween definem esses dois pontos de extremidade de transformação. Em geral, ambos os tipos serão definidos como CustomOverride e os valores SolverHandler.TransformOverride e InBetween.SecondTransformOverride resultantes são definidos como os dois pontos de extremidade controlados.

O componente InBetween criará outro componente SolverHandler no tempo de execução com base nas propriedades do Segundo Tipo de Destino Controlado e Segunda Substituição de Transformação.

Na linha entre duas transformações, o PartwayOffset define onde o objeto deve ser colocado com 0,5 como a metade, 1,0 na primeira transformação e 0,0 na segunda transformação.

SurfaceMagnetism

SurfaceMagnetism funciona executando um raio em um LayerMask definido de superfícies e colocando o GameObject nesse ponto de contato.

O Deslocamento Normal da Superfície colocará o GameObject a uma distância definida em metros de distância da superfície na direção do normal no ponto de clique na superfície.

Por outro lado, o Deslocamento do Raio de Superfície colocará o GameObject a uma distância definida em metros de distância da superfície, mas na direção contrária do raycast executado. Portanto, se o raio for o olhar do usuário, o GameObject se aproximará da câmera ao longo da linha do ponto de contato na superfície.

O Modo de Orientação determina o tipo de rotação a ser aplicada em relação ao normal na superfície.

  • None – nenhuma rotação aplicada
  • TrackedTarget – o objeto ficará voltado para a transformação controlada executando o raycast
  • SurfaceNormal – o objeto será alinhado com base no normal no ponto de clique na superfície
  • Blended – o objeto será alinhado com base no normal no ponto de clique na superfície E com base na transformação controlada.

Para forçar o GameObject associado a permanecer na vertical em qualquer modo diferente de None, habilite Manter Orientação Vertical.

Observação

Use a propriedade Combinação de Orientação para controlar o equilíbrio entre os fatores de rotação quando o Modo de Orientação for definido como Blended. A orientação do valor de 0,0 será totalmente determinada pelo modo TrackedTarget, e a orientação do valor de 1,0 será totalmente determinada por SurfaceNormal.

Sobreposição

O Overlap é um solucionador simples que manterá a transformação do objeto na mesma posição e rotação que o destino de SolverHandler's transformação.

Determinando quais superfícies podem ser atingidas

Ao adicionar um componente SurfaceMagnetism a um GameObject, é importante considerar a camada do GameObject e seus filhos, se algum tiver colisores. O componente funciona executando vários raios para determinar a qual superfície deverá se "atrair". Suponha que o solucionador GameObject tenha um colisor em uma das camadas listadas na propriedade MagneticSurfaces de SurfaceMagnetism. Nesse caso, o raio provavelmente se atingirá, resultando na anexação do GameObject ao próprio ponto de colisor. Esse comportamento inesperado pode ser evitado definindo o GameObject principal e todos os filhos para a camada Ignorar Raycast ou modificando a matriz LayerMask MagneticSurfaces adequadamente.

Por outro lado, um GameObject SurfaceMagnetism não colidirá com superfícies em uma camada não listada na propriedade MagneticSurfaces. Recomendamos colocar todas as superfícies desejadas em uma camada dedicada (ou seja, Superfícies) e definir a propriedade MagneticSurfaces apenas como essa camada. Usar o padrão ou tudo pode resultar em componentes de interface do usuário ou cursores que contribuem para o solucionador.

Por fim, superfícies mais distantes que a configuração da propriedade MaxRaycastDistance serão ignoradas pelos raycasts SurfaceMagnetism.

DirectionalIndicator

A classe DirectionalIndicator é um componente de marcação voltado para a direção do ponto desejado no espaço. O uso mais comum é quando o Tipo de Destino Controlado do SolverHandler é definido como Head. Dessa forma, um componente de experiência do usuário com o solucionador DirectionalIndicator direcionará um usuário a olhar para o ponto desejado no espaço. Esse ponto é determinado pela propriedade Destino Direcional.

Se o destino direcional puder ser visualizado pelo usuário ou um quadro de referência for definido no SolverHandler, esse solucionador desabilitará todos os componentes Renderer abaixo dele. Se não for visualizável, tudo será habilitado no indicador.

O tamanho do indicador reduzirá quanto mais próximo o usuário estiver de capturar o Destino Direcional em seu FOV.

  • Escala Mínima de Indicadores – a escala mínima do objeto indicador

  • Escala Máxima de Indicadores – a escala máxima do objeto indicador

  • Fator de Escala de Visibilidade – Multiplicador para aumentar ou diminuir o FOV que determina se o ponto do Destino Direcional é visualizável ou não

  • Deslocamento de Exibição – Do ponto de vista do quadro de referência (ou seja, possivelmente a câmera) e na direção do indicador, essa propriedade define a qual distância o objeto está do centro do visor.

Cena de Exemplo do Indicador Direcional (Assets/MRTK/Examples/Demos/Solvers/Scenes/DirectionalIndicatorSolverExample.unity)

Menu lateral com HandConstraint e HandConstraintPalmUp

O comportamento HandConstraint fornece um solucionador que restringe o objeto controlado a uma região segura para conteúdo restrito à mão (como interface do usuário da mão, menus etc.). Regiões seguras são consideradas áreas que não se cruzam com a mão. Uma classe derivada de HandConstraint chamada HandConstraintPalmUp também é incluída para demonstrar um comportamento comum de ativar o objeto controlado pelo solucionador quando a palma da mão estiver voltada para o usuário.

Consulte a documentação Menu Lateral para ver os exemplos de como usar o solucionador de Restrição de Mão para criar menus laterais.