Godot Version
4.6.2
Question
Trying to use RayCast3D for laser weapons - you hold LMB and then the function does “Raycast” stuff in _physics_process, detecting some collisions. But Idk why its not working. All the “Enemies”, “World”, “Objects” are in 1, 2, 3 layers so it can detect them, but still nothing happens (“Debil” doesnt appear in the Output).
Camera and weapon are facing -Z direction.
func _trace_laser(_bullets: int, delta: float) -> void:
if not camera:
push_error("Camera not found!")
return
var damage_tick_interval = 0.5
if not laser_raycast:
laser_raycast = RayCast3D.new()
laser_raycast.collision_mask = 7
laser_raycast.collide_with_bodies = true
laser_raycast.collide_with_areas = false
laser_raycast.add_exception(player_controller)
camera.add_child(laser_raycast)
damage_timer = 0.0
var from = camera.global_transform.origin
var to = from + camera.global_transform.basis.z * -current_weapon.laser_range
laser_raycast.target_position = laser_raycast.to_local(to)
laser_raycast.enabled = true
if laser_raycast.is_colliding():
print("Debil")
damage_timer += delta
if damage_timer >= damage_tick_interval:
attack_type_handler.apply_attack(
get_weapon_data().attack_types,
camera,
laser_raycast.get_collider(),
laser_raycast.get_collision_point(),
laser_raycast.get_collision_normal()
)
damage_timer = 0.0
else:
damage_timer = 0.0
With “Visible Collision Shapes” enabled, I can see that my RayCast3D is properly rotated in the right direction, so thats not the issue.
When Im moving:
Does “Debil” show in the Output?
Jesus, forgot to add… No, ofc its not showing
Need a screenshot of its configuration in the Inspector then.
Im trying to create it “on the fly” in memory. Its not a separate node in my tree… Or did I misunderstand you?
I see that now. Then need to see all the code, not just the one function.
WeaponController (main):
class_name WeaponController extends Node
@export var player_controller: PlayerController
@export var camera: CameraEffects
@export var weapon_model_parent: Node3D
@export var weapon_state_chart: StateChart
@export var base_weapon_projectile: PackedScene
@export var attack_type_handler: AttackTypeHandler
@export var damage_component: DamageComponent
@export var weapon_effects: WeaponEffects
var current_weapon: BaseWeapon
var current_weapon_model: Node3D
#...#
var laser_raycast: RayCast3D = null
var damage_timer: float = 0.0
var is_firing_laser: bool = false
func _physics_process(delta: float) -> void:
if is_firing_laser and current_weapon is LaserWeapon:
current_weapon.perform_attack(self, 0, delta)
elif laser_raycast:
_stop_laser()
func fire_weapon() -> void:
if current_weapon is LaserWeapon:
current_weapon.perform_attack(self, 0, 0.0)
return
#...#
func _trace_laser(_bullets: int, delta: float) -> void:
if not camera:
push_error("Camera not found!")
return
var damage_tick_interval = 0.5
if not laser_raycast:
laser_raycast = RayCast3D.new()
laser_raycast.collision_mask = 7
laser_raycast.collide_with_bodies = true
laser_raycast.collide_with_areas = false
laser_raycast.add_exception(player_controller)
camera.add_child(laser_raycast)
damage_timer = 0.0
var from = camera.global_transform.origin
var to = from + camera.global_transform.basis.z * -current_weapon.laser_range
laser_raycast.target_position = laser_raycast.to_local(to)
laser_raycast.enabled = true
if laser_raycast.is_colliding():
print("Debil")
damage_timer += delta
print(damage_timer)
if damage_timer >= damage_tick_interval:
attack_type_handler.apply_attack(
get_weapon_data().attack_types,
camera,
laser_raycast.get_collider(),
laser_raycast.get_collision_point(),
laser_raycast.get_collision_normal()
)
damage_timer = 0.0
else:
damage_timer = 0.0
func _stop_laser() -> void:
if laser_raycast:
laser_raycast.enabled = false
damage_timer = 0.0
is_firing_laser = false
LaserWeapon resource:
class_name LaserWeapon extends BaseWeapon
@export var laser_range: int
@export_range(0.1, 2.0) var overheat_buildup: float
@export_range(1.0, 10.0) var overheat_threshold: float
@export_range(0.5, 3.0) var cooldown_rate: float
func perform_attack(_controller: WeaponController, _bullets: int, _delta: float) -> void:
_controller._trace_laser(_bullets, _delta)
FiringState logic from my StateMachine:
func _on_firing_state_physics_processing(_delta: float) -> void:
if not weapon_controller.has_ammo():
weapon_controller.weapon_state_chart.send_event("onEmpty")
if weapon_controller.current_weapon.is_automatic:
if Input.is_action_pressed("primary_fire"):
if weapon_controller.can_fire():
weapon_controller.fire_weapon() #thats why Im added three lines here
else:
weapon_controller.weapon_state_chart.send_event("onIdle")
else:
weapon_controller.fire_weapon()
weapon_controller.weapon_state_chart.send_event("onIdle")
Node tree (just for sure):
So you’re sure this matches the boolean values set on the layers of the objects you’re colliding with? It should be colliding with any default object like a wall or floor.
FWIW, I always recommend making the object, getting it to work, then re-creating it in code if you have a reason for that - like a more complex node. TBH there is no benefit I can see for making it in code. Even if you don’t need it all the time, make it a scene, configure it, and instantiate it.
You should use the function set_collision_mask_value(7, true)instead because the actual mask is probably a binary number like
10000010000000000000100000000101
with bits 1, 7 20 etc, you are saying mask =7 which is actually like
11100000 in binary.