Raycasting allows you to detect collisions along a line or query the physics world for intersections. Godot provides several query types for different use cases.
Physics queries are performed through the direct space state:
# Get 2D direct space statevar space_state = get_world_2d().direct_space_state# Get 3D direct space statevar space_state_3d = get_world_3d().direct_space_state
func raycast_2d_example(): var space_state = get_world_2d().direct_space_state var from = global_position var to = global_position + Vector2(0, 100) var query = PhysicsRayQueryParameters2D.create(from, to) var result = space_state.intersect_ray(query) if result: print("Hit: ", result.collider) print("Position: ", result.position) print("Normal: ", result.normal)
var query = PhysicsRayQueryParameters3D.create(origin, target)query.collision_mask = 0b0010 # Only detect layer 2query.collide_with_areas = true # Include areasquery.collide_with_bodies = true # Include bodiesvar result = space_state.intersect_ray(query)
var raycast = RayCast3D.new()raycast.target_position = Vector3(0, 0, -10)raycast.enabled = trueraycast.collision_mask = 0b0001add_child(raycast)# Check in _physics_processfunc _physics_process(delta): if raycast.is_colliding(): var collider = raycast.get_collider() var point = raycast.get_collision_point() var normal = raycast.get_collision_normal()
var raycast = RayCast2D.new()raycast.target_position = Vector2(0, 100)raycast.enabled = trueraycast.collision_mask = 0b0001add_child(raycast)func _physics_process(delta): if raycast.is_colliding(): var collider = raycast.get_collider() var point = raycast.get_collision_point() var normal = raycast.get_collision_normal()
var shape_cast = ShapeCast3D.new()shape_cast.enabled = trueshape_cast.shape = SphereShape3D.new()shape_cast.target_position = Vector3(0, -10, 0)add_child(shape_cast)func _physics_process(delta): if shape_cast.is_colliding(): var collision_count = shape_cast.get_collision_count() for i in collision_count: var collider = shape_cast.get_collider(i) var point = shape_cast.get_collision_point(i) var normal = shape_cast.get_collision_normal(i)
Shape casting is useful for character ground detection, allowing you to use a capsule or box instead of a ray.
func has_line_of_sight(from: Vector3, to: Vector3) -> bool: var space_state = get_world_3d().direct_space_state var query = PhysicsRayQueryParameters3D.create(from, to) query.collision_mask = vision_layer_mask var result = space_state.intersect_ray(query) return result.is_empty() # True if no obstacles
func is_on_ground() -> bool: var space_state = get_world_3d().direct_space_state var origin = global_position var target = origin + Vector3(0, -0.1, 0) var query = PhysicsRayQueryParameters3D.create(origin, target) query.exclude = [self] var result = space_state.intersect_ray(query) return not result.is_empty()
func get_object_at_mouse(): var camera = get_viewport().get_camera_3d() var mouse_pos = get_viewport().get_mouse_position() var from = camera.project_ray_origin(mouse_pos) var to = from + camera.project_ray_normal(mouse_pos) * 1000 var space_state = get_world_3d().direct_space_state var query = PhysicsRayQueryParameters3D.create(from, to) var result = space_state.intersect_ray(query) if result: return result.collider return null
func predict_projectile_path(start: Vector3, velocity: Vector3, steps: int = 50): var space_state = get_world_3d().direct_space_state var gravity = ProjectSettings.get_setting("physics/3d/default_gravity") var gravity_vec = Vector3(0, -gravity, 0) var pos = start var vel = velocity var delta = 0.1 var path = [] for i in steps: var next_pos = pos + vel * delta var query = PhysicsRayQueryParameters3D.create(pos, next_pos) var result = space_state.intersect_ray(query) if result: path.append(result.position) break path.append(next_pos) pos = next_pos vel += gravity_vec * delta return path