Reputation: 305
I'm building a 2D hex based game using Godot Script. There will be multiple hex types, each with its own behaviour. My implementation plan is:
Create a single 'hex_tile' scene which will provide a sprite and a collision2D nodes
A 'HexTile' class which implements shared game logic
class_name HexTile extends Area2D
@onready var hex_sprite = $HexSprite
# Hex cube coordinates
@export var coordinate_x : int = 0
@export var coordinate_z : int = 0
@export var coordinate_y : int = 0
func _input_event(viewport: Node, event: InputEvent, shape_idx: int) -> void:
if event.is_action_pressed("mouse_left_click"):
print_my_coordinates()
func set_hex_position(_position: Vector2):
position = _position
func set_hex_cube_coordinates(row_index: int, column_index: int):
coordinate_x = row_index
coordinate_z = column_index
coordinate_y = - coordinate_x - coordinate_z
func set_texture(texture_path: String):
hex_sprite.Texture = load("res://" + texture_path)
func print_my_coordinates():
print("Cube coordinates: " + str(coordinate_x)
+ ", " + str(coordinate_y) + "; " + str(coordinate_z))
A specific class for each type of hex. This class will inherit from the generic 'HexTile' class
class_name HexTileForest extends HexTile
func print_my_coordinates():
print("I am a forest tile")
super.print_my_coordinates()
A level scene which is empty. During initiation, I instantiate each tile and assign to it the appropriate script and texture depending on its type.
class_name Level extends Node2D
func _ready():
# Initialise hex tile via code
var hex_tile_scene: PackedScene = load("res://hex_tile.tscn")
var new_hex_tile = hex_tile_scene.instantiate()
add_child(new_hex_tile)
new_hex_tile.set_script(load("res://hex_tile_forest.gd"))
new_hex_tile.set_texture("hex_tile_forest.png")
new_hex_tile.set_hex_position(Vector2(100, 200))
new_hex_tile.set_hex_cube_coordinates(0,1)
I'm getting this error in hex_tile.set_texture:
Invalid set index 'Texture' (on base: 'Nil') with value of type 'CompressedTexture2D'.
If I understand it right, this indicates that the tile instance is not set, therefore base is 'nil'. Here is a similar issue: https://www.reddit.com/r/godot/comments/ei7if7/invalid_set_index_texture_on_base_null_instance/
There are some interesting things to see in the inspector in run time:
The script is assigned as expected:
hex_sprite is empty (probably why I have an issue):
I figured this has to do with the order of initialization. I tried assigning the script to the hex instance before it's added as a child, with the same result.
Edit:
Following up on advice below I moved the code from level._ready() to level._init(). I say the same result as before. It occurred to me that perhaps variable hex_sprite is only available for use in _ready() because of the keyword:
@onready var hex_sprite = $HexSprite
There's no @oninit keyword, so I'll leave this as is. Perhaps it's be useful to try other methods of accessing a (sprite)node and updating it's content.
I injected print statements in _ready() and _init() functions for all three classes to try and understand the order in which they happen:
level is initialized
forest tile is initialized
hex tile is
initialized forest tile is ready
hex tile is ready
Level is ready
Finally, I broke off the code in Level between _ready() and _init():
class_name Level extends Node2D
func _ready():
var new_hex_tile = get_child(0)
new_hex_tile.set_texture("hex_tile_forest.png")
new_hex_tile.set_hex_position(Vector2(100, 200))
new_hex_tile.set_hex_coordinates(0,1)
func _init():
var hex_tile_scene: PackedScene = load("res://hex_tile.tscn")
var new_hex_tile = hex_tile_scene.instantiate()
new_hex_tile.set_script(load("res://hex_tile_forest.gd"))
add_child(new_hex_tile)
This works but left me confused. Obviously, _init() runs ahead of _ready(). Why is the code working when split? In both cases, I was accessing hex_sprite from _ready(). Hopefully it's not a race condition.
Upvotes: 2
Views: 713
Reputation: 111
EDIT
After some back and forth debugging with OP, the simplest solution is to switch the add_script
and add_child
lines. If that isn't an option for whatever reason, the old answer still holds value:
OLD ANSWER
First, a quick note that doesn't appear to be related to your current issue: in your set_texture
function, hex_sprite.Texture
should probably be hex_sprite.texture
("texture" instead of "Texture").
On to your actual question, I think the issue you're having isn't an initialization order problem, though that was absolutely my first thought as well. I believe the actual issue is that, when one calls set_script
, the node does not run _ready()
, nor does it appear to run @onready
statements. Instead, per the documentation, it just runs _init()
. If you take your @onready
assignments and all the code in your _ready()
function and move them into _init()
, your code should work.
Hope this helps! Let me know if this doesn't solve the problem and I can take a closer look.
Also, it might be a good idea to suggest an improvement to the documentation of set_script, since this is definitely confusing behavior that could be cleared up with a note in the docs.
Upvotes: 2