Godot 4.0: Raycast, get component, change variable

zhifei
2 min readMay 27, 2023

A little context, I’m working on a game whereby I can select a character and a torus mesh that’s attached to it will be visible (it’s invisible on _ready) — to show that it’s indeed selected. When I select anywhere but the character, the torus will not be visible.

In technical term, what I want to achieve in Godot is as follow:

  • On mouse click event detected
  • A raycast will point towards where I click
  • The raycast detect an object with collider
  • The main script will get the component/script of the object
  • The main script will change the value of the object’s script

Raycast

The setup of the raycast script is as follows:

func _input(event: InputEvent):
if event is InputEventMouseButton and event.pressed and event.button_index == 1:
var from = camera.project_ray_origin(event.position)
var to = from + camera.project_ray_normal(event.position) * 1000.0

var space_state = get_world_3d().direct_space_state
var query = PhysicsRayQueryParameters3D.create(from, to)
var result = space_state.intersect_ray(query)

Collider detection

Apparently Godot’s raycast is capable of detecting object that doesn’t have a collider. I’m aware that I can add a mask to mask away specific object from a certain group.

Taking the result from the script above, I’ll first check if it contains a collider. If it doesn’t, I’ll do nothing:

if !result.has("collider"):
return

Get component and change value

To get the component from the clicked object, we first have to establish a class_name for the script that’s attached to the object, which we’ll call “Player” for now:

extends CharacterBody3D

class_name Player

Then in the main script that’s shooting the raycast, we can simply check whether the collider Node type matches the type of the Player component, if it does, we will cast it as a Player component and call its internal function.

var target = result.collider
if target is Player:
var player = target as Player
player.do_something()

Full script

# game.gd

...

func _input(event: InputEvent):
if event is InputEventMouseButton and event.pressed and event.button_index == 1:
# Raycast
var from = camera.project_ray_origin(event.position)
var to = from + camera.project_ray_normal(event.position) * 1000.0

var space_state = get_world_3d().direct_space_state
var query = PhysicsRayQueryParameters3D.create(from, to)
var result = space_state.intersect_ray(query)
# Check collider
if !result.has("collider"):
return

var target = result.collider
# Match type
if target is Player:
var player = target as Player
player.do_something()

...

----------

# player.gd

extends CharacterBody3D

class_name Player

...

--

--

zhifei

Software engineer. Jack of all stacks, master of none.