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:
O modelo deve conter malhas de collider.
O
GLTFContainerdeve ser configurado para usar a geometria visível com máscaras de colisão.Adicione um componente MeshCollider.
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. Ocampodirectionespera umVector3que 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. Ocampodirectionespera umraycastSystem.registerGlobalDirectionRaycast()que descreve a direção global.raycastSystem.registerGlobalTargetRaycast() alvo global : cria um raycast com uma direção definida por umaposition. Odirectionespera umtargetque 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 umatargetEntity
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 numberpara definir o comprimento com o qual este raio será traçado. Se não for definido, o padrão é 16 metros.: queryType RaycastQueryTypevalor 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. VejaColliderLayer.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.
campoAo definir a direção com uma direção local ou global, oque descreva um vetor relativo à entidade e sua rotação (por exemplo,.campo padrão é
Ao definir a direção com um alvo global, oAo 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 umaAo 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 é.
📔 Nota: O . Se não for definido, a camada padrão usada é campo padrão é a entidade raiz da cena, localizada em 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 interval função na biblioteca Utils) para executar consultas de raycast em um intervalo mais espaçado e regular, veja.
raycasting recorrente
📔 Nota: targetEntity: targetEntity,, queryType e ColliderLayer devem ser importados via
raycastSystem
Veja Imports para como lidar com estes facilmente.
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.globalOrigincampo: A posição onde o raio foi originado, relativa à cena.espera um.: A direção global para a qual o raio estava apontando, como umhitsvalor 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 Stringposition: 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 QuaternionA 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)
📔 Notaconsole.log('nenhuma entidade atingida')
: 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.
📔 Nota: O . Se não for definido, a camada padrão usada é continuous: true,
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
💡 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 na 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.
💡 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
📔 Notaconsole.log(raycastResult)
: 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.
💡 Tipconsole.log("entidade: ", entity) : Neste exemplo usamos o botão primário (E) para acionar o raycast. Não usamos o botão do pointer (clique esquerdo) porque clicar e arrastar também altera o ângulo da câmera por padrão. Se você quiser evitar girar a câmera enquanto arrasta, pode usar uma Virtual Camera
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 pelolocalDirectionespera umcampo, como umque 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 pelolocalDirectionespera umcampo, como umglobalDirectionGLOBAL_TARGETAo definir a direção com um alvo global, olocalDirectionespera 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 peloque 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 RaycastQueryTyperetorna 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.
📔 Nota: O . Se não for definido, a camada padrão usada é campo padrão é a entidade raiz da cena, localizada em 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 interval função na biblioteca Utils) para executar consultas de raycast em um intervalo mais espaçado e regular, veja.
: 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
📔 NotaComponente de resultados de Raycast : A forma mais fácil de lidar com resultados de raycast é usarraycastEventSystem, e registrar uma função callback como parte da mesma declaração que cria o raio. O componente RaycastResult
é 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.globalOrigincampo: A posição onde o raio foi originado, relativa à cena.espera um.: A direção global para a qual o raio estava apontando, como umhitsvalor 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 Stringposition: 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 QuaternionA 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.
📔 Notaconsole.log(result.hits)
: 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