Implementation damage FPS 3D.

Godot Version

4.5.

Question

Hello.!

I have some issues with implementing damage.. The tutorial videos are all different and follow their own logic.

As it stands, I would simply like to tell the software “when the fire button is used and the crosshair hits the enemy, then the enemy loses X health.”

Knowing that I’m trying to do this with an automatic weapon, so I know I need to create a “fire rate” in seconds.

At the moment I’m stuck here, and nothing happens.I removed the non-functional signs.

Thanks in advance.

This is how I do it:

  1. Create a variable within your weapon class the indicates whether the weapon can shoot, and initialize it to true.
  2. When the user presses the “fire” button, shoot, set the “can shoot” variable to false, and set a timer for your fire interval. When it timer expires, or the user releases the “fire” button (or some other condition or set of conditions), set your “can shoot” variable to true. Ignore the “fire” event until the “can fire” boolean changes back to true.

For the damage and checking things under a crosshair, you should look into things like “hitscan” or similar concepts for Godot.
Basically checking from the camera’s position, towards the crosshair, run a ray trace/cast a ray and “hit” the closest object on that ray that counts.

Thank you for your feedback, I was able to make some progress, everything is finally working almost: Despite my current_health and max_health being at 100, and gun_damage being 10, I still one-shot my bot.. Where is the problem ?

script ennemy :

I can’t upload more than one photo at a time… Missing 3 photos from the weapon script. (X

script gun 1 :

2 :

3 :

Thanks in advance. ^^

By setting health to 1000 and damage to 1, it works as it should, but why do we have to make such an extreme change? Is the firing rate of my weapon too high?

You need to reset _shoot_time_gone to 0 when shooting, otherwise _shoot_time_gone >= shoot_delay will always result in true.

Okay, but how do I do that? I half understood this code, I wrote most of it by blindly copying. (X

func _shoot(delta: float) -> void:
	_shoot_time_gone += delta
	if _shoot_time_gone >= shoot_delay:
		
		#add this line:
		_shoot_time_gone = 0.0

It worked, thank you! ^^

Is there a way to precisely choose the location of a CharacterBody3D for damage? Specifically the head?

Without adding a node since my shooting script is based on CharacterBody3D. '^^

If you use multiple CollisionShapes for the CharacterBody3D (one specifically for the head), you can check which CollisionShape got hit by the ray.

The result dictionary has a shape key, that you can use to get the corresponding CollisionShape3D:

# in _hit_the_target():
	var target = result["collider"]
	var shape_index = result["shape"]
	var owner_id = target.shape_find_owner(shape_index)
	var shape: CollisionShape3D = target.shape_owner_get_owner(owner_id)

Then you could add a script to your CollisionShapes:

extends CollisionShape3D

@export var damage_multiplier: float = 1.0

and adjust the value for different body parts in the Inspector.

You could access this value via shape.damage_multiplier, but then the game would crash if the CollisionShape that got hit doesn’t have that script. Using shape.get() can prevent this:

# continuation for _hit_the_target():
	var damage_multiplier: float = shape.get("damage_multiplier")
	if not damage_multiplier:
		damage_multiplier = 1.0

Now the only thing left is to use gun_damage * damage_multiplier when calling target.take_damage().

Thank you for this detailed message. I could just start over and blindly copy your code, but the goal is to understand, and as it stands, I haven’t understood everything. (X

Here’s where I am and where the problem persists. Lines 48 to 54.
No errors, but still no x2, x10 damage or whatever. (X

I can’t fix the problem without starting all over.?

Your ray query does not collide with areas (your head or tete node) only bodies (your ENNEMI node). And if it did then you would need to navigate from the head to the enemy in order to call take_damage which would be get_parent() resulting in

if target.is_in_group("head"):
    target.get_parent().take_damage(gun_damage * 10)

Make sure to paste code snippets instead of screenshots

1 Like
I used your code, but it has the same effect: No error, but no x2 damage either. (x)

Like this ? :


'''if target is CharacterBody3D:
	target.take_damage(gun_damage)

if target.is_in_group("ennemy"):
	if target.is_in_group("head"):
		target.get_parent().take_damage(gun_damage * 100)'''

First, you have to change your ray query parameters (before calling intersect_ray()) for the ray to detect areas:

query.collide_with_areas = true
1 Like
I wanted to do it and I tried again, but Godot tells me "Identifier "query" not declared in the current scope."

Even though I already have a "query" described above, I don't understand how that's possible.
And here is what it tells me when I place your query on the line after my query: Invalid assignment of property or key 'collide_with_area' with a value of type 'bool' on a base object of type 'PhysicsRayQueryParameters3D'.
Update: I think it works this time:

''' if target.is_in_group("head"):

target.get_parent()

target.take_damage(gun_damage*10)'''

But only the head dies, not the enemy. We just need to fix that. (X

So it was indeed the missing `with_areas`, thank you! ^^


Update 2: I managed to fix the problem with the head dying on its own. ^^

Next step: respawn the bot. (X

I managed to make my bot respawn, however there are 3 problems:

The first problem is that it respawns at the slightest click, even though I correctly set it to respawn only after its death, and its death occurs after X number of bullets (this has been verified).

The second is that I can’t make its head respawn.. (X

If I shoot its head, the enemy just disappears. If I shoot its body, it respawns correctly with its head.

And finally, third problem: The respawn is not infinite. Well, it should be, but after 5-6-10 deaths, it eventually stops spawning. Why?

Thank you. I managed to do what I’m showing you in the picture without a tutorial. I’m happy about that, but there are clearly still problems. (X

script ennemy :

‘‘‘if _current_health <= 0.0 and max_health != 0.0:
_die()
else:
ennemy._on_ennemy_respawned()

func _die() → void:
queue_free()

@onreadyonready var ennemy = $“.”

func _on_ennemy_respawned():
self.global_position = Vector3(0.0, 1.28, -23)’’’

Script head :

‘‘‘if _current_health <= 0.0 and max_health != 0.0:
_die()
else:
ennemi._die()
ennemy._on_ennemy_respawned()

func _die() → void:
queue_f@onreadyee()

@onready var e@onreadynemy = $“..”
@onready var tete = $“.”

func _on_ennemy_respawned():
self.global_position = Vector3(0.0, 1.3, -23)’’’

As you can see, I tried, in the head script, to make the enemy respawn entirely instead of just its head, but that doesn’t work either. (X

Update: Problem solved. ^^