Eventos baseados em Systems

Aprenda a lidar com cliques do utilizador na sua cena.

Se a sua Scene tiver várias entities semelhantes que são todas ativadas usando a mesma lógica, você pode escrever um único system para iterar sobre todas elas e descrever esse comportamento apenas uma vez. Esta também é a abordagem mais performática e mais orientada a dados abordagem.

Você também pode usar um system para detectar eventos globais de input, para que a scene reaja sempre que uma tecla for pressionada, sem considerar para onde o cursor do player está apontando.

Se tudo o que você quer fazer é clicar ou pressionar um botão em uma única entity para ativá-la, a forma mais fácil é usar o Register a callback abordagem.

Para definir uma custom logic mais específica, talvez você queira lidar com os dados brutos e usar o Advanced abordagem.

Para que uma entity seja interativa, ela deve ter um collider. Veja obstacles para mais detalhes. Isso também se aplica a eventos de clique por proximidade.

Usando um system

Verifique eventos de botão executando uma das funções auxiliares no namespace inputSystem em cada tick dentro de um system.

Por exemplo, o system a seguir usa a inputSystem.isTriggered() function para verificar se o pointer foi clicado. Em cada tick, ele verifica se o botão foi pressionado. Se inputSystem.isTriggered() retorna true, o system executa uma custom logic em resposta.

engine.addSystem(() => {
	if (inputSystem.isTriggered(InputAction.IA_POINTER, PointerEventType.PET_DOWN)) {
		// Logic em resposta à pressão do botão
	}
})

As seguintes funções auxiliares estão disponíveis no namespace inputSystem e podem ser chamadas de forma semelhante em cada tick:

  • inputSystem.isTriggered: Retorna true se uma input action ocorreu desde o último tick.

  • inputSystem.isPressed: Retorna true se uma input estiver atualmente sendo pressionada. Ela retornará true em cada tick até que o botão seja solto novamente.

  • inputSystem.getInputCommand: Retorna um objeto com dados sobre a input action.

Consulte as seções abaixo para mais detalhes sobre cada uma.

Ao lidar com eventos de botão em uma entity, sempre forneça feedback ao player, para que ele saiba que uma entity pode ser interagida. Se você adicionar um PointerEvents component a uma entity, os players verão uma hint enquanto passam o cursor sobre essa entity. Veja Show feedback para aprender como você pode adicionar hover hints em entities interativas.

Eventos globais de input

Use inputSystem.isTriggered para detectar eventos de botão pressionado e botão solto em qualquer uma das inputs monitoradas pelo SDK.

O exemplo acima verifica se o botão foi pressionado, independentemente de onde o pointer esteja ou de quais entities possam estar em seu caminho.

inputSystem.isTriggered() retorna true somente se o botão indicado tiver sido pressionado no tick atual do game loop. Se o botão não foi pressionado, ou já estava pressionado no tick anterior, ele retorna false.

O inputSystem.isTriggered function aceita os seguintes argumentos obrigatórios:

  • InputAction: Qual input ouvir, como um valor de InputAction . Veja Pointer buttons para opções suportadas.

  • PointerEventType: Que tipo de evento ouvir, como um valor de PointerEventType . Veja Tipos de eventos de pointer para opções suportadas.

Ativar uma entity

Para detectar eventos de botão enquanto aponta para uma entity específica, passe um terceiro argumento opcional para inputSystem.isTriggered para especificar qual entity verificar.

O exemplo acima verifica em cada tick se uma única entity codificada foi pressionada com o botão do pointer (botão esquerdo do mouse).

O inputSystem.isTriggered função recebe os seguintes argumentos:

  • InputAction: Qual input ouvir, como um valor de InputAction . Veja Pointer buttons para opções suportadas.

  • PointerEventType: Que tipo de evento ouvir, como um valor de PointerEventType . Veja Tipos de eventos de pointer para opções suportadas.

  • Entity (opcional): Qual entity verificar para esses eventos. Se nenhum valor for fornecido, ele verificará pressões globais do botão, independentemente de onde o cursor do player estivesse apontando.

Observe que, neste exemplo, também estamos adicionando um PointerEvents component à entity com a qual queremos interagir. Esta etapa é necessária; sem esse component a entity não poderá ser detectada por nenhuma das funções de inputSystem. veja Show Feedback para mais detalhes sobre o PointerEvents component.

Se houver várias entities com as quais o player possa interagir da mesma maneira, considere usar inputSystem.getInputCommand. Este command retorna informações sobre um command de clique global, incluindo o ID da entity; você pode usar isso para executar uma única function que possa lidar com todas elas.

Consulte Dados da ação de input para mais informações. Este método também fornece dados mais detalhados sobre o hit do evento de pointer.

Botão solto do input

Você também pode verificar eventos de pointer solto de maneira muito semelhante, usando PointerEventType.PET_UP.

Verifique botões pressionados

Verifique se um botão está atualmente sendo pressionado usando inputSystem.isPressed() dentro de um system.

inputSystem.isPressed() retorna true se o botão estiver atualmente sendo mantido pressionado, não importa quando o botão foi pressionado e não importa para onde o cursor do player esteja apontando. Caso contrário, ele retorna false.

