How to delete node with Area3D node gracefully?
Asked Answered
I

18

0

Hi, I am having a werid crash problem when deleting a Node3D with a child Area3D node.
Basically I create like 30 objects with Area3D + CollisionShape3D(with a Box collision shape) per mouse click,
then I give them a timed-life of a 8 seconds, after 8 seconds they queue-free themselves.
But it seems after I spam like a few hundreds of Node3D, the game just crashes to desktop with an error in console:

"ERROR:ERROR:ERROR: FATAL: Index p_index = 0 is out of bounds (shapes.size() = 0).
FATAL: Index p_index = 0 is out of bounds (shapes.size() = 0).
FATAL: Index p_index = 0 is out of bounds (shapes.size() = 0).
at: at: get_shape_transform (servers/physics_3d/godot_collision_object_3d.h:128)
get_shape_transform (servers/physics_3d/godot_collision_object_3d.h:128)"

I did read about the docs about node deletion, it seems I don't need to manually free 'children' of a parent Node , since it frees all siblings automatically with queue-free function.

Here is the object I spammed in game to stress-test the problem:

And this is the code I used to auto-free upon object timeout:

extends Node3D

var speed : Vector3 = Vector3.ZERO

var life_start : float = -1.0
var life_span : float = 8.0

var dying : bool = false

# Called when the node enters the scene tree for the first time.
func _ready():
	pass # Replace with function body.


# Called every frame. 'delta' is the elapsed time since the previous frame.
func _process(delta):
	if self.is_queued_for_deletion():
		return
	
	var now_time : float = Time.get_unix_time_from_system()
	
	if life_start < 0:
		life_start = now_time
		
	var life_elapsed = now_time - life_start
	
	if life_elapsed >= life_span:
		dying = true
		hide()
		queue_free()
	
	
	var desire_x = delta * speed.x
	var desire_y = delta * speed.y
	var desire_z = delta * speed.z
	
	self.transform.origin.x += desire_x
	self.transform.origin.y += desire_y
	self.transform.origin.z += desire_z
	
	
func set_velocity(vel):
	speed = vel
	pass

Thanks.

EDIT::
Update:
Just tested spamming objects without 'queue-freeing' them, the number of objects actively reached 4k, and there is still no sign of crash after a few minutes -.-

Ist answered 10/7, 2023 at 14:52 Comment(0)
A
0

[unknown] Your project is a bit messy. You're not actually instantiating rpg_7.tscn as you described earlier but some scene that's embedded in player2.tscn and has a StaticBody3D as a root node and rgp_7.tscn as its child. This may or may not contribute to the problem.

Anyway, never instantiate new physics objects including areas from _physics_process(). So don't call shoot() from there, call it from _input() instead.

Also don't mix _input() event handler and Input object queries for handling input. It's confusing and non-elegant. Use one or the other.

Angeles answered 11/7, 2023 at 12:36 Comment(0)
M
0
  • Try commenting out those lines
	var desire_x = delta * speed.x
	var desire_y = delta * speed.y
	var desire_z = delta * speed.z
	
	self.transform.origin.x += desire_x
	self.transform.origin.y += desire_y
	self.transform.origin.z += desire_z
  • Or try to use call_deferred("queue_free") instead of queue_free()
  • I have no such experience and I don't really understand the errors, those are merely stuff I'd try to do to understand what's happening
Moro answered 10/7, 2023 at 15:36 Comment(0)
I
0

Moro
Thanks for the reply, the part you just mentioned seemed has nothing to do with the crash:
I just commented the lines with 'hide()' and 'queue_free()', it just run with 4k+ objects updating without a single crash after like 10 mins.

I looked up the source code of 4.1 stable, it seems the error is caused by the engine trying to delete a collision shape3D with an index less than or equal to 0, which raised a fatal level assertion or exception in engine that caused the crash, though I don't know why queue-freeing node with Area3D would trigger 'null shape size' in that function.

