Hi all,
I’m trying to write a create method for one of my scene (CharacterPortrait), to call it from another scene (CharacterListScreen) and add multiple instances of it to a grid container , but while the scene instance seems to be created, the child nodes, in this case TextureRect “portrait_texture” stays at null and the code crashes when trying to access it.
here is in order, the script where I wrote the create() method, the tree of its linked scene, and the script and tree of the scene where I’m trying to call this create() method
character_portrait.gd
extends Control
class_name CharacterPortrait
@onready var banned_indicator = $GlobalContainer/InnerMarginContainer/BannedIndicator
@onready var portrait_texture = $GlobalContainer/InnerMarginContainer/PortraitTexture
const PORTRAITS_FOLDER_PATH = "res://Assets/CharacterPortraits/"
static var char:Character
static var p_name:String
var is_banned:bool
# Called when the node enters the scene tree for the first time.
func _ready():
print("character portrait is ready")
is_banned = false
# Called every frame. 'delta' is the elapsed time since the previous frame.
func _process(delta):
if is_banned:
banned_indicator.visible = true
static func create(c_name:String) -> CharacterPortrait:
var instance:CharacterPortrait = CharacterPortrait.new()
var dir_access:DirAccess = DirAccess.open(PORTRAITS_FOLDER_PATH)
for file_path in dir_access.get_files():
if file_path.contains(c_name) and !file_path.contains(".import"):
print("Found texture for character: ",c_name)
var img = Image.new()
var itex = ImageTexture.new()
img.load(PORTRAITS_FOLDER_PATH+file_path)
itex.create_from_image(img)
instance.portrait_texture.texture = itex
instance.p_name = c_name
return instance
func set_character(_character:Character):
char = _character
p_name = _character.character_name
CharacterPortrait.tscn
character_list_screen.gd
extends Control
@onready var grid_container:GridContainer = $ColorRect/GridContainer
# Called when the node enters the scene tree for the first time.
func _ready():
for _name in CharacterManager.get_character_names_list():
var portrait:CharacterPortrait = CharacterPortrait.create(_name)
grid_container.add_child(portrait)
# Called every frame. 'delta' is the elapsed time since the previous frame.
func _process(delta):
pass
character_list_screen.tscn
instance.portrait_texture.texture = itex fails, saying portrait_texture is null
Are you trying to make a character portrait selection screen? or did I misinterpret the code?
If you’re going for that, I don’t think that’s the best way to load a picture. It’s better to have all the images preloaded. However if you have to do it dynamically, sure, you can call load instead of preload.
Then you can use that variable to assign the picture. I’d recommend you create a TextureRect, then assign the pictures and then add them to the GridContainer.
CharacterPortrait is not a scene, think of it like a Node+Script or Class.
Scene is .tscn file, it holds Nodes as tree, Scripts and changed properties of those nodes
@onready vars, like _ready() function become valid after this class/scene will be added into SceneTree (), @onready variables will be null while scene not be added with add_child(scene)
static var is kind of data that same for all class instances, you need usual var for per-instance data
Correct way to do that you want is create variables for name and texture_path
In _ready you must create texture from texture_path and assign it to portrait_texture.texture
Do not use preload("CharacterPortrait.tscn") in this script or you will get cyclil loading, correct way is to use load
func _ready():
for _name in CharacterManager.get_character_names_list():
var portrait:CharacterPortrait = CharacterPortrait.new()
grid_container.add_child(portrait)
portrait.create(_name)
This happens right when calling add_child(), I changed the order and updated the create() method following you comment so it does not seem related.
With “placeholder_portrait_texture” being the path to one of my png files.