Godot Version
4.3
Question
So, I’m currently implementing my gunplay systems into my project. I’m starting with a melee weapon. I got most of it working as intended after 6 hours of brain burning problem solving.
However, I am running into a small roadblock. I cannot get my line of sight raycast3D to point to the player that got hit.
My brain is fried at the moment, so I decided to make this question to put the matter on the backburner till tomorrow.
Let me explain how the melee system works: (Where the input comes from isn’t relevant. Also, don’t worry about how everything’s placed. I’ll get that fixed after I get this implemented)
It starts with the player scene:
The melee hitbox (Called WrenchHitCollision
) is a massive Area3D capsule in front of the player. Whatever a player entity (that isn’t yourself) is in this capsule when the attack input is pressed, it will take damage.
Here’s the script that performs this:
func wrench_attack() -> void:
# The wrench hitbox
var Wrench_Hitbox := $GlobalRotationPivot/HeadPitchPivot/WrenchHitCollision
# Attempted Wrench Raycast
var Wrench_LOS_Raycast := $GlobalRotationPivot/HeadPitchPivot/WrenchHitCollision/RayCast3D
# Wrench damage values
const WRENCH_DAMAGE : int = 25
# Get all Area3Ds upon performing a wrench attack
var Hitboxes:Variant = Wrench_Hitbox.get_overlapping_areas()
for Box:Area3D in Hitboxes:
# Get the Area3D's parent node and parent name
var Hitbox : Node = Box.get_parent()
var Hitbox_Name : String = Box.get_parent().name
# Loops through the collided hitboxes for-
# - players to hit. (And ignore self)
if Hitbox.has_node("PlayerDamageCollision") \
and Hitbox_Name != Player_ID:
# Attepted LOS Check Wrench_LOS_Raycast.set_target_position(Hitbox.get_node("LineOfSightChecker").global_position)
Hitbox.get_node("PlayerDamageCollision").damage(WRENCH_DAMAGE)
else: pass
So, what I want for the raycast to do is point to the player’s given LineOfSightChecker
, which is a Node3D. Then, if it hits a body (not an area), it will not register the hit.
Everything except the line of sight check works. I’m pretty sure I’m missing one or two lines for proper functionality. Let me know if you need any more context. Thanks in advance.
The raycast target position is relative to it’s current position
var hitbox_position: Vector3 = Hitbox.get_node("LineOfSightChecker").global_position
var local_target: Vector3 = hitbox_position - Wrench_LOS_Raycast.global_position
Wrench_LOS_Raycast.target_position = local_target
But you will also need to force a update, it may be better to use a direct space query instead of a raycast that may be running all the time, but only used on command.
var world_3d := get_world_3d().direct_space_state
var ray_query := PhysicsRayQueryParameters3D.new()
ray_query.collision_mask = 0b1111
ray_query.from = Wrench_LOS_Raycast.global_position
ray_query.to = Hitbox.get_node("LineOfSightChecker").global_position
var collision: Dictionary = world_3d.intersect_ray(ray_query)
if collision:
print("hit something!")
1 Like
I’m not quite sure what a “direct space query” every is. But I’ll find out tomorrow. Thanks for the info.
Long time no reply, just wanted to paste my solution real fast for posterity. Thanks to @gertkeno and @pennyloafers for their help.
The Solution
So the scene is still the same with an updated script. It’s a little hard to explain what changed as physics math is confusing. But essentially the issue was due to raycast math and having to use the to_local
on the raycast.
Not sure how the raycast math works, but the most important thing is that it works and I can use it for other features.
Code:
func wrench_attack() -> void:
# Wrench damage values
const WRENCH_DAMAGE : int = 25
# Get all Area3Ds upon performing a wrench attack
var Hitboxes:Variant = Wrench_Hitbox.get_overlapping_areas()
# Loop through all area3Ds
for Box:Area3D in Hitboxes:
# Get the Area3D's parent node and parent name (This works)
var Hitbox : Node = Box.get_parent()
var Hitbox_Name : String = Box.get_parent().name
# Loops through the collided hitboxes for-
# - players to hit. (And ignore self)
if Hitbox.has_node("PlayerDamageCollision") \
and Hitbox_Name != Player_ID:
# Get the enemy's LOS reference node and local hitbox position
var Hit_Player_LOS_Position : Vector3 = Hitbox.get_node("GlobalLineOfSightReference").global_position
var Raycast_Target_Location : Vector3 = Global_Line_Of_Sight_Raycast.to_local(Hit_Player_LOS_Position)
# Send the LOS raycast to the enemy's location and force and update
Global_Line_Of_Sight_Raycast.target_position = Raycast_Target_Location
Global_Line_Of_Sight_Raycast.force_raycast_update()
# Checks if the LOS raycast hits a surface (body)
if Global_Line_Of_Sight_Raycast.is_colliding() == false:
# Damages the enemy hit
Hitbox.get_node("PlayerDamageCollision").damage(WRENCH_DAMAGE)
# Continue if the LOS raycast fails
else: continue
# Ignore everything that isn't an enemy player hitbox
else: pass