I will try replacing queue_free with call_deferred to see if that fixes the problem.

Ist answered 10/7, 2023 at 15:42 Comment(0)
A
0

Moro queue_free() is already deferred.

Explant The problem might be somewhere else in the code. Are you storing/accessing those instances elsewhere? Let's see the instantiation code too.

Angeles answered 10/7, 2023 at 15:42 Comment(0)
I
0

Angeles

Here is the function used to manually create 20 instances of the object:

func shoot():
	var projectiles_node : Node = $"../Projectiles"
	
	for i in range(0, 20):
		var proj_inst = _projectile_scene.instantiate()
		
		var gtran : Transform3D = $Head/SpringCamera.global_transform
		
		var direction = gtran.basis.z
		
		var camera_pos : Vector3 = $Head/SpringCamera.global_transform.origin
		var test_pos : Vector3  = camera_pos + direction * -3
		
		proj_inst.transform.origin = camera_pos + direction * -3
		
		
		var velocity : Vector3 = Vector3.ZERO
		
		velocity = direction * -20.0
		
		proj_inst.transform.basis = gtran.basis
		
		proj_inst.set_velocity(velocity)
		
		var rpg : RPG7 = proj_inst.get_node("RPG7") as RPG7
		
		rpg.set_creator(self)
		
		projectiles_node.add_child(proj_inst)

Edit: Tried call_defered but it still crashes with same error.

Ist answered 10/7, 2023 at 15:53 Comment(0)
I
0

And I also have 6 npc aircrafts shooting at 20ms interval at my character, they spam like 300ish objects per second, and throw them at my character, the objects also get deleted after 4 seconds, but those objects don't seem to have the crash problem as the one shot by my character manually, The only different betwee my player's projectile and npc projectile is the shape of the CollisionShape3D, so I think the the BoxShape3D I am using for the problematic player-shot projectile object is the culprit of the crash. But that is just my guess though.

Edit: Changed from BoxShape3D to SphereShape3D as CollisionShape3D, and it still crashes after spamming like 300 objects =/

Ist answered 10/7, 2023 at 16:1 Comment(0)
A
0

Ist Can you post a minimal project that reproduces the problem?

Angeles answered 10/7, 2023 at 16:1 Comment(0)
I
0

Angeles I uploaded the whole project, it's just a side project/sandbox to test some stuff for my other game, I will make this game open-source soon anyway. -.-

godetteofvictoryrev0.zip
22MB

Steps to reproduce:

  1. Spam clicking left-mouse-button at any location or facing, it should crash after 8 seconds or so (after the first free of the rocket timeout)
Ist answered 10/7, 2023 at 16:22 Comment(0)
A
0

Ist That's not a minimal project that reproduces the problem. Making it can often help to figure it out yourself. So put in some effort and make a minimal project instead of just uploading the whole project in the state you got stuck in.

Angeles answered 10/7, 2023 at 16:43 Comment(0)
C
0

Wild guess: Try setting the CollisionShape3D's disabled property to true before calling queue_free().

Candelabrum answered 10/7, 2023 at 17:17 Comment(0)
M
0
  • I was really interested to know what the problem is so I tried to recreate what you did using only what you showed us here, and with some naming and scene tree related modifications, your code actually works flawlessly except for one thing.
  • The console shouted at me with an Invalid call nonexistent function 'set_creator' I had to comment it out.
  • At first I thought it was a built-in method of some class, haha silly me.
  • So what does this function do? I suspect your problem lies in this function or the RPG7 class.
  • As you said, you face the problem when the player shoots the objects and not when the aircrafts do.
  • If you make the aircrafts shoot RPG7 projectiles, and use the same shoot function showed here (except that the transforms are from the aircraft and not the camera), do you get thrown an error?
  • If you make the player shoot whatever the aircrafts are shooting, and/or use their shoot() function, does it work with no problems.
Moro answered 10/7, 2023 at 21:24 Comment(0)
I
0

