Controladores, ponteiros e foco — MRTK2

Controladores, ponteiros e foco são conceitos de nível superior que se baseiam na base estabelecida pelo sistema de entrada principal. Juntos, eles fornecem uma grande parte do mecanismo para interagir com objetos na cena.

Controladores

Os controladores são representações de um controlador físico (6 graus de liberdade, mão articulada etc.). Eles são criados por gerenciadores de dispositivos e são responsáveis por se comunicar com o sistema subjacente correspondente e traduzir esses dados em eventos e dados em forma de MRTK.a

Por exemplo, na plataforma Windows Mixed Reality, o WindowsMixedRealityArticulatedHand é um controlador responsável pela interfação com as APIs subjacentes de acompanhamento manual do Windows para obter informações sobre as articulações, a pose e outras propriedades da mão. Ele é responsável por transformar esses dados em eventos relevantes do MRTK (por exemplo, chamando RaisePoseInputChanged ou RaiseHandJointsUpdated) e atualizando seu próprio estado interno para que as consultas retornem TryGetJointPose dados corretos.

Em geral, o ciclo de vida de um controlador envolverá:

  1. Um controlador é criado por um gerenciador de dispositivos após a detecção de uma nova origem (por exemplo, o gerenciador de dispositivos detecta e começa a rastrear uma mão).

  2. No loop Update() do controlador, ele chama seu sistema de API subjacente.

  3. No mesmo loop de atualização, ele gera alterações de evento de entrada chamando diretamente para o próprio sistema de entrada principal (por exemplo, levantando HandMeshUpdated ou HandJointsUpdated).

Ponteiros e foco

Ponteiros são usados para interagir com objetos de jogo. Esta seção descreve como os ponteiros são criados, como eles são atualizados e como eles determinam os objetos que estão em foco. Ele também abordará os diferentes tipos de ponteiros que existem e os cenários em que eles estão ativos.

Categorias de ponteiro

Os ponteiros geralmente se enquadram em uma das seguintes categorias:

  • Ponteiros distantes

    Esses tipos de ponteiros são usados para interagir com objetos que estão longe do usuário (distante é definido como simplesmente "não perto"). Esses tipos de ponteiros geralmente convertem linhas que podem ir longe no mundo e permitem que o usuário interaja e manipule objetos que não estão imediatamente ao lado deles.

  • Ponteiros próximos

    Esses tipos de ponteiros são usados para interagir com objetos próximos o suficiente para o usuário pegar, tocar e manipular. Geralmente, esses tipos de ponteiros interagem com objetos procurando objetos nas proximidades (seja fazendo raycasting em pequenas distâncias, fazendo conversão esférica procurando objetos nas proximidades ou enumerando listas de objetos que são considerados agarráveis/tocáveis).

  • Ponteiros de teletransporte

    Esses tipos de ponteiros se conectam ao sistema de teletransporte para lidar com a movimentação do usuário para o local direcionado pelo ponteiro.

Mediação de ponteiro

Como um único controlador pode ter vários ponteiros (por exemplo, a mão articulada pode ter ponteiros de interação próximos e distantes), existe um componente responsável por mediar qual ponteiro deve estar ativo.

Por exemplo, à medida que a mão do usuário se aproxima de um botão pressionável, o ShellHandRayPointer deve parar de ser exibido e o PokePointer deve ser ativado.

Isso é tratado pelo DefaultPointerMediator, que é responsável por determinar quais ponteiros estão ativos, com base no estado de todos os ponteiros. Uma das principais coisas que isso faz é desabilitar ponteiros distantes quando um ponteiro próximo estiver perto de um objeto (consulte DefaultPointerMediator).

É possível fornecer uma implementação alternativa do mediador de ponteiro alterando a PointerMediator propriedade no perfil do ponteiro.

Como desabilitar ponteiros

Como o mediador de ponteiro executa cada quadro, ele acaba controlando o estado ativo/inativo de todos os ponteiros. Portanto, se você definir a propriedade IsInteractionEnabled de um ponteiro no código, ela será substituída pelo mediador de ponteiro a cada quadro. Em vez disso, você pode especificar o PointerBehavior para controlar se os ponteiros devem estar ativados ou desativados por conta própria. Observe que isso só funcionará se você estiver usando o padrão FocusProvider e DefaultPointerMediator no MRTK.

Exemplo: Desabilitar raios manuais no MRTK

O código a seguir desativará os raios de mão no MRTK:

// Turn off all hand rays
PointerUtils.SetHandRayPointerBehavior(PointerBehavior.AlwaysOff);

// Turn off hand rays for the right hand only
PointerUtils.SetHandRayPointerBehavior(PointerBehavior.AlwaysOff, Handedness.Right);

O código a seguir retornará os raios manuais ao comportamento padrão no MRTK:

PointerUtils.SetHandRayPointerBehavior(PointerBehavior.Default);

O código a seguir forçará os raios manuais a serem ativados, independentemente de perto de um agarrável:

// Turn off all hand rays
PointerUtils.SetHandRayPointerBehavior(PointerBehavior.AlwaysOn);

Confira PointerUtils e TurnPointersOnOff para obter mais exemplos.

FocusProvider

O FocusProvider é o cavalo de trabalho responsável por iterar sobre a lista de todos os ponteiros e descobrir qual é o objeto focado para cada ponteiro.

Em cada Update() chamada, isso vai:

  1. Atualize todos os ponteiros, raycasting e fazendo a detecção de ocorrência conforme configurado pelo próprio ponteiro (por exemplo, o ponteiro de esfera pode especificar o RaycastMode SphereOverlap, portanto, FocusProvider fará uma colisão baseada em esfera)

  2. Atualize o objeto focado por ponteiro (ou seja, se um objeto ganhasse foco, ele também dispararia eventos para esse objeto, se um objeto perdesse o foco, acionaria o foco perdido etc.

Configuração de ponteiro e ciclo de vida

Os ponteiros podem ser configurados na seção Ponteiros do perfil do sistema de entrada.

O tempo de vida de um ponteiro geralmente é o seguinte:

  1. Um gerenciador de dispositivos detectará a presença de um controlador. Esse gerenciador de dispositivos criará o conjunto de ponteiros associado ao controlador por meio de uma chamada para RequestPointers.

  2. O FocusProvider, em seu loop Update(), itera em todos os ponteiros válidos e faz a lógica de detecção de raycast ou de ocorrência associada. Isso é usado para determinar o objeto que é focado por cada ponteiro específico.

    • Como é possível ter várias fontes de entrada ativas ao mesmo tempo (por exemplo, duas mãos ativas presentes), também é possível ter vários objetos com foco ao mesmo tempo.
  3. O gerenciador de dispositivos, ao descobrir que uma origem do controlador foi perdida, derrubará os ponteiros associados ao controlador perdido.