Raycasting

Use raycasting para traçar uma linha no espaço e consultar colisões com entidades na cena.

Raycasting é uma ferramenta fundamental no desenvolvimento de jogos. Com raycasting, você pode traçar uma linha imaginária no espaço e consultar se alguma entidade é intersectada por essa linha. Isso é útil para calcular linhas de visão, trajetórias de projéteis, algoritmos de pathfinding e muitas outras aplicações.

Quando um jogador pressiona o botão do ponteiro, ou o botão primário ou secundário, um raio é traçado a partir da posição do jogador na direção em que ele está olhando, veja eventos de botão para mais detalhes sobre isso. Este documento cobre como traçar um raio invisível a partir de qualquer posição e direção arbitrárias, independente das ações do jogador, que você pode usar em muitos outros cenários.

Por favor note que raycasts só atingem objetos com colliders. Então, se você quiser detectar acertos de raio contra um modelo 3D, ou:

Também é uma boa prática atribuir camadas de colisão personalizadas a modelos 3D, para que os raios só precisem calcular colisões contra as entidades relevantes, em vez de contra tudo que possui um collider.

Criar um raio

Todos os raios têm um ponto de origem e uma direção. O ponto de origem é baseado na posição de uma entidade, usando os valores do componente Transform da entidade. A direção de um raio pode ser definida de 4 maneiras diferentes:

  • local: Uma direção relativa à direção frontal da entidade, também afetada pela transformação de quaisquer entidades pai. Isso é útil para detectar obstáculos à frente de veículos levando em conta sua orientação.

  • global: Ignora a rotação da entidade, e aponta para uma direção como se a rotação da entidade fosse 0. Isso é útil, por exemplo, para sempre apontar para baixo.

  • alvo global: Traça uma linha entre a posição da entidade e uma posição alvo global na cena. Ignora a rotação da entidade. Útil, por exemplo, para criar jogos de defesa de torre, onde a torre pode apontar para uma coordenada específica no espaço.

  • entidade alvo: Traça uma linha entre a posição da entidade e a posição de uma segunda entidade alvo. Ignora a rotação de ambas as entidades.

O código a seguir cria um raycast com direção local:

const myEntity = engine.addEntity()
Transform.create(myEntity, {
  position: Vector3.create(4, 1, 4),
})

raycastSystem.registerLocalDirectionRaycast(
  {
    entity: myEntity,
    opts: { direction: Vector3.Forward() },
  },
  function (raycastResult) {
    // função de callback
  }
)

Use as seguintes funções para criar raycasts fornecendo a direção de diferentes maneiras:

  • raycastSystem.registerLocalDirectionRaycast(): cria um raycast com uma local direção. O campo direction espera um Vector3 que descreva um vetor relativo à entidade e sua rotação (por exemplo, Vector3.Forward()

  • acabaria usando o vetor forward do transform da entidade): cria um raycast com uma global direção. O campo direction espera um raycastSystem.registerGlobalDirectionRaycast()

  • que descreve a direção global.raycastSystem.registerGlobalTargetRaycast() alvo global : cria um raycast com uma direção definida por uma position. O direction espera um target

  • que descreve uma posição global na cena.raycastSystem.registerTargetEntityRaycast() entidade alvo : cria um raycast com uma direção definida por uma : cria um raycast com uma direção definida em direção a uma targetEntity

