Raycasting

Usa raycasting para trazar una línea en el espacio y consultar colisiones con entidades en la escena.

El raycast (Raycasting) es una herramienta fundamental en el desarrollo de juegos. Con raycasting, puedes trazar una línea imaginaria en el espacio y consultar si alguna entidad es intersectada por esa línea. Esto es útil para calcular líneas de visión, trayectorias de proyectiles, algoritmos de búsqueda de rutas y muchas otras aplicaciones.

Cuando un jugador presiona el botón del puntero, o el botón primario o secundario, se traza un rayo desde la posición del jugador en la dirección hacia la que está mirando, ver eventos de botonesarrow-up-right para más detalles sobre esto. Este documento cubre cómo trazar un rayo invisible desde cualquier posición y dirección arbitrarias, independiente de las acciones del jugador, que puedes usar en muchos otros escenarios.

Ten en cuenta que los raycasts solo impactan objetos con colliders. Así que si deseas detectar impactos de rayos contra un modelo 3D, o bien:

También es buena práctica asignar capas de colisiónarrow-up-right a modelos 3D, de modo que los rayos solo necesiten calcular colisiones contra las entidades relevantes, en lugar de contra todo lo que tenga un collider.

Crear un rayo

Todos los rayos tienen un punto de origen y una dirección. El punto de origen se basa en la posición de una entidad, tomando los valores del componente Transform de la entidad. La dirección de un rayo puede definirse de 4 maneras distintas:

  • local: Una dirección relativa a la dirección frontal de la entidad, afectada también por la transformación de cualquier entidad padre. Esto es útil para detectar obstáculos delante de vehículos respetando su orientación.

  • global: Ignora la rotación de la entidad y apunta en una dirección como si la rotación de la entidad fuera 0. Esto es útil, por ejemplo, para apuntar siempre hacia abajo.

  • objetivo global: Traza una línea entre la posición de la entidad y una posición objetivo global en la escena. Ignora la rotación de la entidad. Útil por ejemplo para crear juegos de defensa de torres; la torreta de cada torre puede apuntar a una coordenada concreta en el espacio.

  • entidad objetivo: Traza una línea entre la posición de la entidad y la posición de una segunda entidad objetivo. Ignora la rotación de ambas entidades.

El siguiente código crea un raycast con una dirección local:

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

raycastSystem.registerLocalDirectionRaycast(
  {
    entity: myEntity,
    opts: { direction: Vector3.Forward() },
  },
  function (raycastResult) {
    // función callback
  }
)

Usa las siguientes funciones para crear raycasts proporcionando la dirección de diferentes maneras:

  • raycastSystem.registerLocalDirectionRaycast(): crea un raycast con una local dirección. El campo direction espera un Vector3 que describa un vector relativo a la entidad y su rotación (por ejemplo, Vector3.Forward()

  • terminaría usando el vector forward del transform de la entidad): crea un raycast con una global dirección. El campo direction espera un raycastSystem.registerGlobalDirectionRaycast()

  • que describe la dirección global.raycastSystem.registerGlobalTargetRaycast() objetivo global : crea un raycast con una dirección definida por una posición. El direction espera un target

  • que describe una posición global en la escena.raycastSystem.registerTargetEntityRaycast() entidad objetivo : crea un raycast con una dirección definida por una : crea un raycast con una dirección definida hacia una targetEntity

