How to call a function defined in a Scene's script from another script?

:information_source: Attention Topic was automatically imported from the old Question2Answer platform.
:bust_in_silhouette: Asked By malraux42

Hello all!

I have a Scene (called Scaffold) that instantiates several other Scenes (called Blocks) when it enters the the tree. The Scaffold holds a Conveyor scene whose job it is to move Blocks towards positive z-index while it runs – a typical auto-moving scenery setup where each Block holds things like obstacles and traps and the player stays in the center. Block has a method called set_block_index that registers which block it is in the Conveyor and initializes its position based on that.

The problem I’m having is that set_block_index can’t be called from Scaffold.gd during this setup execution as it will throw “Invalid call. Nonexistent function `set_block_index` in base ‘Spatial’.”. I can get it to work by calling the method via call_deferred, but I’m unsure of the ramifications of calling the method in such a fashion.

So there are a few things I’d like to understand here:

  • Why can’t a method defined on a Scene script be called externally?
  • Why is the call not found in the base Spatial when it should be looking in Block.gd? Perhaps that’s just saying it didn’t find it in either and not until it failed the fallback did it throw the error.

Here’s the code in question:

# Scaffold.gd

var starting_block_scene = preload("res://StartingBlock.tscn")
var block_scene = preload("res://Block.tscn")
var current_block_index = 0

func _enter_tree():
    setup_blocks()

func setup_blocks():
    add_block(starting_block_scene)	
    add_block(block_scene)

func add_block(scene):
    var block = scene.instance()
    $Conveyor.add_child(block)

    # Calling `set_block_index` immediately causes:
    #   "Invalid call. Nonexistent function `set_block_index` in base 'Spatial'."
    block.set_block_index(current_block_index)

    # Calls `set_block_index` correctly.
    # block.call_deferred("set_block_index", current_block_index)

    current_block_index += 1

extends Spatial

var block_index = 0
const block_length = 40

func set_block_index(index):
    block_index = index
    # Do other initialization here based on the block index...
    translation.z = -index * block_length

Thanks for your help in advance!

:bust_in_silhouette: Reply From: Zylann

“Invalid call. Nonexistent function set_block_index in base ‘Spatial’.”

It really just looks like either the root node of StartingBlock.tscn or Block.tscn do not have the script attached to them. The call should not fail if the root nodes have Block.gd.

As for call_deferred, it should not be necessary. I’m surprised it would “work” according to your description. Maybe it just doesnt stop executing, but if the script is absent then it will still log an error (keep an eye on these).

Yep, that was (kind of) the issue! Both root nodes had the scripts attached to them, but StartingBlock was supposed to extend Block and it did not. As soon as I fixed that it worked. The call_deferred was allowing the StartingBlock->set_block_index call to fail silently, and since it was no longer in the way of the calls to Block->set_block_index they were successful.

I didn’t notice anything in the game with the failed StartingBlock call because it just happened to already be in the right position.

Thanks!

malraux42 | 2022-01-31 14:19