0 votes

Hello!

My system:
Windows 10 Pro 1909 x64
Nvidia GTX 1050 Ti
Godot 3.2 stable mono

About the error:
I'm making a script to control bullets and I'm having a problem to delete the array element of the bullet when the bullet is destroyed. Sometimes it works without any problem but times to times it throws an error saying

"Invalid get index 'x' (on base: 'Array')

The x is not always the same value. Sometimes the error appears in one second and sometimes it appears in several seconds (after hundreds of bullets were created).

Here is the code:

var _bullet_ref := []

func shoot() -> void:
    var bullet_instance = bullet_scene.instance()
    add_child(bullet_instance)
    _init_bullet(bullet_instance, p)


func _init_bullet(p_bullet_instance, p_player_instance) -> void:
    var new_rot = p_player_instance.bullet_socket.global_transform.basis.get_euler()
    p_bullet_instance.rotation = new_rot
    p_bullet_instance.set_global_3d_position(
            p_player_instance.bullet_socket.get_global_3d_position()
            )
    p_bullet_instance.start_position = p_bullet_instance.get_global_3d_position()
    p_bullet_instance.can_process = true
    var b_ref = weakref(p_bullet_instance)
    _bullet_ref.append(weakref(p_bullet_instance))


func _physics_process(p_delta: float) -> void:
    var idx_to_remove := []
    for i in range(0, _bullet_ref.size()):
        var bullet = _bullet_ref[i].get_ref()
        if bullet:
            if not bullet.can_process:
                continue
            var forward = bullet.get_forward_vector()           
            var new_pos = forward.normalized() * bullet.speed * p_delta         
            bullet.global_translate(new_pos)

            if bullet.get_global_3d_position().distance_to(bullet.start_position) > bullet.max_distance:
                idx_to_remove.append(i)

    for idx in idx_to_remove:
        var b = _bullet_ref[idx].get_ref()
        _bullet_ref.remove(idx)
        if b:
            b.queue_free()
in Engine by (96 points)
retagged by

dont know if this is the cause, which array creates the error? Is it bulletref?
If it is, likely the issue come from _bullet_ref.remove(idx) changing the size of the array. When the array changes in size, the index in the idxtoremove do not represent the bullet you wanted to remove anymore (and if the index is high, and you remove enough indexes in the same frame, some value inside idxtoremove might be higher than _bullet_ref.size(), which expalins the invalid get index)

I suggest you to work with the values of the array instead of the indexes,
idx_to_remove[]=>bullet_to_remove[], the last part of you code became:

if bullet.get_global_3d_position().distance_to(bullet.start_position) > bullet.max_distance:
                    bullet_to_remove.append(bullet)


for b in bullet_to_remove:
    _bullet_ref.erase(b)
     b.queue_free()

by the way, instead of constructing your own array with the reference of bullet, you could accomplish a similar results in a simpler way by placing all the bullet nodes inside a parent_bullet node and cycle through them directly with parent_bullet.get_children(), or even by adding the bullets to a bullet group with bullet.add_to_group("bullets"), and ciclying through them by get_tree().get_nodes_in_group("bullets")
After that you can queue_free() them without having to update any array with the reference

Thank you! It worked.
What solution you think is better, performance-wise? I thought array was the fastest option.

cant tell which solution is more efficient among the one discussed, but you can do some analisys using OS.getthickmsec(), or looking at the profiler.
I usually use the parent_node method though, because it generates a cleaner project tree.
However i dont think any solution is particularly more efficient than the others, because the heavier part of the code is the calculation of the distance for each bullet, and this is independent from the way you want to cycle through them.
If you need to increase performance for this specific bullet-delete function, i would suggest to re-think the condition the bullets are deleted, like adding a timer to the bullet node that kill them (you have to check if it is really better), or performing the queue_free action dilated in time (like no more than 10 bullets in a frame)

Thank you for the information!

Please log in or register to answer this question.

Welcome to Godot Engine Q&A, where you can ask questions and receive answers from other members of the community.

Please make sure to read How to use this Q&A? before posting your first questions.
Social login is currently unavailable. If you've previously logged in with a Facebook or GitHub account, use the I forgot my password link in the login box to set a password for your account. If you still can't access your account, send an email to webmaster@godotengine.org with your username.