Alright, so here’s the final solution I came to: PackedScene kinda sucks lol, if you wanna preserve your stuff at runtime there’s better ways to do it. My solution is as follows:
Create a Scene Manager
Main reason why I didn’t initially go for a scene manager was because every resource seemed really complicated to follow along because they were either mostly older tutorials or just didn’t make that much sense to me in the scope of my current project. My current project has 3 game states it needs to go through: Title, Map and Combat. This is not supposed to be that complicated. So here’s what I came up with:
extends Node
var mainTitleScreenClass = preload("res://Assets/Scenes/Title.tscn")
var mainTitleScreen
var testLevelClass = preload("res://Assets/Scenes/LevelTest.tscn")
var testLevel
func _ready() -> void:
mainTitleScreen = mainTitleScreenClass.instantiate()
add_child(mainTitleScreen)
var startButton = mainTitleScreen.get_node("Start Button")
startButton.pressed.connect(_on_press_start)
func _on_press_start():
if is_instance_valid(testLevel) != true:
testLevel = testLevelClass.instantiate()
var backButton = testLevel.get_node("GUI/Return Button")
backButton.pressed.connect(_return_to_title)
remove_child(mainTitleScreen)
add_child(testLevel)
func _return_to_title():
testLevel.queue_free()
add_child(mainTitleScreen)
Code Breakdown
mainTitleScreenClass and testLevelClass, for a lack of a better term or understanding of the engine with regards to what preloading fully does, serve as classes for mainTitleScreen and testLevel respectively when they’re both instantiated. These 4 variables currently are defined outside of any function within the script so they can be referenced between functions, which is why they’re at the top. At runtime, _ready() will trigger and instantiate the mainTitleScreenClass variable to the mainTitleScreen variable as a node, add it to the Scene Manager node, then we drill down for a start button within mainTitleScreen using get_node() to find an element within that scene, in this case it’s just a child called “Start Button”, and then we can give it a behavior by connecting the pressed signal to reference the _on_press_start() function.
Then, once _on_press_start() is triggered through the button press, it’ll do a check on testLevel to see if it’s a valid node or not, and if it isn’t true, then it’ll instantiate on testLevel to make it a proper node with correct button controls to switch scenes, then it removes the referenced mainTitleScreen and adds the testLevel Node to the Scene Manager Node. I mainly added button controls in testLevel to test it, but I should very easily be able to rework it as part of a hideable settings menu that you access at run time to return back to title screen.
Lastly, _return_to_title() was mainly used to test the functionality to make sure it works.
func _return_to_title():
remove_child(testLevel)
add_child(mainTitleScreen)
This was the original snipit of code I used to test the functionality, works great, I was able to swap between the two screens no problem, everything kept working as intended and nothing broke in terms of _process() scripts or timers baked into the level, which means I can very well use this during the gameplay to transition between the Map and Combat, I’d just have to make sure the game stores the pause state in the global variables or something. That, or figure out how to set the specific node to be paused before the remove_child(testLevel) happens. The sanity check for why I did testLevel.queue_free() was because queue_free() deletes the currently existing level data so that it wipes out for the variable when you head back to the title screen. That way, the “Start Button” actually does what it’s supposed to and starts a fresh game for the player when they click on it, because testLevel would remain in memory otherwise, which wouldn’t make for a good game if you could only cleanly play it once lol.
Now, you may be asking “Aren’t there 3 scenes you need to switch between?” Yes, but I actually will need to make the Combat scene, which means looking up a lot of stuff on making shooting gallery games, but that will be for another time, I still need to implement enemy collisions and even create the main player objects you’ll move around on the map. For now, I’m happy with this.