Interacionável — MRTK2

Interativo

O Interactable componente é um contêiner all-in-one para tornar qualquer objeto facilmente interacionável e responsivo à entrada. O interativo atua como um catch-all para todos os tipos de entrada, incluindo toque, raios manuais, fala etc e funil essas interações em eventos e respostas de tema visual . Esse componente fornece uma maneira fácil de criar botões, alterar a cor em objetos com foco e muito mais.

Como configurar Interacionável

O componente permite três seções primárias de configuração:

  1. Configuração de entrada geral
  2. Temas visuais direcionados a vários GameObjects
  3. Manipuladores de eventos

Configurações gerais de entrada

Configurações Gerais Interacionáveis

Estados

States é um parâmetro ScriptableObject que define as fases de interações, como pressionar ou observar, para Perfis Interacionáveis e Temas Visuais.

O DefaultInteractableStates (Assets/MRTK/SDK/Features/UX/Interactable/States/DefaultInteractableStates.asset) é fornecido com o MRTK pronto para uso e é o parâmetro padrão para componentes interativos .

Exemplo de States ScriptableObject no inspector

O ativo DefaultInteractableStates contém quatro estados e utiliza a implementação do InteractableStates modelo de estado.

  • Padrão: nada está acontecendo, esse é o estado base mais isolado.

  • Foco: o objeto está sendo apontado. Esse é um único estado, nenhum outro estado está definido no momento, mas ele sairá da classificação Padrão.

  • Pressione: o objeto está sendo apontado e um botão ou uma mão está pressionando. O estado De imprensa é classificado como Padrão e Foco. Esse estado também será definido como um fallback para a Imprensa Física.

  • Desabilitado: o botão não deve ser interativo e os comentários visuais informarão ao usuário se, por algum motivo, esse botão não estiver utilizável no momento. Em teoria, o estado desabilitado pode conter todos os outros estados, mas quando Habilitado está desativado, o estado Desabilitado supera todos os outros estados.

