# Raycasting

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 Entity es intersectada por esa línea. Esto es útil para calcular líneas de visión, trayectorias de balas, algoritmos de búsqueda de caminos y muchas otras aplicaciones.

Cuando un jugador pulsa el botón del Pointer, o el botón primario o secundario, se traza un rayo desde la posición del jugador en la dirección en la que está mirando, ver [eventos de botón](/creator/content-creator-es/scenes-sdk7/interactividad/eventos-de-button/click-events.md) para más detalles sobre esto. Este documento explica 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 quieres detectar impactos de rayos contra un modelo 3D, debes hacer una de estas dos cosas:

* El modelo debe contener [mesh de collider](/creator/content-creator-es/modelado-3d-y-animaciones/colliders.md).
* El `GLTFContainer` debe configurarse para usar la [geometría visible con collision masks](/creator/content-creator-es/scenes-sdk7/esenciales-de-contenido-3d/colliders.md#colliders-on-3d-models).
* Añade un [componente MeshCollider](/creator/content-creator-es/scenes-sdk7/esenciales-de-contenido-3d/colliders.md).

También es una buena práctica asignar [collision layers](/creator/content-creator-es/scenes-sdk7/esenciales-de-contenido-3d/colliders.md#collision-layers) personalizadas a los modelos 3D, de modo que los rayos solo necesiten calcular colisiones contra las Entities 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 Entity, tomando los valores del componente Transform de la Entity. La dirección de un rayo puede definirse de 4 maneras diferentes:

* **local**: Una dirección relativa a la dirección hacia adelante de la Entity, también afectada por la transformación de cualquier Entity padre. Esto es útil para detectar obstáculos delante de vehículos respetando su rumbo.
* **global**: Ignora la rotación de la Entity y apunta en una dirección como si la rotación de la Entity fuera 0. Esto es útil, por ejemplo, para apuntar siempre hacia abajo.
* **objetivo global**: Traza una línea entre la posición de la Entity y una posición global objetivo en la Scene. Ignora la rotación de la Entity. Útil, por ejemplo, para crear juegos de tower defense, donde la torreta de cada torre puede apuntar a una coordenada precisa en el espacio.
* **Entity objetivo**: Traza una línea entre la posición de la Entity y la posición de una segunda Entity objetivo. Ignora la rotación de cualquiera de las dos Entities.

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

```ts
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 de callback
  }
)
```

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

* `raycastSystem.registerLocalDirectionRaycast()`: crea un raycast con una **local** dirección. El `direction` campo espera un `Vector3` que describe un vector relativo a la Entity y a su rotación (por ejemplo, `Vector3.Forward()` terminaría usando el vector forward del Transform de la Entity)
* `raycastSystem.registerGlobalDirectionRaycast()`: crea un raycast con una **global** dirección. El `direction` campo espera un `Vector3` que describe la dirección global.
* `raycastSystem.registerGlobalTargetRaycast()`: crea un raycast con una dirección definida por una **objetivo global** position. El `target` campo espera un `Vector3` que describe una posición global en la Scene.
* `raycastSystem.registerTargetEntityRaycast()`: crea un raycast con una dirección definida hacia una **Entity objetivo** position. El `targetEntity` campo espera una referencia a una Entity; la posición de esa Entity 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`: *number* para establecer la longitud con la que se trazará este rayo. Si no se establece, el valor predeterminado es 16 metros.
* `queryType`: *RaycastQueryType* valor enum, para definir si el rayo devolverá todas las Entities impactadas o solo la primera. Las siguientes opciones están disponibles:
  * `RaycastQueryType.RQT_HIT_FIRST`: *(predeterminado)* solo devuelve la primera Entity impactada, empezando desde el punto de origen.
  * `RaycastQueryType.RQT_QUERY_ALL`: devuelve todas las Entities 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 Entity, añade un desplazamiento para iniciar la consulta desde una posición relativa. Por ejemplo, puedes usar un pequeño desplazamiento para evitar que el rayo colisione con el propio collider de la Entity. Si no se establece, el valor predeterminado es `Vector3.Zero()`.
* `collisionMask`: Solo detecta colisiones con ciertas collision layers. Usa esto junto con una collision layer personalizada, o para detectar solo la layer de physics o la de pointer events. Ver [collision layers](/creator/content-creator-es/scenes-sdk7/esenciales-de-contenido-3d/colliders.md#collision-layers). Si no se establece, la layer predeterminada usada es `ColliderLayer.CL_PHYSICS`.
* `continuous`: 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 predeterminado es false.
* Al establecer la dirección con una dirección local o glocal, el `direction` campo se establece de forma predeterminada en `Vector3.Forward()`.
* Al establecer la dirección con un objetivo global, el `globalTarget` campo se establece de forma predeterminada en `Vector3.Zero()`.
* Al establecer la dirección con un objetivo de Entity, el `targetEntity` campo se establece de forma predeterminada en la root entity de la Scene, ubicada en `Vector3.Zero()`.

{% hint style="warning" %}
**📔 Nota**: La `continuous` propiedad debe usarse con cautela, ya que ejecutar una consulta de raycast en cada frame puede ser muy costoso para el rendimiento. Cuando sea posible, usa un system (o la `interval` función en la biblioteca Utils) para ejecutar consultas de raycast a intervalos regulares y más espaciados, ver [raycasting recurrente](#recurrent-raycasting).
{% endhint %}

A continuación hay ejemplos que usan cada uno de los cuatro métodos para determinar la dirección del rayo:

```ts
// LOCAL DIRECTION RAYCAST
raycastSystem.registerLocalDirectionRaycast(
  {
    entity: myEntity,
    opts: {
      queryType: RaycastQueryType.RQT_QUERY_ALL,
      direction: Vector3.Forward(),
      maxDistance: 30,
    },
  },
  function (raycastResult) {
    console.log(raycastResult.hits)
  }
)
// GLOBAL DIRECTION RAYCAST
raycastSystem.registerGlobalDirectionRaycast(
  {
    entity: myEntity,
    opts: {
      queryType: RaycastQueryType.RQT_QUERY_ALL,
      direction: Vector3.Forward(),
      maxDistance: 30,
    },
  },
  function (raycastResult) {
    console.log(raycastResult.hits)
  }
)
// GLOBAL TARGET POSITION RAYCAST
raycastSystem.registerGlobalTargetRaycast(
  {
    entity: myEntity,
    opts: {
      queryType: RaycastQueryType.RQT_QUERY_ALL,
      globalTarget: Vector3.Zero(),
    },
  },
  (raycastResult) => {
    console.log(raycastResult.hits)
  }
)
// TARGET ENTITY RAYCAST
const targetEntity = engine.addEntity()
Transform.create(targetEntity, { position: Vector3.create(8, 1, 10) })

raycastSystem.registerTargetEntityRaycast(
  {
    entity: myEntity,
    opts: {
      queryType: RaycastQueryType.RQT_QUERY_ALL,
      targetEntity: targetEntity,
    },
  },
  (raycastResult) => {
    console.log(raycastResult.hits)
  }
)
```

{% hint style="warning" %}
**📔 Nota**: `raycastSystem`, `RaycastQueryType` y `ColliderLayer` deben importarse mediante

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

Consulta [Imports](/creator/content-creator-es/scenes-sdk7/primeros-pasos/coding-scenes.md#imports) para ver cómo manejar esto fácilmente.
{% endhint %}

## Resultado del raycast

La función de callback que maneja el raycast recibe un objeto que contiene datos sobre el propio rayo y sobre cualquier Entity que haya sido impactada.

* `globalOrigin`: La posición desde la que se originó el rayo, relativa a la Scene.
* `direction`: La dirección global hacia la que apuntaba el rayo, como un `Vector3`.
* `hits`: Un array con un objeto por cada Entity que fue impactada. Si no hubo Entities impactadas, este array está vacío. Si el raycast usó `RaycastQueryType.RQT_HIT_FIRST`, este array solo contendrá un objeto.

Cada objeto en el `hits` array incluye:

* `entityId`: Número de Id de la Entity que fue impactada por el rayo.
* `meshName`: *String* con el nombre interno del mesh específico del modelo 3D que fue impactado. Esto es útil cuando un modelo 3D está compuesto por múltiples meshes.
* `position`: *Vector3* para la posición donde el rayo intersectó con la Entity impactada (relativa a la Scene)
* `length`: Longitud del rayo desde su origen hasta la posición donde ocurrió el impacto contra la Entity.
* `normalHit`: *Quaternion* para el ángulo de la normal del impacto en world space.
* `globalOrigin`: *Vector3* para la posición donde se origina el rayo (relativa a la Scene)
* `direction`: La dirección global hacia la que apuntaba el rayo, como un `Vector3`.

El siguiente ejemplo itera sobre las Entities que fueron impactadas:

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

raycastSystem.registerLocalDirectionRaycast(
  {
    entity: myEntity,
    opts: {
      queryType: RaycastQueryType.RQT_QUERY_ALL,
      direction: Vector3.Forward(),
      maxDistance: 30,
    },
  },
  function (raycastResult) {
    if (raycastResult.hits.length > 0) {
      for (const hit of raycastResult.hits) {
        if (hit.entityId) {
          console.log('hit entity ', hit.entityId)
        }
      }
    } else {
      console.log('no entities hit')
    }
  }
)
```

{% hint style="warning" %}
**📔 Nota**: Puedes obtener un resultado de raycast al impactar una Entity en una Scene diferente.
{% endhint %}

## Manejar Entities impactadas

Cuando obtienes un resultado de raycast que impactó una Entity, puedes usar el `entityId` para interactuar con la Entity y sus components. Una Entity es [nada más que un número](/creator/content-creator-es/scenes-sdk7/arquitectura/entities-components.md#overview), así que el `entityId` valor en sí mismo puede interpretarse como un `Entity` type.

```ts
const hitEntity = result.entityId as Entity
const transform = Transform.get(entity)
console.log(transform.position)
```

## Collision layers

Es una buena práctica comprobar solo colisiones contra las Entities relevantes, para que la Scene sea más eficiente. El `collisionMask` campo permite listar solo collision layers específicas, que pueden incluir la layer de physics (que bloquea el movimiento del jugador), la layer de pointer (que se usa para pointer events) y 8 layers personalizadas que puedes asignar libremente según tus necesidades. Ver [collision layers](/creator/content-creator-es/scenes-sdk7/esenciales-de-contenido-3d/colliders.md#collision-layers). De forma predeterminada, se detectan todas las layers.

De forma predeterminada, el `collisionMask` campo se establece para responder a ambas layers `ColliderLayer.CL_POINTER` y `ColliderLayer.CL_PHYSICS`. Puedes cambiar este valor para listar solo una de ellas o para incluir layers personalizadas. Usa el `|` separador para listar múltiples opciones.

```ts
raycastSystem.registerLocalDirectionRaycast(
  {
    entity: myEntity,
    opts: {
      queryType: RaycastQueryType.RQT_QUERY_ALL,
      direction: Vector3.Forward(),
      maxDistance: 30,
      collisionMask:
        ColliderLayer.CL_CUSTOM1 |
        ColliderLayer.CL_CUSTOM3 |
        ColliderLayer.CL_POINTER,
    },
  },
  (raycastResult) => {
    log(raycastResult.hits)
  }
)
```

## raycasting recurrente

Cuando usas las funciones de la `raycastSystem`, el comportamiento predeterminado es crear un solo rayo, que consultará colisiones una vez. Como alternativa, puedes establecer el `continuous` campo en *true* para ejecutar una consulta y la función de callback en cada tick del game loop.

El siguiente ejemplo seguirá ejecutando la consulta de raycast a partir de este punto

```ts
raycastSystem.registerLocalDirectionRaycast(
  {
    entity: myEntity,
    opts: {
      queryType: RaycastQueryType.RQT_QUERY_ALL,
      direction: Vector3.Forward(),
      maxDistance: 30,
      continuous: true,
    },
  },
  function (raycastResult) {
    log(raycastResult.hits)
  }
)
```

{% hint style="warning" %}
**📔 Nota**: La `continuous` propiedad debe usarse con cautela, ya que ejecutar una consulta de raycast en cada frame puede ser muy costoso para el rendimiento.
{% endhint %}

Cuando ya no sea necesario, elimina cualquier raycast recurrente. Para hacerlo, debes usar `raycastSystem.removeRaycasterEntity`.

```ts
raycastSystem.removeRaycasterEntity(myEntity)
```

Cuando sea posible, usa un system (o la `interval` función en la biblioteca Utils) para ejecutar consultas de raycast a intervalos regulares y más espaciados, como solo una vez por segundo o cada quinto de segundo.

```typescript
// custom components
const CubeOscilator = engine.defineComponent('CubeOscilator', {
  t: Schemas.Float,
})

const TimerComponent = engine.defineComponent('TimerComponent', {
  t: Schemas.Float,
})

const RAY_INTERVAL = 0.1

// check rays
engine.addSystem((dt) => {
  for (const [entity] of engine.getEntitiesWith(TimerComponent)) {
    const timer = TimerComponent.getMutable(entity)
    timer.t += dt

    if (timer.t > RAY_INTERVAL) {
      timer.t = 0
      raycastSystem.registerGlobalDirectionRaycast(
        {
          entity: myEntity,
          opts: {
            queryType: RaycastQueryType.RQT_HIT_FIRST,
            direction: Vector3.Forward(),
            maxDistance: 16,
          },
        },
        function (raycastResult) {
          log(raycastResult.hits)
        }
      )
    }
  }
})

TimerComponent.create(engine.addEntity())

// oscillating cube system
engine.addSystem((dt) => {
  for (const [entity, cube] of engine.getEntitiesWith(
    CubeOscilator,
    Transform
  )) {
    CubeOscilator.getMutable(entity).t += dt
    Transform.getMutable(entity).position.y = 2 + Math.cos(cube.t)
  }
})

// create cube
const cubeEntity = engine.addEntity()
Transform.create(cubeEntity, { position: { x: 8, y: 1, z: 8 } })
CubeOscilator.create(cubeEntity)
MeshRenderer.setBox(cubeEntity)
MeshCollider.setBox(cubeEntity)
```

El ejemplo anterior ejecuta un raycast recurrente cada 0,1 segundos. Usa un componente timer y la propiedad `dt` de un system para temporizar esto de forma uniforme. También incluye un cubo que oscila arriba y abajo, controlado por otro system, para entrar y salir de la trayectoria del rayo.

{% hint style="info" %}
**💡 Consejo**: Usa la `interval` función en la [biblioteca Utils del SDK](https://github.com/decentraland/sdk7-utils) para una forma más sencilla de ejecutar una función a un intervalo fijo.
{% endhint %}

## Raycasts mediante un system

Otra forma de realizar raycasts recurrentes es ejecutarlos desde la función recurrente de un system. Esto te permite tener mucho más control sobre cuándo y cómo funcionan. En lugar de registrar una función de 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 system.

Ten en cuenta que, como el raycast se ejecuta en un system, el resultado solo estará disponible en el siguiente tick, por lo que se necesitan dos ejecuciones del system. Una para registrar el raycast para el siguiente frame y la siguiente para procesar su resultado.

```ts
engine.addSystem((deltaTime) => {
		const result = raycastSystem.registerRaycast(
			entity,
			localDirectionOptions({
				collisionMask: ColliderLayer.CL_CUSTOM1 | ColliderLayer.CL_CUSTOM3 | ColliderLayer.CL_POINTER,
				originOffset: Vector3.create(0, 0.4, 0),
				maxDistance: RAY_POWER,
				queryType: raycastQueryType,
				direction: Vector.forward()
				continuous: true // don't overuse the 'continuous' property as raycasting is expensive on performance
			})
		)
		if (result) // do something
	})
```

## Colisionar con el jugador

No puedes impactar directamente el avatar del jugador ni el de otros jugadores con un rayo, pero como solución puedes posicionar una Entity invisible ocupando el mismo espacio que un jugador usando el [componente AvatarAttach](/creator/content-creator-es/scenes-sdk7/esenciales-de-contenido-3d/entity-positioning.md#attach-an-entity-to-an-avatar), y comprobar colisiones con ese cubo.

## Raycasts desde el jugador

Para trazar un rayo desde la posición del jugador en la dirección hacia la que mira la cámara, puedes trazar un rayo usando la cámara o el avatar [Reserved entities](/creator/content-creator-es/scenes-sdk7/arquitectura/entities-components.md#reserved-entities).

{% hint style="info" %}
**💡 Consejo**: En la mayoría de los casos, quizá te convenga más usar [Pointer eveents](/creator/content-creator-es/scenes-sdk7/interactividad/eventos-de-button/click-events.md) en lugar de raycasts.
{% endhint %}

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

```ts
raycastSystem.registerGlobalDirectionRaycast(
  {
    entity: engine.CameraEntity,
    opts: {
      queryType: RaycastQueryType.RQT_HIT_FIRST,
      direction: Vector3.rotate(
        Vector3.Forward(),
        Transform.get(engine.CameraEntity).rotation
      ),
    },
  },
  function (raycastResult) {
    console.log(raycastResult)
  }
)
```

{% hint style="warning" %}
**📔 Nota**: 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.
{% endhint %}

## 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.

En este ejemplo, detectamos cuando el jugador pulsa la tecla E y luego trazamos un rayo desde la posición del cursor hacia el mundo 3D. Después comprobamos si el rayo impactó alguna Entity y, si es así, hacemos algo con ella.

```ts
import { engine, Entity, InputAction, inputSystem, PointerEventType, RaycastQueryType, raycastSystem, TextShape, Transform } from '@dcl/sdk/ecs'
import { EntityNames } from '../assets/scene/entity-names'
import { PrimaryPointerInfo } from '@dcl/sdk/ecs'

let cooldown = 1
let rayFrequency = 0.1
let mousePressed = false

export function main() {
   engine.addSystem(rayCastSystem)
}

const rayCastSystem = (t: number) => {

    if (inputSystem.isTriggered(InputAction.IA_PRIMARY, PointerEventType.PET_DOWN)) {
      mousePressed = true
    }

    if (inputSystem.isTriggered(InputAction.IA_PRIMARY, PointerEventType.PET_UP)) {
      mousePressed = false
    }

    if (!mousePressed) {
      cooldown = 0
      raycastSystem.removeRaycasterEntity(engine.CameraEntity)
      return
    }

    cooldown += t
    if (cooldown > rayFrequency) return
    cooldown = 0

    const pointerInfo = PrimaryPointerInfo.getOrCreateMutable(engine.RootEntity)
    let dir = pointerInfo.worldRayDirection

    raycastSystem.registerGlobalDirectionRaycast(
      {
        entity: engine.CameraEntity,
        opts: {
          queryType: RaycastQueryType.RQT_HIT_FIRST,
          direction: dir,
        },
      },
      function (raycastResult) {
        let result = raycastResult.hits[0]

        // do something in the hit position
        if (result && result.position) {
          console.log("x:", result.position.x, ", y:", result.position.y, ", z:", result.position.z)
        }

        // do something with the hit entity
        const entity = result.entityId as Entity
        if (entity) {
          console.log("entity: ", entity)
        }
      }
    )
}

```

{% hint style="info" %}
**💡 Consejo**: En este ejemplo usamos el botón primario (E) para activar el raycast. No usamos el botón del Pointer (clic izquierdo) porque hacer clic y arrastrar también cambia el ángulo de la cámara de forma predeterminada. Si quieres evitar rotar la cámara mientras arrastras, puedes usar un [Virtual Camera](/creator/content-creator-es/scenes-sdk7/esenciales-de-contenido-3d/camera.md) para fijar el ángulo de la cámara.
{% endhint %}

## Sintaxis avanzada

### Crear un componente Raycast

Un componente Raycast describe el rayo invisible que se usa para consultar Entities intersectadas. El rayo se traza empezando en la posición de la Entity, tal como la define el componente Transform y afectada por la de cualquier Entity padre. La dirección puede definirse de varias maneras,

Los rayos se definen usando los siguientes datos:

* `direction`: Un objeto que contiene un `$case` campo para seleccionar el tipo de dirección, y un campo adicional que dependerá de este tipo y que determina esta dirección. Los siguientes son los valores aceptados para `$case`:
  * `LOCAL_DIRECTION`: Una dirección relativa a la dirección hacia adelante de la Entity, también afectada por la transformación de cualquier Entity padre. Esto es útil para detectar obstáculos delante de vehículos respetando su rumbo. La rotación se define mediante el `localDirection` campo, como un `Vector3` que describe una rotación.
  * `GLOBAL_DIRECTION`: Ignora la rotación de la Entity y apunta en una dirección como si la rotación de la Entity fuera 0. Esto es útil, por ejemplo, para apuntar siempre hacia abajo. La rotación se define mediante el `globalDirection` campo, como un `Vector3` que describe una rotación.
  * `GLOBAL_TARGET`: Traza una línea entre la posición de la Entity y una posición global objetivo en la Scene. Ignora la rotación de la Entity. Útil para crear juegos de tower defense, donde la torreta de cada torre puede apuntar a una coordenada precisa en el espacio. El objetivo se define mediante el `globalTarget` campo, como un `Vector3` que describe la posición global.
  * `TARGET_ENTITY`: Traza una línea entre la posición de la Entity y la posición de una segunda Entity objetivo. Ignora la rotación de cualquiera de las dos Entities. El objetivo se define mediante el `targetEntity` campo, que contiene una referencia a la Entity.
* `maxDistance`: *number* para establecer la longitud con la que se trazará este rayo.
* `queryType`: *RaycastQueryType* valor enum, para definir si el rayo devolverá todas las Entities impactadas o solo la primera. Las siguientes opciones están disponibles:
  * `RaycastQueryType.RQT_QUERY_ALL`: solo devuelve la primera Entity impactada, empezando desde el punto de origen.
  * `RaycastQueryType.RQT_HIT_FIRST`: devuelve todas las Entities impactadas, desde el origen hasta la distancia máxima del rayo.
* `collisionMask`: Solo detecta colisiones con ciertas collision layers. Usa esto junto con una collision layer personalizada, o para detectar solo la layer de physics o la de pointer events. Ver [collision layers](/creator/content-creator-es/scenes-sdk7/esenciales-de-contenido-3d/colliders.md#collision-layers). De forma predeterminada, se detectan todas las layers.
* `originOffset`: En lugar de iniciar el raycast desde la posición de origen de la Entity, añade un desplazamiento para iniciar la consulta desde una posición relativa. Por ejemplo, puedes usar un pequeño desplazamiento para evitar que el rayo colisione con el propio modelo 3D de la Entity.
* `continuous`: Si es true, seguirá ejecutando una consulta de raycast en cada frame. Si es false, el rayo solo se usará en el frame actual. De forma predeterminada, este valor es false.

{% hint style="warning" %}
**📔 Nota**: La `continuous` propiedad debe usarse con cautela, ya que ejecutar una consulta de raycast en cada frame puede ser muy costoso para el rendimiento. Cuando sea posible, usa un system (o la `interval` función en la biblioteca Utils) para ejecutar consultas de raycast a intervalos regulares y más espaciados, ver [raycasting recurrente](#recurrent-raycasting).
{% endhint %}

El siguiente ejemplo usa una rotación global para determinar la dirección y solo devuelve la primera Entity que es impactada en el frame en que se envía el rayo.

```typescript
const entity1 = engine.addEntity()

Transform.create(entity1, {
  position: Vector3.create(8, 1, 0)
})

Raycast.createOrReplace(entity1, {
  direction: {
    $case: "globalDirection",
    globalDirection: Vector3.create(0, 0, 1)
  }
  maxDistance: 16,
  queryType: RaycastQueryType.RQT_HIT_FIRST
})
```

El ejemplo siguiente lanza un rayo en la dirección hacia adelante de la Entity, devolviendo solo el primer elemento impactado. Lo hace de forma continua. También incluye un pequeño desplazamiento de 0,5 para evitar que el rayo impacte el collider de la propia Entity.

```typescript
const entity1 = engine.addEntity()

Transform.create(entity1, {
  position: Vector3.create(8, 1, 0)
})

Raycast.createOrReplace(entity1, {
  direction: {
    $case: "localDirection",
    localDirection: Vector3.Forward()
  }
  maxDistance: 16,
  queryType: RaycastQueryType.RQT_HIT_FIRST,
  originOffset: Vector3.create(0.5, 0, 0),
  continuous: true
})
```

Este ejemplo traza un rayo entre dos Entities. Devuelve todas las Entities que se impactan en medio.

```ts
const entity1 = engine.addEntity()

Transform.create(entity1, {
  position: Vector3.create(8, 1, 0)
})

const entity2 = engine.addEntity()

Transform.create(entity2, {
  position: Vector3.create(0, 1, 8)
})

Raycast.createOrReplace(entity1, {
  direction: {
    $case: "targetEntity",
    targetEntity: entity2
  }
  maxDistance: 16,
  queryType: RaycastQueryType.RQT_QUERY_ALL
})
```

### Componente de resultados de Raycast

{% hint style="warning" %}
**📔 Nota**: La forma más sencilla de tratar los resultados de raycast es usar `raycastEventSystem`, y registrar una función de callback como parte de la misma sentencia que crea el rayo. El componente`RaycastResult` se usa internamente por esa interfaz, pero también se expone para habilitar una lógica personalizada más avanzada.
{% endhint %}

Después de crear un componente Raycast, la Entity a la que se añade este componente tendrá un `RaycastResult` componente. Este componente incluye información sobre cualquier impacto del rayo. Configura un system para comprobar estos datos.

El `RaycastResult` componente contiene los siguientes datos:

* `globalOrigin`: La posición desde la que se originó el rayo, relativa a la Scene.
* `direction`: La dirección global hacia la que apuntaba el rayo, como un `Vector3`.
* `hits`: Un array con un objeto por cada Entity que fue impactada. Si no hubo Entities impactadas, este array está vacío. Si el raycast usó `RaycastQueryType.RQT_HIT_FIRST`, este array solo contendrá un objeto.

Cada objeto en el `hits` array incluye:

* `entityId`: Número de Id de la Entity que fue impactada por el rayo.
* `meshName`: *String* con el nombre interno del mesh específico del modelo 3D que fue impactado. Esto es útil cuando un modelo 3D está compuesto por múltiples meshes.
* `position`: *Vector3* para la posición donde el rayo intersectó con la Entity impactada (relativa a la Scene)
* `length`: Longitud del rayo desde su origen hasta la posición donde ocurrió el impacto contra la Entity.
* `normalHit`: *Quaternion* para el ángulo de la normal del impacto en world space.
* `globalOrigin`: *Vector3* para la posición donde se origina el rayo (relativa a la Scene)
* `direction`: La dirección global hacia la que apuntaba el rayo, como un `Vector3`.

El siguiente ejemplo muestra cómo puedes acceder a los resultados desde una Entity individual usando un system:

```typescript

const rayEntity = engine.addEntity()

Transform.create(rayEntity, {
  position: Vector3.create(8, 1, 0)
})

// return all entities
Raycast.createOrReplace(rayEntity, {
  direction: {
    $case: "globalDirection",
    globalDirection: Vector3.create(0, 0, 1)
  }
  maxDistance: 16,
  queryType: RaycastQueryType.RQT_QUERY_ALL
})

engine.addSystem(() => {
  const rayResult = RaycastResult.get(rayEntity)
  console.log(rayResult.hits)
})
```

El siguiente ejemplo muestra cómo puedes acceder a `RaycastResult` components de todas las Entities de la Scene, usando una [consulta de componentes](/creator/content-creator-es/scenes-sdk7/arquitectura/querying-components.md).

```typescript
engine.addSystem(() => {
  for (const [_, result] of engine.getEntitiesWith(RaycastResult)) {
    console.log(result.hits)
  }
})
```

{% hint style="warning" %}
**📔 Nota**: Los resultados de un raycast no llegan en el mismo tick del game loop en el que creaste el raycast. Los resultados pueden tardar uno o varios ticks en llegar.
{% endhint %}

En una Scene en la que uses varios tipos de rayos para distintos propósitos (como path finding, comprobación de línea de visión, trazado de proyectiles, etc.), quizá quieras usar distintos [collision layers](/creator/content-creator-es/scenes-sdk7/esenciales-de-contenido-3d/colliders.md#collision-layers), para evitar calcular colisiones irrelevantes.


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.decentraland.org/creator/content-creator-es/scenes-sdk7/interactividad/raycasting.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
