Enemy health problem

Godot Version

4.2.1

Question

Hello, I’m doing a game where I have some slimes as enemies, but when I hit them, their lifes are connected and all instances get hit. Here’s the code for the spawn and the slime, in that order.

extends Node3D
@onready var spawns = $map/Spawns
@onready var navigation_region = $map/NavigationRegion3D

var slime = load("res://scenes/slime.tscn")
var instance

# Called when the node enters the scene tree for the first time.
func _ready():
	randomize()

# Called every frame. 'delta' is the elapsed time since the previous frame.
func _process(delta):
	pass

func _get_random_child(parent_node):
	var random_id = randi() % parent_node.get_child_count()
	return parent_node.get_child(random_id)

func _on_slime_spawner_timer_timeout():
	var spawn_point = _get_random_child(spawns).global_position
	instance = slime.instantiate()
	instance.position = spawn_point
	navigation_region.add_child(instance)
	print(instance, ": ", instance.health)
extends CharacterBody3D

var player = null
var gravity = ProjectSettings.get_setting("physics/3d/default_gravity")
var state_machine
var health = 100

const SPEED = 2.0
const ATTACK_RANGE = 2.5

@export var player_path := "/root/World/map/NavigationRegion3D/Player" 
@onready var health_bar = $Node/slime/SubViewport/HealthBar3D
@onready var nav_agent = $NavigationAgent3D
@onready var anim_tree = $AnimationTree

func _ready():
	player = get_node(player_path)
	player.connect("attacking", _take_damage)
	state_machine = anim_tree.get("parameters/playback")
	
func _physics_process(delta):
	health_bar.value = health
	if health <= 0:
		queue_free()
	
	if not is_on_floor():
		velocity.y -= gravity * delta

func _process(delta):
	
	velocity = Vector3.ZERO
	
	match state_machine.get_current_node():
		"walk":
			# Navigation
			nav_agent.set_target_position(player.global_transform.origin)
			var nex_nav_point = nav_agent.get_next_path_position()
			velocity = (nex_nav_point - global_transform.origin).normalized() * SPEED
			# Rotation
			#velocity.y = lerp_angle(rotation.y, atan2(-velocity.x, -velocity.z), delta * 10)
			look_at(Vector3(global_position.x + velocity.x, global_position.y, global_position.z + velocity.z), Vector3.UP)
		"Attack":
				# Rotation
				look_at(Vector3(player.global_position.x, player.global_position.y, player.global_position.z), Vector3.UP)
			
	
	# Conditions
	anim_tree.set("parameters/conditions/attack", _target_in_range())
	anim_tree.set("parameters/conditions/run", !_target_in_range())
	
	move_and_slide()
	
func _target_in_range():
	return global_position.distance_to(player.global_position) < ATTACK_RANGE

func _hit_finished():
	if global_position.distance_to(player.global_position) < ATTACK_RANGE + 1.0:
		var dir = global_position.direction_to(player.global_position)
		player.hit(dir)

func _take_damage():
	health -= 10
	print(health)

Don’t connect signal like this, otherwise all instance connected same time, try use Area2D Node to detect the enemy who needs take damage then connect signal when entered in area2d and disconnect when exit

The area2d(in my case is 3d) that you are talking is the collision for the attack? If it is, how do i connect them? my player is in a different scene. But thank you for your help!

Can u share more info about your player and enemy scene structure ? (Screenshot)

Of course.
Player scene:


Player script: (Sorry i have to clean up my player script immediately)

extends CharacterBody3D

@onready var camera_mount = $camera_mount
@onready var animation_player = $visuals/blockbench_export/AnimationPlayer
@onready var visuals = $visuals

#step variables
const STEP_FREQ = 2.4
const STEP_AMP = 0.08
var t_step = 0.0

#footstep variables
var can_play : bool = true
signal step

signal player_hit
signal attacking

var SPEED = 3
const JUMP_VELOCITY = 4.5
var HIT_STAGGER = 8.0
var enemy_in_range = false
@onready var camera = $camera_mount/Camera3D
@onready var dust_particles = $Dust/vfx_dust/Dust_particles
@onready var health_bar = $SubViewport/HealthBar3D
@onready var attack_collision = $visuals/AttackCollision
@onready var ray_cast_collision = $visuals/RayCast3D
var slime = load("res://scenes/slime.tscn")
var enemy = slime.instantiate()

