Interacionável — MRTK2

Interactable

O Interactable componente é um contêiner all-in-one para tornar qualquer objeto facilmente interacionável e responsivo à entrada. O interacionável 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

General Interactable Settings

Estados

Estados são 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 interacionáveis .

States ScriptableObject example in inspector

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

  • Padrão: nada está acontecendo, este é 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 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 o usuário se, por algum motivo, este 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 se destinem a ser controlados por outros componentes. Aqui está uma lista daqueles com funcionalidade interna.

  • Visitado: o Interacionável foi clicado.
  • Alternado: o botão está em um estado alternado ou o índice Dimension é 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 Interacionável.
  • PhysicalTouch: uma entrada de toque é detectada no momento, use NearInteractionTouchable para habilitar.
  • Captura: Uma mão está agarrando os limites do objeto, use NearInteractionGrabbable para habilitar

Enabled

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

A propriedade habilitada de um Interacionável é diferente da propriedade habilitada configurada por meio de GameObject/Component (ou seja, SetActive etc. Desabilitar o MonoBehaviour GameObject ou Interacionável desabilitará tudo na classe da execução, incluindo entrada, temas visuais, eventos etc. Desabilitar por meio Interactable.IsEnabled de desabilitará a maioria dos tratamentos de entrada, redefinindo os estados de entrada relacionados. No entanto, a classe ainda executará cada quadro e receberá eventos de entrada que serão ignorados. Isso é útil para exibir o Interacionável 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 interacionável 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 é falso, o que restringirá a entrada somente a esse colisor interacionável /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 verdadeiro, o comando de voz ativará apenas o Interacionável se e somente se ele já tiver o foco de um ponteiro. Se for falso, o Interacionável atuará como um ouvinte global para o comando de voz selecionado. O comportamento padrão é verdadeiro, pois vários ouvintes globais de fala 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 fileiras associados a um botão. O nível de dimensão atual é rastreado 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 Interacionável é 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 modos de Alternância 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 em 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 verdadeiro quando o componente Interacionável 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 interacionável deixará as propriedades modificadas como está. Neste último caso, o último estado de valores persistirá, a menos que seja alterado por outro componente externo. O padrão é false.

Profile theams

Eventos

Cada componente interacionável tem um evento OnClick que é acionado quando o componente é simplesmente selecionado. No entanto, interacionável pode ser usado para detectar eventos de entrada diferentes apenas do 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.

Events example)

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 se estenda ReceiverBase.

Event Toggle Receiver Example

Exemplo de um receptor de evento de alternância

Receptores interacionáveis

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

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

Interactable reciver

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 maneiras principais:

  1. Estenda a ReceiverBase classe para criar um evento personalizado que aparecerá na lista suspensa de tipos de eventos. Um evento 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 interacionável ou em outro objeto. O ReceiverBaseMonoBehavior referenciará o Interacionável para detectar alterações de estado.

Exemplo de extensão ReceiverBase

A CustomInteractablesReceiver classe exibe informações de status sobre um Interacionável 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 métodos e ReceiverBase.OnClick() os ReceiverBase.OnVoiceCommand() métodos são úteis para criar uma lógica de evento personalizada quando o Interacionável é 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 do ReceiverBase usam InspectorField atributos para expor propriedades personalizadas no inspetor. Aqui está um exemplo do 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 interacionável for selecionado e tiver o tipo de Receptor de Eventos associado adicionado.

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

Como usar Interacionável

Criando um botão simples

É possível criar um botão simples adicionando o componente Interacionável 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 GameObjects baseado em interface do usuário do Unity, ele deverá estar no Canvas GameObject.

Leve o botão um passo adiante, criando um novo perfil, atribuindo o próprio GameObject 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 Interacionável .

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

Botão de alternância

Para tornar um botão capaz de alternar, altere o Selection Mode campo para digitar Toggle. Na seção Perfis , um novo tema alternado é adicionado para cada perfil que é usado quando o Interacionável é ativado.

Embora esteja SelectionMode definida como Alternância, a caixa de seleção IsToggled pode ser usada para definir o valor padrão do controle na inicialização do runtime.

CanSelect significa que o Interacionável pode ir de fora para diante , enquanto o CanDeselect significa o inverso.

Profile Toggle Visual Themes Example

Os desenvolvedores podem utilizar e SetToggledIsToggled interfaces para obter/definir o estado de alternância de um interacionável por meio do 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 interacionável seja ativado a qualquer momento. O RadialSet (Assets/MRTK/SDK/Features/UX/Interactable/Prefabs/RadialSet.prefab) também é um ótimo ponto inicial pronto para uso.

Para criar um grupo de botões radial personalizado:

  1. Criar vários botões/GameObjects interacionáveis
  2. Definir cada Interacionável com SelectionMode = Alternar, CanSelect = true e CanDeselect = false
  3. Criar um GameObject pai vazio em todos os Interacionáveis e adicionar o componente InteractableToggleCollection
  4. Adicionar todos os interacionáveis ao ToggleList no InteractableToggleCollection
  5. Defina a propriedade InteractableToggleCollection.CurrentIndex para determinar qual botão está selecionado por padrão no início
Toggle collection

Botão multidimensional

O modo de seleção de várias dimensões é usado para criar botões sequenciais ou um botão que tenha 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 1 em runtime até que o Dimensions valor seja atingido. Em seguida, o ciclo será redefinido para 0.

Multi-Dimensional profile example

Os desenvolvedores podem avaliar a DimensionIndex dimensão 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 interacionável em runtime

Interacionável 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 eventos dinamicamente no 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 disparadas.

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/dessemarcadas em interacionáveis 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 disparadas.

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"));
}

Veja também