set_creator is method to assign and store rpg's parent as attacker, to do some player buff/weapon info query later, like damage, speed etc, but it has no function atm.

I think the crash is related to 2 or more Area 3Ds got freed when they overlapped at the same location, that's why I created 20 Area3D node with one click. But for some reason it's not 100% triggering the crash if you don't spam enough rocket projectiles.
I tested on a few PC ranging from windows and ubuntu or ubuntu_like OS, they seem to all have the crashing problem, and on some platform it just freezes the entire OS. -.-

I will give Dave's method a try, hopefully it will rectify the crash.

Ist answered 11/7, 2023 at 0:8 Comment(0)
I
0
godetteofvictoryrev1.zip
22MB

Tried disabling physics with set_physics_process/internal, but it still crashes upon first deletion of rocket object.
So pls lemme know if I did something wrong with using/deleting area3D or disabling it -.-

Thanks.

Ist answered 11/7, 2023 at 6:14 Comment(0)
A
0

[unknown] Your project is a bit messy. You're not actually instantiating rpg_7.tscn as you described earlier but some scene that's embedded in player2.tscn and has a StaticBody3D as a root node and rgp_7.tscn as its child. This may or may not contribute to the problem.

Anyway, never instantiate new physics objects including areas from _physics_process(). So don't call shoot() from there, call it from _input() instead.

Also don't mix _input() event handler and Input object queries for handling input. It's confusing and non-elegant. Use one or the other.

Angeles answered 11/7, 2023 at 12:36 Comment(0)
I
0

[unknown]

I think I have already replaced rpg_7's root node 'projectile.tscn' from StaticBody3D to Node3D to avoid potential conflicts, I will double-check if its type is changed 'Node3D'.

The rpg_7 is a child node of projectile.tscn, I have projectile as packed scene in player2.tscn, and instantiate projectile.tscn in 'shoot' function:

@export var _projectile_scene : PackedScene

func shoot():
	var projectiles_node : Node = $"../Projectiles"
	
	for i in range(0, 20):
		var proj_inst = _projectile_scene.instantiate()
        blahblah...

So I assume projectile's child nodes got instantiated too when projectile packedscene node got instantiated, though I am not 100% sure about godot's hierarchiral instantiating behaviors.

Your project is a bit messy. You're not actually instantiating rpg_7.tscn as you described earlier but some scene that's embedded in player2.tscn and has a StaticBody3D as a root node and rgp_7.tscn as its child. This may or may not contribute to the problem.

Thanks for the insight. I will move them to input function, and stick to one input event to handle all input events.

Anyway, never instantiate new physics objects including areas from _physics_process(). So don't call shoot() from there, call it from _input() instead.

Also don't mix _input() event handler and Input object queries for handling input. It's confusing and non-elegant. Use one or the other.

P.S:Moving the input and shoot functions from 'physics_process' to 'process' seemed to fix the problem. Any testing is appreciated.

Ist answered 11/7, 2023 at 13:34 Comment(0)
A
0

Ist Moving the input and shoot functions from 'physics_process' to 'process' seemed to fix the problem. Any testing is appreciated.

I already tested it. Adding collision nodes from _physics_process() is the culprit. The rest I just mentioned in passing.

Angeles answered 11/7, 2023 at 14:51 Comment(0)
M
0

[unknown] Wow, good to know!
Is it a bug or is there some known reason?

Moro answered 11/7, 2023 at 15:31 Comment(0)
A
0

Moro Hard to tell. But introducing new colliders in the middle of the physics frame processing is bound to produce at least some strange behavior. I wasn't exactly excepting crashes though.

Angeles answered 11/7, 2023 at 16:24 Comment(0)
I
0

Thanks again for the helps guys.
Just curious is there somewhere in the doc mentioning what you should NOT do in the process, physics_process?
I think people will more likely to avoid falling into the pitfall like the one I just crawled out, and it's nign impossible to find the mistake I made without your helps -.-

Ist answered 11/7, 2023 at 16:43 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.