Function logic only targeting some nodes in an array

Godot Version

4.3

Question

I have a player character with a knockback ability. That knockback ability has a number of charges. When the charges are depleted, the player will have to pass through a zone(collisionshape3D) to replenish the charges. I want the collisionshape that the player passes through to deactivate and hide, then be reactivated and unhidden when colliding with one of the other replenishing collisionshapes.

I have code that includes an array of four collisionshapes. When entering collisionshapes 1 and 2, the code seems to operate as intended; they deactivate and hide. However, entering 3 and 4 do not hide themselves but rather they apply the deactivate and hide to 1 and 2. As seen here: https://i.imgur.com/4eY0ztU.mp4; In this project I use omnilights as the knockback charge indicators;in this clip I use the knockback ability twice which turns off the lights and then passing through the collisionshapes unhides the lights. So shapes 1 and 2, apply their func logic to themselves to deactivate and hide, while also replenishing and updating visuals on the omnilights, where as 3 and 4 also replenish and update visuals on the omnilight knockback indicators, but apply their own func logic to shapes 1 and 2. I don’t understand what’s happening here, because they are all included in the same array and are all children of the same area3D node: https://i.imgur.com/eWEm9Sp.png

Here’s a screenshot of the output if it helps. Every print of collisionshape3d 1 and 2 in this print me colliding with shapes 3 and 4: https://i.imgur.com/nrnzGg1.png

Thanks, in advance, to anyone who spends literally any time looking at this.

extends CharacterBody3D

const SPEED = 5.0
const JUMP_VELOCITY = 4.5
var gravity = -9.8

@export var health = 1


var max_knockback_uses: int = 5
var current_knockback_uses: int = 5
var knockback_indicators: Array[OmniLight3D] = []

@onready var collision_shapes: Array[CollisionShape3D] = []
@onready var active_collision_shape: CollisionShape3D = null

func _ready():
	print("Area3D is monitoring:", $"../Area3D".monitoring)
	print("Collision shapes array: ", collision_shapes)
	knockback_indicators = [
		$"Knockback_Indicator 1", 
		$"Knockback_Indicator 2", 
		$"Knockback_Indicator 3", 
		$"Knockback_Indicator 4", 
		$"Knockback_Indicator 5", 
	]
	print("Knockback indicators:", knockback_indicators)
	update_knockback_visuals()
	
	collision_shapes = [
		$"../Area3D/CollisionShape3D 1", 
		$"../Area3D/CollisionShape3D 2", 
		$"../Area3D/CollisionShape3D 3", 
		$"../Area3D/CollisionShape3D 4",
	]
	
	
func _physics_process(delta):
	
# Add the gravity.
	if not is_on_floor():
		velocity += get_gravity() * delta

	# Handle jump.
	if Input.is_action_just_pressed("ui_accept") and is_on_floor():
		velocity.y = JUMP_VELOCITY

	# Get the input direction and handle the movement/deceleration.
	# As good practice, you should replace UI actions with custom gameplay actions.
	var input_dir := Input.get_vector("ui_left", "ui_right", "ui_up", "ui_down")
	var direction := (transform.basis * Vector3(input_dir.x, 0, input_dir.y)).normalized()
	if direction:
		velocity.x = direction.x * SPEED
		velocity.z = direction.z * SPEED
	else:
		velocity.x = move_toward(velocity.x, 0, SPEED)
		velocity.z = move_toward(velocity.z, 0, SPEED)

	move_and_slide()	
		
	
var knockback_power = 100.0
var knockback_radius = 50.0

func knockback():
	
	var enemies = get_tree().get_nodes_in_group("enemies")
	
	# Iterate through each enemy
	for enemy in enemies:
		if enemy is CharacterBody3D:
			# Calculate the distance between the player and the enemy
			var direction = enemy.global_transform.origin - global_transform.origin
			var distance = direction.length()
			
			# Check if the enemy is within the knockback radius
			if distance <= knockback_radius:
				direction = direction.normalized() # Get the direction vector
				# Apply knockback force
				enemy.apply_knockback(direction * knockback_power)
		

func use_knockback():
	if current_knockback_uses > 0:
		knockback()
		current_knockback_uses -= 1
		print("Knockback used. Remaining:", current_knockback_uses)
		print("Calling update_knockback_visuals()...")
		update_knockback_visuals()
	else:
		print("No knockback uses left!")

func _input(event):
		if event.is_action_pressed("Knockback"):
			use_knockback()
			print("knockback pressed")
func replenish_knockback(amount: int = 1):
	#print("Replenish knockback called")
	if current_knockback_uses < max_knockback_uses:
		current_knockback_uses = max_knockback_uses
		update_knockback_visuals()
		print("Knockback replenished. Current uses:", current_knockback_uses)


func update_knockback_visuals():
	print("Updating knockback visuals...")
	for i in range(max_knockback_uses):
		if knockback_indicators[i]:
			if i < current_knockback_uses:
				knockback_indicators[i].visible = true
				print("Indicator", i + 1, "visible")
			else:
				knockback_indicators[i].visible = false
				print("Indicator", i + 1, "hidden")
		else:
			print("Knockback indicator", i + 1, "is missing!")

	
func _on_area_3d_body_entered(body: Node3D) -> void:
	print("body entered", body.name)

	if body == self:  
		for shape in collision_shapes:
			print("Checking shape:", shape.name, "Disabled:", shape.disabled)
			if shape and not shape.disabled:
				replenish_knockback()
				deactivate_collision_shape(shape)
				reactivate_other_collision_shapes(shape)
				break
	
func deactivate_collision_shape(shape: CollisionShape3D):
	for collision_shape in collision_shapes:
		if collision_shape == shape:
			if not collision_shape.disabled:
				shape.set_deferred("disabled", true)
				shape.set_deferred("visible", false)
				print("Deactivating shape:", shape.name, "Disabled state after call:", shape.disabled)
				await get_tree().process_frame
		
			print("Post-deferred disabled state:", shape.disabled)
		

func reactivate_other_collision_shapes(excluded_shape: CollisionShape3D):
	# Reactivate all other shapes
	for shape in collision_shapes:
		if shape and shape != excluded_shape:
			if shape.disabled:
				shape.set_deferred("disabled", false)
				shape.set_deferred("visible", true)
				print("Reactivated shape:", shape.name)

For anyone who may be curious or possibly need this(for as long as it still works with whichever future version you’re on), this is currently working for me. I replaced the last three methods, func _on_area_body_3d_entered, func deactivate_collision_shape and func reactivate_other_collision_shapes with the following:

func _on_area_3d_body_shape_entered(_body_rid: RID, body: Node3D, _body_shape_index: int, local_shape_index: int) -> void:
	# Return if not colliding with the player
	if body != self:
		return

	# Activate all shapes indiscriminately
	for shape in collision_shapes:
		shape.set_deferred("disabled", false)
		shape.visible = true

	# Deactivate the shape the player collided with
	if local_shape_index >= 0 and local_shape_index < collision_shapes.size():
		var collided_shape = collision_shapes[local_shape_index]
		collided_shape.set_deferred("disabled", true)
		collided_shape.visible = false
		
	replenish_knockback()

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