O inputSystem.isPressed function aceita um único argumento:

  • InputAction: Qual input ouvir, como um valor de InputAction . Veja Pointer buttons para opções suportadas.

Lidar com várias entities

Se sua scene tiver várias entities afetadas por eventos de pointer da mesma forma, faz sentido escrever um system que itere sobre todas elas.

Este exemplo usa um consulta de componentes para iterar sobre todas as entities com um PointerEvents component. Em seguida, ele verifica cada uma dessas entities com inputSystem.isTriggered, iterando sobre elas uma por uma. Se uma input action for detectada em qualquer uma dessas entities, ele executa a custom logic.

Em vez de iterar sobre todos os as entities com um PointerEvents component em um único system, você pode querer escrever systems diferentes para lidar com entities que devem se comportar de maneiras diferentes. A abordagem recomendada é marcar diferentes tipos de entities com custom components, e iterar sobre elas em systems separados.

Este exemplo tem um system que itera sobre todas as entities que têm um custom component chamado IsDoor e outro que itera sobre todas as entities que têm um custom component chamado isGem. Em ambos os systems, ele verifica cada entity correspondente para ver se elas foram ativadas com o botão do pointer.

Essa forma de organizar o código da sua scene é muito orientada a dados e deve resultar em um uso muito eficiente dos recursos de memória.

## Vários botões em uma entity

Usando helpers como pointerEventsSystem.onPointerDown, cada entity fica limitada a ter um único tipo de evento. Você não pode registrar dois eventos de botão diferentes. Você não tem essa limitação ao usar eventos baseados em system. Por exemplo, aqui está uma entity com a qual os players podem interagir tanto pressionando E quanto F, cada um disparando funcionalidades diferentes.

Show feedback

Para exibir hints de UI enquanto aponta para uma entity, use as propriedades no PointerEvents component.

Sempre que o cursor do player apontar para os colliders nesta entity, a UI exibirá uma hover hint para indicar que a entity pode ser interagida. Veja as seções abaixo para detalhes sobre o que você pode configurar.

O PointerEvents component requer pelo menos uma definição de evento de pointer. Cada definição de evento de pointer pode ser configurada com o seguinte:

  • eventType: Que tipo de evento ouvir, como um valor de PointerEventType . Veja Tipos de eventos de pointer para opções suportadas.

  • eventInfo: Um objeto que pode conter os seguintes campos:

    • button (obrigatório): Qual input ouvir, como um valor de InputAction . Veja Pointer buttons para opções suportadas.

    • hoverText (opcional): Qual string exibir na dica de hover feedback. "Interact" por padrão.

    • hideFeedback (opcional): Se true, oculta tanto a dica de hover quanto o destaque de borda para esta entidade. false por padrão.

    • showHighlight (opcional): Se true, os jogadores verão o destaque de borda ao passar o cursor sobre a entidade. true por padrão. Este valor só é considerado se hideFeedback for false.

    • maxDistance (opcional): Mostra feedback apenas quando o player estiver mais perto do que uma certa distância da entity. O padrão é 10 metros.

Um único PointerEvents component pode conter várias definições de eventos de pointer, que podem detectar eventos diferentes para botões diferentes. Cada entity pode ter apenas um um PointerEvents component, mas esse component pode incluir vários objetos em seu pointerEvents array, um para cada evento a ser tratado.

Os players verão vários rótulos, um para cada evento de pointer, exibidos radialmente ao redor do cursor.

O exemplo abaixo combina o uso de PointerEvents para mostrar hover hints, junto com um system que realmente trata a ação do player com custom logic.

Feedback de Hover

Quando um player passa o cursor sobre um item com um PointerEvents component, ele vê:

  • Um destaque de borda na entidade

  • Uma dica de hover próxima ao cursor com um ícone para o botão que eles precisam pressionar e uma string que diz "Interact".

Esses elementos podem ser alternados e personalizados.

O hover feedback na UI exibe um ícone diferente dependendo do input selecionado no button field. No PC, ele exibe um ícone com um E para InputAction.IA_PRIMARY, um F para InputAction.IA_SECONDARY, e um mouse para InputAction.IA_POINTER.

Altere a string alterando o hoverText valor. Mantenha essa string curta, para que seja rápida de ler e não fique muito intrusiva na tela.

No exemplo acima, o evento de pointer inclui um valor para hoverText. Este campo define a string a ser exibida na UI enquanto o player aponta para a entity. Por padrão, essa string é Interact.

💡 Dica: A hoverText string deve descrever a ação que acontece ao interagir. Por exemplo Open, Activate, Grab, Selecione. Essas strings devem ser o mais curtas possível, para evitar roubar muita atenção do player.

Se uma entity tiver vários eventos de pointer, os hover hints de cada um deles são exibidos radialmente ao redor do cursor.

O hoverText de um .UP evento de pointer é exibido apenas enquanto o player já estiver mantendo pressionada a tecla correspondente e apontando para a entity.

