Godot Version
v4.6.2
Question
Hi, I’m new to Godot and programming as a whole so I wanted to consult some people who were a bit more experienced to figure out how I should handle this.
I’m created a grid-based ecosystem game where you can choose which animal you’d like to be and so far it’s going pretty well. The problem I keep encountering, however, is that some things get loaded in before others, for instance:
My game would load my menus then my main scene and then add my player into it.
My problem came with the health bars, which were added alongside the main scene and freaking out because they couldn’t see the player’s health components and saying values were null (which were added a few seconds later with the player). Even when I added the healthbar to the Player scene it returned null - that stumped me for a day or two. Finally I added a signal that, after the player had been added as a child to the main scene, would emit that “player was ready” and that’s worked great so far - the only annoying thing being that I have to add it to the ready function of literally anything trying to interact with something that hasn’t been ‘created’ yet.
I have a few questions:
- Is there a way around this / is this bad practice (regarding the ‘player ready’ signal)? Is there an easier way than doing what I’m doing? I just started working on a FoodSpawner script for the food in the ecosystem and am running into some issues trying to access a TileMapLayer on the main scene that hasn’t been loaded yet.
- Should I instantiate my world from the launch of the game and pause it while the player is in the menus or should I only instantiate after they’ve selected their character and pressed start?
- If I instantiate at character select, how do I access nodes that ‘don’t exist yet’?
instantiate() doesn’t run any code in your scene except _init() functions which you likely don’t use.
Your _ready() functions will run only when you add instantiated scene to engine’s main scene tree, typically via add_child().
They can’t all run at once. Only one _ready() can run at a time. The order in which they will run is determined by the order of nodes in the scene’s branch. Being aware of the order in which your _ready()s will run is important.
If something is initialized in one _ready() and some other _ready() depends on it being initialized, you need to make sure that they run in that order. This is called initialization order. You need to make sure that dependencies are always initialized before the code that depends on them runs. Otherwise you’ll get infamous “null reference” errors.
3 Likes
Its probably a good idea to make nodes that dont rely on the instantiation of others,
as @normalized stated, using a _ready() functions can only be done once per script, and as such will only run once, this in mind, it is very helpful and often all i need for setting things up in my scenes.
I recommend using @onready for node variables, and using a globals script. (which you seem to be doing)
I find its always a good practice to go the React route (dont mind if you arent familliar, its a web development framework), which would be splitting separate components into different scenes, if they work by themselves, they should work together.
I have never encountered that many issues with instantiation of nodes, what exactly is your main system?
Okay, that makes a ton of sense. So I think I’d need to instantiate my world sooner then. I’ll see if that fixes the issue on it’s own.
Yeah I’ve tried to make it so that my game is very modular so far and relies on signals to speak between each other - it’s really only the health bars that have been finicky about it so far.
Sorry not sure what you mean by main system. My main scene is a main menu, which then does change_scene_to_file to character select, and then in character select it does change_scene_to_file again to the game world (currently a small square since I’m just testing stuff).
I don’t think I have anywhere that my game world is instantiated before that point, it just switches the main scene to it when it’s ready. My game world script is actually responsible for spawning in the player that was chosen (now I’m wondering if I should change that). That’s probably why it’s having so many issues.
Apologies for the delay,
What I meant by main system is how you structure UI, objects, BGs, etc.
I use a SNES-style layering system using canvas nodes, as for the character rendering,
I highly recommend using a single player node with multiple modes based off of an ID,
here is how i made my player script’s animation controller, it should help you with yours:
func animation_controller():
gender = current_character.Sex
clothes_id = current_character.Armor.ID
accessory_id = current_character.Accessory.ID
hair_id = current_character.HairStyle
body.play(str(gender) + "_" + str(dir_index))
arms.play(str(gender) + "_" + str(dir_index))
clothes.play(str(gender) + "_" + str(clothes_id))
accessory.play(str(gender) + "_" + str(accessory_id))
hair.play(str(gender) + "_" + str(hair_id))
match dir:
"down":
dir_index = 0
accessory.z_index = 0
"up":
dir_index = 1
accessory.z_index = 3
"right":
dir_index = 2
accessory.z_index = 0
"left":
dir_index = 3
accessory.z_index = 0
clothes.frame = dir_index
accessory.frame = dir_index
hair.frame = dir_index
In my system, gender is an attribute for characters and is stored in each character’s Character Resource.
I personally think moving to a single character for all scenarios would fix almost all of your problems, since i assume you are using animated sprite 2Ds, this system should work perfectly with a simple
match animal_id: #or what your id actually is.
0:
pass #replace with logic for first animal, e.g. abilities, sprite id, states, etc.
1:
pass #and so on.
1 Like
I see what you’re saying - I did try a similar version of this but the problem is each animal will not only have different stats, movement values, ect. but also growth stages and sprites on top of that.
Those values I have saved in resources and I have no idea how to change those resources when you click to change your character. Do you know how I would go about doing that?
1 Like
Now, as for different sprites, I recommend using different animations based per index,
for instance, say index 0 is a rabbit,
you would have 0_idle, 0_walk, 0_action, etc.
And for resources, it really depends on where your resource is stored and whatnot, but
in my case, I have my SNES.gd file for constants, and inside i load the resource file for my character party. It is simple as one line:
var data = load("res://Scripts/DataHolder/Heroes/MainParty.tres") #Leads to resource.
After that, where i access the data variable (character.gd), I can change any values in the desired resource:
func _ready():
current_character = Snes.data.MainParty[index]
current_character.Accessory.ID = 0
As you may notice, you can also access resources stored inside resources and edit them as well.
1 Like
Okay, that makes a lot of sense.
I have one more question: Do you think I should have my world (which characters will stay in the whole time) be my main scene and just have a game menu that kind of overlays it until ur ready to start? Or should I keep what I have now where it changes to it after u start?
1 Like
I would recommend using a separate main menu scene and a separate world map scene, otherwise you will face issues such as characters and the world going on while in the game menu
+several other issues and whatnot.
—if you use an inventory UI or something then that would usually go in the main scene though.
1 Like