Um valor de bit (#) é atribuído ao estado dependendo da ordem na lista.

Observação

Geralmente, é recomendável utilizar o DefaultInteractableStates (Assets/MRTK/SDK/Features/UX/Interactable/States/DefaultInteractableStates.asset) ao criar componentes interacionáveis .

No entanto, há 17 estados interacionáveis disponíveis que podem ser usados para conduzir temas, embora alguns sejam destinados a serem controlados por outros componentes. Aqui está uma lista daqueles com funcionalidade interna.

  • Visitado: o Interativo foi clicado.
  • Alternado: o botão está em um estado alternado ou Índice de dimensão é um número ímpar.
  • Gesto: a mão ou o controlador foi pressionado e movido da posição original.
  • VoiceCommand: um comando de fala foi usado para disparar o Interactable.
  • PhysicalTouch: uma entrada por toque é detectada no momento, use NearInteractionTouchable para habilitar.
  • Captura: uma mão está agarrando atualmente nos limites do objeto , use NearInteractionGrabbable para habilitar

Enabled

Alterna se um Interativo começará habilitado ou não. Isso corresponde ao Interactable.IsEnabled código in.

A propriedade habilitada de um Interativo é diferente da propriedade habilitada configurada por meio de GameObject/Component (ou seja, SetActive etc. Desabilitar o GameObject ou o MonoBehaviour Interativo desabilitará tudo na classe da execução, incluindo entrada, temas visuais, eventos etc. Desabilitar por meio Interactable.IsEnabled de desabilitará a maior parte da manipulação de entrada, redefinindo os estados de entrada relacionados. No entanto, a classe ainda executará todos os quadros e receberá eventos de entrada que serão ignorados. Isso é útil para exibir o Interativo em um estado desabilitado que pode ser feito por meio de Temas Visuais. Um exemplo típico disso seria um botão enviar aguardando a conclusão de todos os campos de entrada necessários.

Ações de entrada

Selecione a ação de entrada na configuração de entrada ou no perfil de mapeamento do controlador ao qual o componente Interativo deve reagir.

Essa propriedade pode ser configurada em runtime no código por meio de Interactable.InputAction.

IsGlobal

Se for true, isso marcará o componente como um ouvinte de entrada global para a ação de entrada selecionada. O comportamento padrão é false, o que restringirá a entrada apenas a esse colisor interativo /GameObject.

Essa propriedade pode ser configurada em runtime no código por meio de Interactable.IsGlobal.

Comando de Fala

Comando de fala, do Perfil de Comandos de Fala do MRTK, para disparar um evento OnClick para interação de voz.

Essa propriedade pode ser configurada em runtime no código por meio de Interactable.VoiceCommand.

Requer foco

Se for true, o comando de voz só ativará o Interactable se e somente se ele já tiver o foco de um ponteiro. Se for false, o Interactable atuará como um ouvinte global para o comando de voz selecionado. O comportamento padrão é verdadeiro, pois vários ouvintes de fala globais podem ser difíceis de organizar em uma cena.

Essa propriedade pode ser configurada em runtime no código por meio de Interactable.VoiceRequiresFocus.

Modo de Seleção

Essa propriedade define a lógica de seleção. Quando um Interacionável é clicado, ele itera em um próximo nível de dimensão . As dimensões são semelhantes à classificação e definem um estado fora das entradas (ou seja, foco, pressione etc. Eles são úteis para definir estados de alternância ou outros estados de várias classificações associados a um botão. O nível de Dimensão atual é acompanhado por Interactable.DimensionIndex.

Os modos de seleção disponíveis são:

  • Botão - Dimensões = 1, simples clicável interacionável
  • Alternar - Dimensões = 2, alternativas interacionáveis entre o/ estadodesativado
  • Várias dimensões - Dimensões>= 3, cada clique aumenta o nível de dimensão atual + 1. Útil para definir um estado de botão para uma lista, etc.

Interacionável também permite que vários temas sejam definidos por Dimensão. Por exemplo, quando SelectionMode=Toggle, um tema pode ser aplicado quando o Interativo é desmarcado e outro tema aplicado quando o componente é selecionado.

O modo de seleção atual pode ser consultado em runtime por meio de Interactable.ButtonMode. A atualização do modo em runtime pode ser obtida definindo a Interactable.Dimensions propriedade para corresponder à funcionalidade desejada. Além disso, a dimensão atual, útil para os modos Alternar e Várias Dimensões , pode ser acessada por meio de Interactable.CurrentDimension.

Perfis interacionáveis

Perfis são itens que criam uma relação entre um GameObject e um Tema Visual. O perfil define qual conteúdo será manipulado por um tema quando ocorrer uma alteração de estado.

Os temas funcionam muito como materiais. Eles são objetos scriptáveis que contêm uma lista de propriedades que serão atribuídas a um objeto com base no estado atual. Os temas também são reutilizáveis e podem ser atribuídos em vários objetos de UX interacionáveis .

Redefinir ao destruir

Os temas visuais modificam várias propriedades em um GameObject direcionado, dependendo da classe e do tipo de mecanismo de tema selecionado. Se Reset On Destroy for true quando o componente Interativo for destruído, o componente redefinirá todas as propriedades modificadas de temas ativos para seus valores originais. Caso contrário, quando destruído, o componente Interativo deixará todas as propriedades modificadas no estado em que se encontra. Neste último caso, o último estado de valores persistirá, a menos que seja alterado por outro componente externo. O padrão é falso.

Barra de perfil

Eventos

Cada componente interativo tem um evento OnClick que é acionado quando o componente é simplesmente selecionado. No entanto, Interacionável pode ser usado para detectar eventos de entrada diferentes de apenas OnClick.

Clique no botão Adicionar Evento para adicionar um novo tipo de definição do Receptor de Eventos. Depois de adicionado, selecione o tipo de Evento desejado.

Exemplo de eventos)

Há diferentes tipos de receptores de eventos para responder a diferentes tipos de entrada. O MRTK é fornecido com o seguinte conjunto de receptores prontos para uso.

Um receptor personalizado pode ser criado fazendo uma nova classe que estende ReceiverBase.

Exemplo de receptor de alternância de evento

Exemplo de um receptor de evento de alternância

Receptores interacionáveis

O InteractableReceiver componente permite que os eventos sejam definidos fora do componente interativo de origem. O InteractableReceiver escutará um tipo de evento filtrado disparado por outro Interativo. Se a propriedade Interactable não for atribuída diretamente, a propriedade Escopo da Pesquisa definirá a direção que InteractableReceiver escuta para eventos que estão em si, em um pai ou em um GameObject filho.

InteractableReceiverList atua de maneira semelhante, mas para uma lista de eventos correspondentes.

Receptor interacionável

Criar eventos personalizados

Assim como os Temas Visuais, os eventos podem ser estendidos para detectar qualquer padrão de estado ou para expor a funcionalidade.

Eventos personalizados podem ser criados de duas main maneiras:

  1. Estenda a ReceiverBase classe para criar um evento personalizado que aparecerá na lista suspensa de tipos de eventos. Um evento do Unity é fornecido por padrão, mas eventos adicionais do Unity podem ser adicionados ou o evento pode ser definido para ocultar eventos do Unity. Essa funcionalidade permite que um designer trabalhe com um engenheiro em um projeto para criar um evento personalizado que o designer pode configurar no editor.

  2. Estenda a ReceiverBaseMonoBehavior classe para criar um componente de evento completamente personalizado que pode residir no objeto Interativo ou em outro. O ReceiverBaseMonoBehavior fará referência ao Interativo para detectar alterações de estado.

Exemplo de extensão ReceiverBase

A CustomInteractablesReceiver classe exibe status informações sobre um Interativo e é um exemplo de como criar um Receptor de Eventos personalizado.

public CustomInteractablesReceiver(UnityEvent ev) : base(ev, "CustomEvent")
{
    HideUnityEvents = true; // hides Unity events in the receiver - meant to be code only
}

Os métodos a seguir são úteis para substituir/implementar ao criar um Receptor de Eventos personalizado. ReceiverBase.OnUpdate() é um método abstrato que pode ser usado para detectar padrões/transições de estado. Além disso, os ReceiverBase.OnVoiceCommand() métodos e ReceiverBase.OnClick() são úteis para criar lógica de evento personalizada quando o Interativo é selecionado.

public override void OnUpdate(InteractableStates state, Interactable source)
{
    if (state.CurrentState() != lastState)
    {
        // the state has changed, do something new
        lastState = state.CurrentState();
        ...
    }
}

public virtual void OnVoiceCommand(InteractableStates state, Interactable source,
                                    string command, int index = 0, int length = 1)
{
    base.OnVoiceCommand(state, source, command, index, length);
    // voice command called, perform some action
}  

public virtual void OnClick(InteractableStates state,
                            Interactable source,
                            IMixedRealityPointer pointer = null)
{
    base.OnClick(state, source);
    // click called, perform some action
}
Exibindo campos de receptor de eventos personalizados no inspetor

Os scripts ReceiverBase usam InspectorField atributos para expor propriedades personalizadas no inspetor. Aqui está um exemplo de Vector3, uma propriedade personalizada com informações de rótulo e dica de ferramenta. Essa propriedade aparecerá como configurável no inspetor quando um GameObject Interativo for selecionado e tiver o tipo de Receptor de Evento associado adicionado.

[InspectorField(Label = "<Property label>",Tooltip = "<Insert tooltip info>",Type = InspectorField.FieldTypes.Vector3)]
public Vector3 EffectOffset = Vector3.zero;

Como usar Interacionável

Criar um botão simples

É possível criar um botão simples adicionando o componente Interativo a um GameObject configurado para receber eventos de entrada. Ele pode ter um colisor sobre ele ou em uma criança para receber entrada. Se estiver usando o Interactable com um GameObjects baseado em interface do usuário do Unity, ele deverá estar no Objeto de Jogo de Tela.

Leve o botão um passo adiante, criando um novo perfil, atribuindo o GameObject em si e criando um novo tema. Além disso, use o evento OnClick para fazer algo acontecer.

Observação

Tornar um botão pressionável requer o PressableButton componente. Além disso, o PhysicalPressEventRouter componente é necessário para canalizar eventos de imprensa para o componente Interativo .

Criando botões de alternância e várias dimensões

Botão de alternância

Para tornar um botão Com capacidade de alternância, altere o Selection Mode campo para digitar Toggle. Na seção Perfis , um novo tema alternado é adicionado para cada perfil que é usado quando o Interativo é ativado.

Enquanto o SelectionMode está definido como Alternar, a caixa marcar IsToggled pode ser usada para definir o valor padrão do controle na inicialização do runtime.

CanSelect significa que o Interativo pode ir desabilitado para ativado , enquanto o CanDeselect significa o inverso.

Exemplo de temas visuais de alternância de perfil

Os desenvolvedores podem utilizar as SetToggled interfaces e IsToggled para obter/definir o estado de alternância de um Interativo por meio de código.

// If using SelectionMode = Toggle (i.e Dimensions == 2)

// Make the Interactable selected and toggled on
myInteractable.IsToggled = true;

// Get whether the Interactable is selected or not
bool isSelected = myInteractable.IsToggled;
Alternar coleção de botões

É comum ter uma lista de botões de alternância em que apenas um pode estar ativo a qualquer momento, também conhecido como um conjunto radial ou botões de opção etc.

Use o InteractableToggleCollection componente para habilitar essa funcionalidade. Esse controle garante que apenas um Interativo seja ativado a qualquer momento. O RadialSet (Assets/MRTK/SDK/Features/UX/Interactable/Prefabs/RadialSet.prefab) também é um ótimo ponto de partida pronto para uso.

Para criar um grupo de botões radial personalizado:

  1. Criar vários botões/GameObjects interacionáveis
  2. Definir cada Interativo com SelectionMode = Alternância, CanSelect = true e CanDeselect = false
  3. Crie um GameObject pai vazio em todos os Interativos e adicione o componente InteractableToggleCollection
  4. Adicionar todos os Interativos ao ToggleList no InteractableToggleCollection
  5. Defina a propriedade InteractableToggleCollection.CurrentIndex para determinar qual botão é selecionado por padrão no início
Alternar coleção

Botão multidimensional

O modo de seleção de várias dimensões é usado para criar botões sequenciais ou um botão que tem mais de duas etapas, como controlar a velocidade com três valores, Rápido (1x), Mais Rápido (2x) ou Mais Rápido (3x).

Com dimensões sendo um valor numérico, até 9 temas podem ser adicionados para controlar o rótulo de texto ou textura do botão para cada configuração de velocidade, usando um tema diferente para cada etapa.

Cada evento de clique avançará o DimensionIndex em 1 no runtime até que o Dimensions valor seja atingido. Em seguida, o ciclo será redefinido para 0.

Exemplo de perfil multidimensional

Os desenvolvedores podem avaliar o DimensionIndex para determinar qual dimensão está ativa no momento.

// If using SelectionMode = Multi-dimension (i.e Dimensions >= 3)

//Access the current DimensionIndex
int currentDimension = myInteractable.CurrentDimension;

//Set the current DimensionIndex to 2
myInteractable.CurrentDimension = 2;

// Promote Dimension to next level
myInteractable.IncreaseDimension();

Criar interativo em runtime

Pode ser facilmente adicionado a qualquer GameObject no runtime. O exemplo a seguir demonstra como atribuir um perfil com um tema visual.

var interactableObject = GameObject.CreatePrimitive(PrimitiveType.Cylinder);
var interactable = interactableObject.AddComponent<Interactable>();

// Get the default configuration for the Theme engine InteractableColorTheme
var newThemeType = ThemeDefinition.GetDefaultThemeDefinition<InteractableColorTheme>().Value;

// Define a color for every state in our Default Interactable States
newThemeType.StateProperties[0].Values = new List<ThemePropertyValue>()
{
    new ThemePropertyValue() { Color = Color.black},  // Default
    new ThemePropertyValue() { Color = Color.black}, // Focus
    new ThemePropertyValue() { Color = Random.ColorHSV()},   // Pressed
    new ThemePropertyValue() { Color = Color.black},   // Disabled
};

interactable.Profiles = new List<InteractableProfileItem>()
{
    new InteractableProfileItem()
    {
        Themes = new List<Theme>()
        {
            Interactable.GetDefaultThemeAsset(new List<ThemeDefinition>() { newThemeType })
        },
        Target = interactableObject,
    },
};

// Force the Interactable to be clicked
interactable.TriggerOnClick()

Eventos interacionáveis por meio de código

É possível adicionar uma ação ao evento base Interactable.OnClick por meio do código com o exemplo a seguir.

public static void AddOnClick(Interactable interactable)
{
    interactable.OnClick.AddListener(() => Debug.Log("Interactable clicked"));
}

Use a Interactable.AddReceiver<T>() função para adicionar receptores de evento dinamicamente em runtime.

O código de exemplo abaixo demonstra como adicionar um InteractableOnFocusReceiver, que escuta a entrada/saída do foco e, além disso, define o código de ação a ser executado quando as instâncias de evento são acionadas.

public static void AddFocusEvents(Interactable interactable)
{
    var onFocusReceiver = interactable.AddReceiver<InteractableOnFocusReceiver>();

    onFocusReceiver.OnFocusOn.AddListener(() => Debug.Log("Focus on"));
    onFocusReceiver.OnFocusOff.AddListener(() => Debug.Log("Focus off"));
}

O código de exemplo abaixo demonstra como adicionar um InteractableOnToggleReceiver, que escuta transições de estado selecionadas/desmarcadas em interativos de alternância e, além disso, define o código de ação a ser executado quando as instâncias de evento são acionadas.

public static void AddToggleEvents(Interactable interactable)
{
    var toggleReceiver = interactable.AddReceiver<InteractableOnToggleReceiver>();

    // Make the interactable have toggle capability, from code.
    // In the gui editor it's much easier
    interactable.Dimensions = 2;
    interactable.CanSelect = true;
    interactable.CanDeselect  = true;

    toggleReceiver.OnSelect.AddListener(() => Debug.Log("Toggle selected"));
    toggleReceiver.OnDeselect.AddListener(() => Debug.Log("Toggle un-selected"));
}

Confira também