Se uma entity tiver tanto um DOWN evento de pointer e um evento UP de pointer, a hint da ação DOWN é mostrada enquanto o botão não está sendo pressionado. A hint muda para a do UP evento somente quando o botão é pressionado e permanece pressionado.

Para ocultar a dica de hover, mas manter o destaque de borda, defina o valor de hoverText como "".

Para ocultar o destaque de borda, mas manter a dica de hover, defina showHighlight // desenhar UI false.

Para ocultar tanto a dica de hover quanto o destaque de borda, defina o hideFeedback para um true. Ao fazer isso, o cursor não mostra nenhum ícone, texto ou destaque de borda. Você também pode simplesmente remover o PointerEvents component da entity.

Distância máxima

Algumas entities podem ser intencionalmente interativas apenas em curta distância. Se um player estiver muito longe de uma entity, a hover hint não será exibida ao lado do cursor.

Por padrão, as entities só podem ser clicadas quando o player está a curta distância da entity, com uma distância máxima de 10 metros. Você pode alterar a distância máxima definindo a maxDistance property de um evento de pointer.

O exemplo acima define a distância máxima para hover hints como 6 metros. Certifique-se de que a lógica para lidar com as input actions também siga as mesmas regras. Veja Dados da ação de input para saber como obter a distância de uma input action.

Hints customizados avançados

O PointerEvents component adiciona facilmente hints de UI quando o cursor do player começa a passar sobre uma entity. Em geral, é algo bom que os hints se comportem de forma consistente com o que os players estão acostumados a ver em outras scenes de Decentraland. No entanto, em alguns casos você pode querer sinalizar que algo é interativo de uma forma customizada. Por exemplo, você poderia tocar um som sutil quando o player começar a passar o cursor sobre a entity. Você também poderia mostrar um destaque brilhante ao redor da entity enquanto estiver em hover e ocultá-lo quando não estiver mais em hover. Isso também pode ser usado para mecânicas de gameplay específicas.

Use o inputSystem.isTriggered() function junto com os PointerEventType.PET_HOVER_ENTER e PointerEventType.PET_HOVER_LEAVE events para executar comportamentos customizados sempre que o cursor do player começar a apontar para o collider da entity e sempre que o cursor parar de apontar para ele.

O exemplo abaixo aumenta entities para um tamanho de 1.5 quando o cursor começa a apontar para o collider delas, e as redefine para um tamanho de 1 quando o cursor sai delas.

Eventos de proximidade

Verifique pressões de botão por proximidade independentemente de onde o cursor esteja apontando usando PET_DOWN em uma entity que usa InteractionType.PROXIMITY. Defina o interactionType campo em eventInfo para marcar o evento como baseado em proximidade.

Consulte Proximity Events para mais detalhes, incluindo as funções auxiliares disponíveis.

Usando helpers como pointerEventsSystem.onProximityDown, cada entity fica limitada a ter um único tipo de evento. Você não pode registrar dois eventos de botão diferentes, ou até mesmo uma combinação de eventos de proximidade e pointer usando o mesmo botão. Você não tem essa limitação ao usar eventos baseados em system. Por exemplo, aqui está uma entity com a qual é possível interagir tanto apontando um cursor e pressionando E, quanto andando perto dela e pressionando E sem apontar para ela com o cursor.

Você também pode usar inputSystem.isTriggered() com o PET_PROXIMITY_ENTER e PET_PROXIMITY_LEAVE event types para reagir quando um player entra ou sai da faixa de proximidade de uma entity. Isso é útil para exibir feedback extra para entities ativadas por proximidade, por exemplo tocar um som ou uma animação.

Para isso funcionar, a entity deve ter um PointerEvents component com o correspondente tipo de evento de proximidade definido.

Dados da ação de input

Busque dados de uma ação de input, como o botão que foi pressionado, a entidade que foi atingida, a direção e o comprimento do ray, etc. Veja (Ver documentação) para uma descrição de todos os dados disponíveis.

Para buscar esses dados, use inputSystem.getInputCommand. Esta function retorna a estrutura de dados completa com dados sobre o evento de input.

Se não houver nenhuma input action que corresponda à consulta, então inputSystem.getInputCommand retorna undefined. Certifique-se de tratar esse cenário na sua lógica.

Distância máxima do clique

Para impor uma distância máxima, de modo que uma entity só possa ser clicada em curta distância, busque hit.length property dos dados do evento.

Diferentes meshes dentro de um model

Muitas vezes, .glTF models 3D são compostos por vários meshes, cada um com um nome interno individual. Todos os eventos de botão incluem a informação de qual mesh específica foi clicada, então você pode usar essa informação para acionar diferentes comportamentos de clique em cada caso.

Para ver como as meshes dentro do model são nomeadas, você deve abrir o model 3D com uma ferramenta de edição, como Blender por exemplo.



💡 Dica: Você também pode descobrir o nome do mesh clicado registrando-o e lendo-o no console.

Você acessa a meshName property como parte do hit object, que é retornado pelo evento de clique.

No exemplo abaixo temos um model de casa que inclui um mesh chamado firePlace. Queremos ligar a lareira somente quando a mesh correspondente for clicada.

Atualizado