var walking_speed = 3.0
var running_speed = 5.0

var running = false
var blocking = false
var is_locked = false

@export var sens_horizontal = 0.5
@export var sens_vertical = 0.5

var health = 100
#var damage = 20

# Get the gravity from the project settings to be synced with RigidBody nodes.
var gravity = ProjectSettings.get_setting("physics/3d/default_gravity")


func _ready():
	
	Input.mouse_mode = Input.MOUSE_MODE_CAPTURED
	camera.current = true
	

func _input(event):
	
	if event is InputEventMouseMotion:
		rotate_y(deg_to_rad(-event.relative.x * sens_horizontal))
		visuals.rotate_y(deg_to_rad(event.relative.x * sens_horizontal))
		camera_mount.rotate_x(deg_to_rad(-event.relative.y * sens_vertical))

func _physics_process(delta):
	health_bar.value = health
	if  health <= 0:
		global_position = Vector3.ZERO
		health = 100
	
	if !animation_player.is_playing():
		is_locked = false
	
	if Input.is_action_just_pressed("attack"):
		if animation_player.current_animation != "attack":
			animation_player.play("attack")
			is_locked = true
			if enemy_in_range:
				emit_signal("attacking")
			
	if Input.is_action_just_pressed("block"):
		if animation_player.current_animation != "shield_stand":
			animation_player.play("shield_stand")
			blocking = true
			is_locked = true
			dust_particles.emitting = false
	
	if Input.is_action_just_released("block"):
		if animation_player.current_animation == "shield_stand":
			animation_player.play("idle")
			blocking = false
			is_locked = false
			
	if Input.is_action_pressed("run"):
		SPEED = running_speed
		running = true
	else:
		SPEED = walking_speed
		running = false
		dust_particles.emitting = false
	
	
	# Add the gravity.
	if not is_on_floor():
		velocity.y -= gravity * delta

	t_step += delta * velocity.length() * float(is_on_floor())
	camera.transform.origin = _headstep(t_step)
	
	# Get the input direction and handle the movement/deceleration.
	var input_dir = Input.get_vector("left", "right", "forward", "backward")
	var direction = (transform.basis * Vector3(input_dir.x, 0, input_dir.y)).normalized()
	if direction:
		if !is_locked:
			if  running:
				if animation_player.current_animation != "run":
					animation_player.play("run")
					dust_particles.emitting = true
			else:
				if animation_player.current_animation != "walk":
					animation_player.play("walk")
					
			visuals.look_at(position + direction)
			
		velocity.x = direction.x * SPEED
		velocity.z = direction.z * SPEED
	else:
		if !is_locked:
			if animation_player.current_animation != "idle":
				animation_player.play("idle")
				dust_particles.emitting = false
		velocity.x = move_toward(velocity.x, 0, SPEED)
		velocity.z = move_toward(velocity.z, 0, SPEED)
	if !is_locked:
		move_and_slide()

func _headstep(time) -> Vector3:
	var pos = Vector3.ZERO
	pos.y = sin(time * STEP_FREQ) * STEP_AMP
	pos.x = sin(time * STEP_FREQ / 2) * STEP_AMP
	
	var low_pos = STEP_AMP - 0.05
	if pos.y > -low_pos and is_locked != true:
		can_play = true
	if pos.y < -low_pos and can_play:
		can_play = false
		emit_signal("step")
		
	return pos

func hit(dir):
	if health > 0 and !blocking:
		emit_signal("player_hit")
		velocity += dir * HIT_STAGGER
		health -= 10


func _on_attack_collision_body_entered(body):
	if enemy.is_in_group("enemy"):
		enemy_in_range = true
		print("enemy in range")


func _on_attack_collision_body_exited(body):
	if enemy.is_in_group("enemy"):
		enemy_in_range = false
		print("enemy not in range")

Enemy scene:


Main scene:

1 Like

Could someone find a way to solve the problem? I would be grateful

what is attack_collision type? it is raycast?

to deal damage to the enemy try this:
create a ray cast if you don’t have one for attack range
inside if Input.is_action_just_pressed("attack"): add this few lines

	var enemy := [YOUR_ATTACK_RAY_CAST].get_collider() as Enemy
	if enemy:
		enemy._take_damage()

also, add class_name Enemy in the first line of your enemy script so you can detect it from your player script

2 Likes

remember to remove the _take_damage connected signal from an enemy script

It worked!! Thank you, It’s working fine now

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.