o campo espera uma referência a uma entidade, a posição dessa entidade será usada como o alvo do raio.

  • Os seguintes campos opcionais estão disponíveis ao criar um raio com qualquer um dos métodos acima:: maxDistance number

  • para definir o comprimento com o qual este raio será traçado. Se não for definido, o padrão é 16 metros.: queryType RaycastQueryType

    • valor enum, para definir se o raio retornará todas as entidades atingidas ou apenas a primeira. As seguintes opções estão disponíveis:: RaycastQueryType.RQT_HIT_FIRST (padrão)

    • retorna apenas a primeira entidade atingida, começando do ponto de origem.RaycastQueryType.RQT_QUERY_ALL

  • : retorna todas as entidades atingidas, desde a origem até a distância máxima do raio.originOffset : Em vez de iniciar o raycast na posição de origem da entidade, adicione um deslocamento para iniciar a consulta a partir de uma posição relativa. Você pode, por exemplo, usar um pequeno deslocamento para evitar que o raio colida com o próprio collider da entidade. Se não for definido, o padrão é.

  • collisionMaskVector3.Zero() camadas de colisão: Detectar apenas colisões com certas camadas de colisão. Use isso juntamente com uma camada de colisão personalizada, ou para detectar apenas a camada de physics ou pointer events. Veja ColliderLayer.CL_PHYSICS.

  • . Se não for definido, a camada padrão usada écontinuous

  • : Se true, continuará executando uma consulta de raycast em cada frame. Se false, o raio só será usado no frame atual. Se não for definido, o padrão é false. campo Ao definir a direção com uma direção local ou global, o que descreva um vetor relativo à entidade e sua rotação (por exemplo,.

  • campo padrão é Ao definir a direção com um alvo global, o Ao definir a direção com uma direção local ou global, o : Em vez de iniciar o raycast na posição de origem da entidade, adicione um deslocamento para iniciar a consulta a partir de uma posição relativa. Você pode, por exemplo, usar um pequeno deslocamento para evitar que o raio colida com o próprio collider da entidade. Se não for definido, o padrão é.

  • globalTarget : cria um raycast com uma direção definida em direção a uma Ao definir a direção com um alvo de entidade, o : Em vez de iniciar o raycast na posição de origem da entidade, adicione um deslocamento para iniciar a consulta a partir de uma posição relativa. Você pode, por exemplo, usar um pequeno deslocamento para evitar que o raio colida com o próprio collider da entidade. Se não for definido, o padrão é.

circle-exclamation

raycasting recorrente

circle-exclamation

import { raycastSystem, RaycastQueryType, ColliderLayer } from "@dcl/sdk/ecs"

Resultado do raycast

  • A função callback que lida com o raycast recebe um objeto contendo dados sobre o próprio raio e quaisquer entidades que foram atingidas.globalOrigin

  • campo: A posição onde o raio foi originado, relativa à cena. espera um.

  • : A direção global para a qual o raio estava apontando, como umhits valor enum, para definir se o raio retornará todas as entidades atingidas ou apenas a primeira. As seguintes opções estão disponíveis:: Um array com um objeto para cada entidade que foi atingida. Se não houve entidades atingidas, esse array está vazio. Se o raycast usou

, esse array conterá apenas um objeto. : A direção global para a qual o raio estava apontando, como um Cada objeto no

  • array inclui:entityId

  • : Número de Id da entidade que foi atingida pelo raio.: meshName String

  • position: espera um com o nome interno da malha específica no modelo 3D que foi atingida. Isso é útil quando um modelo 3D é composto por múltiplas meshes.

  • para a posição onde o raio intersectou com a entidade atingida (relativa à cena)length

  • : Comprimento do raio desde sua origem até a posição onde o acerto contra a entidade ocorreu.: normalHit Quaternion

  • A função callback que lida com o raycast recebe um objeto contendo dados sobre o próprio raio e quaisquer entidades que foram atingidas.: espera um para o ângulo da normal do acerto no espaço mundial.

  • campo: A posição onde o raio foi originado, relativa à cena. espera um.

para a posição onde o raio se origina (relativa à cena)

circle-exclamation

: Você pode obter um resultado de raycast de um acerto em uma entidade em uma cena diferente.

Lidar com entidades atingidas array inclui: Quando você obtém um resultado de raycast que atingiu uma entidade, você pode usar o para interagir com a entidade e seus componentes. Uma entidade énada mais do que um número array inclui: , então o valor em si pode ser interpretado como um Entity

Collision layers

console.log(transform.position) collisionMask É uma boa prática verificar colisões apenas contra entidades relevantes, para tornar a cena mais performática. O camadas de colisãocampo permite listar apenas camadas de colisão específicas, que podem incluir a camada de physics (que bloqueia o movimento do jogador), a camada de pointer (que é usada para eventos de pointer), e 8 camadas personalizadas que você pode atribuir livremente conforme suas necessidades. Veja

. Por padrão, todas as camadas são detectadas. collisionMask Por padrão, o ColliderLayer.CL_POINTER e ColliderLayer.CL_PHYSICScampo é configurado para responder a ambas as camadas | . Você pode alterar esse valor para listar apenas uma delas, ou para incluir camadas personalizadas. Use o

log(raycastResult.hits)

Raycasting recorrente targetEntity: targetEntity,Ao usar as funções do . Se não for definido, a camada padrão usada é , o comportamento padrão é criar um único raio, que consultará colisões uma vez. Como alternativa, você pode definir o true campo para

para executar uma consulta e a função callback em cada tick do loop do jogo.

circle-exclamation

essa propriedade deve ser usada com cautela, pois executar uma consulta de raycast em cada frame pode ser muito caro para o desempenho. Quando não for mais necessário, remova quaisquer raycasts recorrentes. Para isso, você deve usar.

raycastSystem.removeRaycasterEntity(myEntity) essa propriedade deve ser usada com cautela, pois executar uma consulta de raycast em cada frame pode ser muito caro para o desempenho. Quando possível, use um sistema (ou a Quando possível, use um sistema (ou a

MeshCollider.setBox(cubeEntity) O exemplo acima executa um raycast recorrente a cada 0,1 segundos. Ele usa um componente timer e a dt

circle-info

💡 Tippropriedade do sistema para cronometrar isso de forma uniforme. Inclui também um cubo que oscila para cima e para baixo, controlado por outro sistema, para mover-se dentro e fora do caminho do raio. essa propriedade deve ser usada com cautela, pois executar uma consulta de raycast em cada frame pode ser muito caro para o desempenho. Quando possível, use um sistema (ou a : Use a função naarrow-up-right biblioteca SDK Utils

para uma forma mais simples de executar uma função em um intervalo fixo.

Raycasts via um sistema Outra forma de realizar raycasts recorrentes é executá-los dentro da função recorrente de um sistema. Isso permite que você tenha muito mais controle sobre quando e como eles funcionam. Em vez de registrar uma função de callback, você pode executar uma consulta de raycast com raycastSystem.registerRaycast

e então verificar os dados retornados por essa operação, tudo dentro da função do sistema.

if (result) // faça algo

Colidir com o jogador Você não pode atingir diretamente o avatar do jogador ou o de outros jogadores com um raio, mas o que você pode fazer como solução alternativa é posicionar uma entidade invisível ocupando o mesmo espaço que um jogador usando ocomponente AvatarAttach

, e verificar colisões com esse cubo.

Raycasts a partir do jogador Para traçar um raio a partir da posição do jogador na direção para a qual a câmera está apontando, você pode traçar um raio usando a câmera ou o avatar.

circle-info

💡 TipEntidades reservadas : Para a maioria dos casos, talvez seja melhor usar Eventos de pointer

em vez de raycasts. O exemplo a seguir traça um raio a partir da posição da câmera do jogador para frente, usando a entidade engine.CameraEntity

circle-exclamation

: Tenha em mente que em 3ª pessoa o cursor pode no futuro não se comportar da mesma forma que em 1ª pessoa. É recomendado usar isso apenas se o jogador estiver em 1ª pessoa.

Raycast a partir da posição do cursor

Você também pode traçar um raio a partir da posição do cursor do jogador para o mundo 3D. Isso pode ser usado para arrastar objetos, jogos de tiro, etc.

para manter o ângulo da câmera fixo.

Sintaxe avançada

Criar um componente Raycast

Um componente Raycast descreve o raio invisível que é usado para consultar entidades intersectantes. O raio é traçado começando na posição da entidade, conforme definido pelo componente Transform e afetado pelo de quaisquer entidades pai. A direção pode ser definida de várias maneiras,

  • campoRaios são definidos usando os seguintes dados: $case : Um objeto que contém um $case:

    • campo para selecionar o tipo de direção, e um campo adicional que dependerá desse tipo, que determina essa direção. Os seguintes valores são aceitos paraLOCAL_DIRECTION : Uma direção relativa à direção frontal da entidade, também afetada pela transformação de quaisquer entidades pai. Isso é útil para detectar obstáculos à frente de veículos levando em conta sua orientação. A rotação é definida pelo localDirection espera um campo, como um

    • que descreve uma rotação.GLOBAL_DIRECTION : Ignora a rotação da entidade, e aponta para uma direção como se a rotação da entidade fosse 0. Isso é útil, por exemplo, para sempre apontar para baixo. A rotação é definida pelo localDirection espera um campo, como um

    • globalDirectionGLOBAL_TARGET Ao definir a direção com um alvo global, o localDirection espera um : Traça uma linha entre a posição da entidade e uma posição alvo global na cena. Ignora a rotação da entidade. Útil para criar jogos de defesa de torre, onde a torre pode apontar para uma coordenada específica no espaço. O alvo é definido pelo

    • que descreve a posição global.TARGET_ENTITY : cria um raycast com uma direção definida em direção a uma : Traça uma linha entre a posição da entidade e a posição de uma segunda entidade alvo. Ignora a rotação de ambas as entidades. O alvo é definido pelo

  • Os seguintes campos opcionais estão disponíveis ao criar um raio com qualquer um dos métodos acima:: maxDistance campo, contendo uma referência à entidade.

  • para definir o comprimento com o qual este raio será traçado. Se não for definido, o padrão é 16 metros.: queryType RaycastQueryType

    • retorna apenas a primeira entidade atingida, começando do ponto de origem.para definir o comprimento com o qual este raio será traçado.

    • valor enum, para definir se o raio retornará todas as entidades atingidas ou apenas a primeira. As seguintes opções estão disponíveis:RaycastQueryType.RQT_QUERY_ALL

  • collisionMaskVector3.Zero() camadas de colisãocampo permite listar apenas camadas de colisão específicas, que podem incluir a camada de physics (que bloqueia o movimento do jogador), a camada de pointer (que é usada para eventos de pointer), e 8 camadas personalizadas que você pode atribuir livremente conforme suas necessidades. Veja

  • : retorna todas as entidades atingidas, desde a origem até a distância máxima do raio.: retorna apenas a primeira entidade atingida, começando do ponto de origem.

  • . Se não for definido, a camada padrão usada é: Em vez de iniciar o raycast na posição de origem da entidade, adicione um deslocamento para iniciar a consulta a partir de uma posição relativa. Você pode, por exemplo, usar um pequeno deslocamento para evitar que o raio colida com o próprio modelo 3D da entidade.

circle-exclamation

: Se true, continuará executando uma consulta de raycast em cada frame. Se false, o raio só será usado no frame atual. Por padrão este valor é false.

queryType: RaycastQueryType.RQT_HIT_FIRST

continuous: true

queryType: RaycastQueryType.RQT_QUERY_ALL

circle-exclamation

é usado internamente por essa interface, mas também é exposto para permitir lógica personalizada mais avançada. , e registrar uma função callback como parte da mesma declaração que cria o raio. O componente Após criar um componente Raycast, a entidade à qual esse componente é adicionado terá um

O , e registrar uma função callback como parte da mesma declaração que cria o raio. O componente componente. Este componente inclui informações sobre quaisquer acertos do raio. Configure um sistema para verificar esses dados.

  • A função callback que lida com o raycast recebe um objeto contendo dados sobre o próprio raio e quaisquer entidades que foram atingidas.globalOrigin

  • campo: A posição onde o raio foi originado, relativa à cena. espera um.

  • : A direção global para a qual o raio estava apontando, como umhits valor enum, para definir se o raio retornará todas as entidades atingidas ou apenas a primeira. As seguintes opções estão disponíveis:: Um array com um objeto para cada entidade que foi atingida. Se não houve entidades atingidas, esse array está vazio. Se o raycast usou

, esse array conterá apenas um objeto. : A direção global para a qual o raio estava apontando, como um Cada objeto no

  • array inclui:entityId

  • : Número de Id da entidade que foi atingida pelo raio.: meshName String

  • position: espera um com o nome interno da malha específica no modelo 3D que foi atingida. Isso é útil quando um modelo 3D é composto por múltiplas meshes.

  • para a posição onde o raio intersectou com a entidade atingida (relativa à cena)length

  • : Comprimento do raio desde sua origem até a posição onde o acerto contra a entidade ocorreu.: normalHit Quaternion

  • A função callback que lida com o raycast recebe um objeto contendo dados sobre o próprio raio e quaisquer entidades que foram atingidas.: espera um para o ângulo da normal do acerto no espaço mundial.

  • campo: A posição onde o raio foi originado, relativa à cena. espera um.

o componente contém os seguintes dados:

console.log(rayResult.hits) , e registrar uma função callback como parte da mesma declaração que cria o raio. O componente O próximo exemplo mostra como você pode acessar componentes de todas as entidades na cena, usando uma.

circle-exclamation

: Os resultados de um raycast não chegam no mesmo tick do loop do jogo em que você criou o raycast. Os resultados podem levar um ou múltiplos ticks para chegar. camadas de colisãoEm uma cena onde você usa múltiplos tipos de raios para diferentes propósitos (como para pathfinding, checagem de linha de visão, rastreamento de projéteis, etc), você pode querer usar diferentes máscaras de colisão, para evitar calcular colisões irrelevantes.

Atualizado