el campo espera una referencia a una entidad; la posición de esa entidad se usará como objetivo del rayo.

  • Los siguientes campos opcionales están disponibles al crear un rayo con cualquiera de los métodos anteriores:: maxDistance número

  • para establecer la longitud con la que se trazará este rayo. Si no se establece, el valor por defecto es 16 metros.: queryType RaycastQueryType

    • valor enum, para definir si el rayo devolverá todas las entidades impactadas o solo la primera. Están disponibles las siguientes opciones:: RaycastQueryType.RQT_HIT_FIRST (por defecto)

    • solo devuelve la primera entidad impactada, empezando desde el punto de origen.RaycastQueryType.RQT_QUERY_ALL

  • : devuelve todas las entidades impactadas, desde el origen hasta la distancia máxima del rayo.originOffset : En lugar de iniciar el raycast desde la posición de origen de la entidad, añade un offset para iniciar la consulta desde una posición relativa. Por ejemplo, puedes usar un pequeño offset para evitar que el rayo colisione con el propio collider de la entidad. Si no se establece, el valor por defecto es.

  • propiedad collisionMask,Vector3.Zero() capas de colisiónarrow-up-right: Detectar únicamente colisiones con ciertas capas de colisión. Usa esto junto con una capa de colisión personalizada, o para detectar solo la capa de física o la capa de eventos pointer. Ver ColliderLayer.CL_PHYSICS.

  • . Si no se establece, la capa por defecto usada escontinuous

  • : Si es true, seguirá ejecutando una consulta de raycast en cada frame. Si es false, el rayo solo se usará en el frame actual. Si no se establece, el valor por defecto es false. campo Al establecer la dirección con una dirección local o global, el que describa un vector relativo a la entidad y su rotación (por ejemplo,.

  • campo predetermina a Al establecer la dirección con un objetivo global, el Al establecer la dirección con una dirección local o global, el : En lugar de iniciar el raycast desde la posición de origen de la entidad, añade un offset para iniciar la consulta desde una posición relativa. Por ejemplo, puedes usar un pequeño offset para evitar que el rayo colisione con el propio collider de la entidad. Si no se establece, el valor por defecto es.

  • globalTarget : crea un raycast con una dirección definida hacia una Al establecer la dirección con un objetivo entidad, el : En lugar de iniciar el raycast desde la posición de origen de la entidad, añade un offset para iniciar la consulta desde una posición relativa. Por ejemplo, puedes usar un pequeño offset para evitar que el rayo colisione con el propio collider de la entidad. Si no se establece, el valor por defecto es.

circle-exclamation

raycasting recurrente

circle-exclamation

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

Resultado del raycast

  • La función callback que maneja el raycast recibe un objeto que contiene datos sobre el propio rayo y sobre las entidades que fueron impactadas.globalOrigin

  • campo: La posición donde se originó el rayo, relativa a la escena. espera un.

  • : La dirección global hacia la que apuntaba el rayo, como unhits valor enum, para definir si el rayo devolverá todas las entidades impactadas o solo la primera. Están disponibles las siguientes opciones:: Un arreglo con un objeto por cada entidad que fue impactada. Si no hubo entidades impactadas, este arreglo está vacío. Si el raycast usó

, este arreglo contendrá solo un objeto. : La dirección global hacia la que apuntaba el rayo, como un Cada objeto en el

  • arreglo incluye:entityId

  • : Número Id de la entidad que fue impactada por el rayo.: meshName String

  • posición: espera un con el nombre interno de la malla específica del modelo 3D que fue impactada. Esto es útil cuando un modelo 3D está compuesto por múltiples mallas.

  • para la posición donde el rayo se intersectó con la entidad impactada (relativa a la escena)length

  • : Longitud del rayo desde su origen hasta la posición donde ocurrió el impacto contra la entidad.: normalHit Quaternion

  • La función callback que maneja el raycast recibe un objeto que contiene datos sobre el propio rayo y sobre las entidades que fueron impactadas.: espera un para el ángulo de la normal del impacto en el espacio mundial.

  • campo: La posición donde se originó el rayo, relativa a la escena. espera un.

para la posición donde el rayo se origina (relativa a la escena)

circle-exclamation

: Puedes obtener un resultado de raycast al impactar una entidad en otra escena.

Manejar entidades impactadas arreglo incluye: Cuando obtienes un resultado de raycast que impactó una entidad, puedes usar el para interactuar con la entidad y sus componentes. Una entidad esarrow-up-rightnada más que un número arreglo incluye: , así que el valor en sí mismo puede interpretarse como un Entity

Collision layers

console.log(transform.position) propiedad collisionMask, Es buena práctica comprobar colisiones solo contra entidades relevantes, para hacer la escena más eficiente. El capas de colisiónarrow-up-rightcampo permite listar únicamente capas de colisión específicas, que pueden incluir la capa de física (que bloquea el movimiento del jugador), la capa pointer (que se usa para eventos de puntero) y 8 capas personalizadas que puedes asignar libremente según tus necesidades. Ver

. Por defecto, todas las capas se detectan. propiedad collisionMask, Por defecto, el ColliderLayer.CL_POINTER y ColliderLayer.CL_PHYSICScampo está configurado para responder a ambas capas | . Puedes cambiar este valor para listar solo una de ellas, o para incluir capas personalizadas. Usa el

log(raycastResult.hits)

Raycasting recurrente targetEntity: targetEntity,Al usar las funciones del . Si no se establece, la capa por defecto usada es , el comportamiento por defecto es crear un solo rayo, que consultará colisiones una vez. Como alternativa, puedes establecer el true campo a

para ejecutar una consulta y la función callback en cada tick del bucle del juego.

circle-exclamation

esta propiedad debe usarse con precaución, ya que ejecutar una consulta de raycast en cada frame puede ser muy costoso para el rendimiento. Cuando ya no sean necesarios, elimina los raycasts recurrentes. Para hacerlo, debes usar.

raycastSystem.removeRaycasterEntity(myEntity) esta propiedad debe usarse con precaución, ya que ejecutar una consulta de raycast en cada frame puede ser muy costoso para el rendimiento. Cuando sea posible, usa un sistema (o la Cuando sea posible, usa un sistema (o la

MeshCollider.setBox(cubeEntity) El ejemplo anterior ejecuta un raycast recurrente cada 0.1 segundos. Usa un componente timer y la propiedad dt

circle-info

💡 Consejodel sistema para cronometrar esto de forma uniforme. También incluye un cubo que oscila hacia arriba y hacia abajo, controlado por otro sistema, para moverse dentro y fuera de la trayectoria del rayo. esta propiedad debe usarse con precaución, ya que ejecutar una consulta de raycast en cada frame puede ser muy costoso para el rendimiento. Cuando sea posible, usa un sistema (o la : Usa la función en laarrow-up-right librería SDK Utils

para una forma más sencilla de ejecutar una función en un intervalo fijo.

Raycasts vía un sistema Otra forma de realizar raycasts recurrentes es ejecutarlos desde la función recurrente de un sistema. Esto te permite tener mucho más control sobre cuándo y cómo funcionan. En lugar de registrar una función callback, puedes realizar una consulta de raycast con raycastSystem.registerRaycast

y luego comprobar los datos devueltos por esta operación, todo dentro de la función del sistema.

if (result) // hacer algo

Colisionar con el jugador No puedes impactar directamente el avatar del jugador ni los de otros jugadores con un rayo, pero como solución alternativa puedes posicionar una entidad invisible que ocupe el mismo espacio que un jugador usando elarrow-up-rightcomponente AvatarAttach

, y comprobar colisiones con ese cubo.

Raycasts desde el jugador Para trazar un rayo desde la posición del jugador en la dirección a la que apunta la cámara, puedes trazar un rayo usando la cámara o el avatararrow-up-right.

circle-info

💡 ConsejoEntidades reservadas : En la mayoría de los casos, puede ser mejor usararrow-up-right Eventos Pointer

en lugar de raycasts. El siguiente ejemplo traza un rayo desde la posición de la cámara del jugador hacia adelante, usando la entidad engine.CameraEntity

circle-exclamation

: Ten en cuenta que en tercera persona el cursor podría en el futuro no comportarse igual que en primera persona. Se recomienda usar esto solo si el jugador está en primera persona.

Raycast desde la posición del cursor

También puedes trazar un rayo desde la posición del cursor del jugador hacia el mundo 3D. Esto puede usarse para arrastrar objetos, shooters, etc.

circle-info

💡 Consejoconsole.log("entidad: ", entity) Virtual Cameraarrow-up-right : En este ejemplo usamos el botón primario (E) para activar el raycast. No usamos el botón del puntero (clic izquierdo) porque hacer clic y arrastrar también desplaza el ángulo de la cámara por defecto. Si quieres evitar rotar la cámara mientras arrastras, puedes usar un

Sintaxis avanzada

para fijar el ángulo de la cámara.

Crear un componente Raycast

Un componente Raycast describe el rayo invisible que se usa para consultar entidades intersectadas. El rayo se traza empezando en la posición de la entidad, según lo definido por el componente Transform y afectado por el de cualquier entidad padre. La dirección puede definirse de varias maneras,

  • campoLos rayos se definen usando los siguientes datos: $case : Un objeto que contiene un campo $case:

    • para seleccionar el tipo de dirección, y un campo adicional que dependerá de este tipo, que determina dicha dirección. Los siguientes son los valores aceptados paraLOCAL_DIRECTION : Una dirección relativa a la dirección frontal de la entidad, afectada también por la transformación de cualquier entidad padre. Esto es útil para detectar obstáculos delante de vehículos respetando su orientación. La rotación se define por el campo localDirection espera un como un

    • que describe una rotación.GLOBAL_DIRECTION : Ignora la rotación de la entidad y apunta en una dirección como si la rotación de la entidad fuera 0. Esto es útil, por ejemplo, para apuntar siempre hacia abajo. La rotación se define por el campo localDirection espera un como un

    • globalDirectionGLOBAL_TARGET Al establecer la dirección con un objetivo global, el localDirection espera un : Traza una línea entre la posición de la entidad y una posición objetivo global en la escena. Ignora la rotación de la entidad. Útil para crear juegos de defensa de torres; la torreta de cada torre puede apuntar a una coordenada concreta en el espacio. El objetivo se define por el

    • que describe la posición global.TARGET_ENTITY : crea un raycast con una dirección definida hacia una : Traza una línea entre la posición de la entidad y la posición de una segunda entidad objetivo. Ignora la rotación de ambas entidades. El objetivo se define por el campo

  • Los siguientes campos opcionales están disponibles al crear un rayo con cualquiera de los métodos anteriores:: maxDistance sujetando una referencia a la entidad.

  • para establecer la longitud con la que se trazará este rayo. Si no se establece, el valor por defecto es 16 metros.: queryType RaycastQueryType

    • solo devuelve la primera entidad impactada, empezando desde el punto de origen.para establecer la longitud con la que se trazará este rayo.

    • valor enum, para definir si el rayo devolverá todas las entidades impactadas o solo la primera. Están disponibles las siguientes opciones:RaycastQueryType.RQT_QUERY_ALL

  • propiedad collisionMask,Vector3.Zero() capas de colisiónarrow-up-rightcampo permite listar únicamente capas de colisión específicas, que pueden incluir la capa de física (que bloquea el movimiento del jugador), la capa pointer (que se usa para eventos de puntero) y 8 capas personalizadas que puedes asignar libremente según tus necesidades. Ver

  • : devuelve todas las entidades impactadas, desde el origen hasta la distancia máxima del rayo.: solo devuelve la primera entidad impactada, empezando desde el punto de origen.

  • . Si no se establece, la capa por defecto usada es: En lugar de iniciar el raycast desde la posición de origen de la entidad, añade un offset para iniciar la consulta desde una posición relativa. Por ejemplo, puedes usar un pequeño offset para evitar que el rayo colisione con el propio modelo 3D de la entidad.

circle-exclamation

: Si es true, seguirá ejecutando una consulta de raycast en cada frame. Si es false, el rayo solo se usará en el frame actual. Por defecto este valor es false.

queryType: RaycastQueryType.RQT_HIT_FIRST

continuous: true

queryType: RaycastQueryType.RQT_QUERY_ALL

circle-exclamation

se usa internamente por esa interfaz, pero también se expone para permitir lógica personalizada más avanzada. , y registrar una función callback como parte de la misma declaración que crea el rayo. El componente Después de crear un componente Raycast, la entidad a la que se añade este componente tendrá un componente

El , y registrar una función callback como parte de la misma declaración que crea el rayo. El componente RaycastResult. Este componente incluye información sobre cualquier impacto del rayo. Configura un sistema para comprobar estos datos.

  • La función callback que maneja el raycast recibe un objeto que contiene datos sobre el propio rayo y sobre las entidades que fueron impactadas.globalOrigin

  • campo: La posición donde se originó el rayo, relativa a la escena. espera un.

  • : La dirección global hacia la que apuntaba el rayo, como unhits valor enum, para definir si el rayo devolverá todas las entidades impactadas o solo la primera. Están disponibles las siguientes opciones:: Un arreglo con un objeto por cada entidad que fue impactada. Si no hubo entidades impactadas, este arreglo está vacío. Si el raycast usó

, este arreglo contendrá solo un objeto. : La dirección global hacia la que apuntaba el rayo, como un Cada objeto en el

  • arreglo incluye:entityId

  • : Número Id de la entidad que fue impactada por el rayo.: meshName String

  • posición: espera un con el nombre interno de la malla específica del modelo 3D que fue impactada. Esto es útil cuando un modelo 3D está compuesto por múltiples mallas.

  • para la posición donde el rayo se intersectó con la entidad impactada (relativa a la escena)length

  • : Longitud del rayo desde su origen hasta la posición donde ocurrió el impacto contra la entidad.: normalHit Quaternion

  • La función callback que maneja el raycast recibe un objeto que contiene datos sobre el propio rayo y sobre las entidades que fueron impactadas.: espera un para el ángulo de la normal del impacto en el espacio mundial.

  • campo: La posición donde se originó el rayo, relativa a la escena. espera un.

el componente contiene los siguientes datos:

console.log(rayResult.hits) , y registrar una función callback como parte de la misma declaración que crea el rayo. El componente El siguiente ejemplo muestra cómo puedes acceder a los componentes de todas las entidades en la escena, usando unaarrow-up-right.

circle-exclamation

: Los resultados de un raycast no llegan en el mismo tick del bucle de juego en el que creaste el raycast. Los resultados pueden tardar uno o varios ticks en llegar. capas de colisiónarrow-up-rightEn una escena donde usas múltiples tipos de rayos para diferentes propósitos (como para búsqueda de rutas, comprobación de línea de visión, trazado de proyectiles, etc.), puede que quieras usar diferentes máscaras de colisión, para evitar calcular colisiones irrelevantes.

Última actualización