devlog.thermokar.st

2022-04-13

Godot experiments

3:15pm

Playing around with ways to avoid the “editable children” pitfall in godot. Stay tuned.

6:30pm

Node tree for our custom node called “Thing”:

Thing
├── Area2D
│   └── CollisionShape2D
└── Sprite

“Thing.gd”:

tool
extends Node2D

export var sprite: Texture = null setget set_sprite
export var collision_shape: Shape2D = null setget set_collision_shape

onready var _sprite: Sprite = $Sprite
onready var _collision_shape: CollisionShape2D = $Area2D/CollisionShape2D

func _get_configuration_warning() -> String:
    var warnings = []

    if sprite == null:
        warnings.append("missing sprite")
    if collision_shape == null:
        warnings.append("missing collision_shape")

    return PoolStringArray(warnings).join("; ")

func set_sprite(value: Texture) -> void:
    sprite = value
    if not is_inside_tree():
        yield(self, "ready")
    if _sprite != null:
        _sprite.texture = sprite

func set_collision_shape(value: Shape2D) -> void:
    collision_shape = value
    if not is_inside_tree():
        yield(self, "ready")
    if _collision_shape != null:
        _collision_shape.shape = collision_shape

The idea here is to encapsulate all of the “wiring” by exposing the public attributes as exported variables. In this case, two elements: the sprite texture (Sprite), as well as a collision box (Area2D, which requires some kind of CollisionShape-esque child).

A brief tour:

tool

This is necessary for running the script in the editor (which allows for live updates).

Godot docs: running code in the editor

export var ...

This allows for assigning instance variables via the editor UI.

Godot docs: gdscript exports

func _get_configuration_warning(): -> String:

This allows for displaying a warning (in the editor and the console), basically I’m using it here to define required attrs.

Godot docs: _get_configuration_warning

The setter functions for the sprite and collision_shape attributes allows us to wire the custom nodes attributes into the private children, as necessary. One little “gotcha” here, the yield business is for handing the case when the setter is called during initial construction of the node, otherwise the wiring never actually happens. The yield lets us pause execution until the ready signal has fired, letting us know the base node has entered the editor tree.

Godot docs: coroutines

I’ve got a few places I want to get some real-world practice with this particular pattern, so stay tuned for more info.