2
\$\begingroup\$

I built a simple scene that I instanciate at will. Simply a KinematicBody2D as root, containing a Sprite and a CollisionShape2D. There is a script linked to the root node that basically expose a few properties to describe an orbit and make it move in orbit around its parent. The point being to make a very basic solar system simulation or anything that requires a node to move around another in a circular fashion.

Instanciated scene tree

The movement work fine and I’ve been playing around my scene tree, instancing a bunch and watching them move around as moons of planets, etc. But the texture is all the same. If I make children editable, I can go change the texture of the sprite, but I’d like to expose that property from the root node so I can access it directly from the editor.

I tried adding an exposed property with a setter and a getter to assign and retrieve the texture, but it seems to crash Godot completely with this code:

export(Texture) onready var texture setget texture_set, texture_get

func texture_set(newtexture):
    $"Sprite".texture = newtexture

func texture_get():
    return $"Sprite".texture

Is there a solution for that? Or simply another way?

\$\endgroup\$
1
  • \$\begingroup\$ I've been using that, what you need to make it work is to add a conditional (if) check to the methods to make sure that the $"Sprite" node exists when you try to retrieve or set that information. If it doesn't, you have a couple of options: completely ignore the get/set call, or return a default value for get and cache the value for set, then set that value to the actual parent property in _ready. The reason $Sprite wouldn't exist is if the get/set method gets called before _ready (or in case you forgot to add the Sprite child node) \$\endgroup\$
    – sleighty
    Jun 13, 2019 at 20:54

2 Answers 2

1
\$\begingroup\$

I would export the texture variable from the root node and simply set the child texture in _ready(), rather than mess around with getters and setters.

export(Texture) var my_texture 

func _ready():
    get_node("Sprite").texture = my_texture
\$\endgroup\$
2
  • 1
    \$\begingroup\$ Always the simple solution that works ;) \$\endgroup\$ Apr 25, 2018 at 22:15
  • \$\begingroup\$ Reviving this because I've been giving this issue some thought recently. What happens when some external actor decides to set my_texture on the parent? Is there a way to make it read-only? \$\endgroup\$
    – sleighty
    Jun 13, 2019 at 20:51
1
\$\begingroup\$

Let us start with your code:

export(Texture) onready var texture setget texture_set, texture_get

func texture_set(newtexture):
    $"Sprite".texture = newtexture

func texture_get():
    return $"Sprite".texture

Oh, yes we have an error, don't we? The problem is that these variables are being set before the node is added to the scene tree. Let's fix that:

export(Texture) onready var texture setget texture_set, texture_get;

func texture_set(newtexture):
    if not is_inside_tree():
        yield(self, 'ready');

    $"Sprite".texture = newtexture;

func texture_get():
    return $"Sprite".texture;

Here is_inside_tree will be false on initialization. So we use yield to have the execution of the function halt until we get the signal ready (at which point the node is in the scene tree and ready to interact with other nodes).

Wait, we cannot read it. Same reason as before. So, let us store the value of the property locally, and only talk to $"Sprite" when we are in the tree.

export(Texture) onready var texture setget texture_set, texture_get;

func texture_set(newtexture):
    texture = newtexture;
    if not is_inside_tree():
        yield(self, 'ready');

    $"Sprite".texture = texture;

func texture_get():
    if is_inside_tree():
        texture = $"Sprite".texture;

    return texture;

Et voilà. This should work.

Of course, an alternative is to have _ready copy the value to the child as per GrimGrom's answer. However, this approach will allow any changes of the value on the parent to be transferred to the child in runtime.

By the way, you can add tool at the start of the file (before extends, and before class_name if you have that) to have the changes also work in the editor. If you set this up with tool, you can use Engine.editor_hint to tell if it is running in the editor or not.


Bruno Ely said:

Reviving this because I've been giving this issue some thought recently. What happens when some external actor decides to set my_texture on the parent? Is there a way to make it read-only?

Well, yes. Make the set function do nothing:

func texture_set(_newtexture):
    pass;

You can even generate an error:

func texture_set(_newtexture):
    push_error("Hey, your code is trying to set something you don't want to be set.");

You might also be interested in push_warning(String);, assert(bool, String); and breakpoint; Similarly to push_error(String);, this only work on debug builds and on the editor.


Semicolons not necessary.

\$\endgroup\$

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .