|
|
|
|
Reply From: |
Wakatta |
That tutorial seems pretty straight forward all nodes you want saved, add it to a Persist
group.
So open all scenes you want saved and add them to that group
Then when you call save_game()
all nodes in that group in the sceneTree get their data saved
The Parent":"/root/SystemIce/@Player@5
suggests that you are cloning objects as that naming scheme is used when a node is added to the tree while another node with the same name is already present
Yeah I already did that and its not working. What I did, and its working, is each of the parent nodes that require saves I added that code for each of them and its working good now. I need to meddle more into this save game stuff but at least this one is working for now. Its not the best but at least something is being done.
Thank you!
GoldSpark | 2022-11-20 15:23
Whoa there no no no no no !!!
Will have to stop you before you develop a bad programming habit.
You must at all cost and can’t stress this enough, avoid redundancy as it makes your code hard to repair / understand later on and it wastes CPU cycles.
So gonna walk you through exactly how this code works
Lets say your scene tree looks like this
Root
┖╴Main (World.gd)
┠╴HUD
┃ ┖╴Camera2D *
┠╴Player *
┃ ┖╴Ship *
┖╴Enemy *
All nodes with the *
are part of the Persist
group
Only the Main node has a scrip that looks like this
# World.gd
func _ready():
load_game()
func _exit_tree():
save_game()
func save_game():
# all save game function data
func load_game():
all load game function data
In the save / load blocks
# This line gets all nodes within the persist group
# In the above example that will be var save_nodes = [Camera2D, Player, Ship, Enemy]
var save_nodes = get_tree().get_nodes_in_group("Persist")
# This line loops through each node in save_nodes
# assigning each value to (`i`)
for i in save_nodes:
# so i = Camera2D
# then i = Player
# then i = Ship
# then i = Enemy
# then you can modify each node (`i`) as needed for example
i.queue_free() #deletes the node
Notes
You can assign the the save_game()
and load_game()
function to button presses instead
Wakatta | 2022-11-20 16:13
Hey thank you. Yes I did that and it crashes the second time I save the game. I have NPC Ships which have different values and inventory and player. The player parent path keeps showing like @Player@5 and I have no idea how to fix that. I know you’ve said that its because Player node is already in the scene but that player node is being deleted every time in that load function at first so I don’t understand how its getting duplicated.
Thank you you are right about the habit but I can’t see other way around this its insanely complicated at least for me for now.
What I will do I will make only one script which saves all these nodes in separate files. Its only 3 nodes which requires this.
So here is my player load and save:
public void SaveScene()
{
File saveGame = new File();
saveGame.Open("user://player.dat", File.ModeFlags.Write);
Godot.Collections.Array saveNodes = GetTree().GetNodesInGroup("Persist");
foreach (Node saveNode in saveNodes)
{
if (IsAParentOf(saveNode))
{
//Check the node is an instanced scene so it can be instanceds again during load
if (saveNode.Filename.Empty())
{
GD.Print(String.Format("persistent node '{0}' is not an instanced scene, skipping", saveNode.Name));
continue;
}
if (!saveNode.HasMethod("Save"))
{
GD.Print(String.Format("persistent node '{0}' is missing a Save() function, skipping", saveNode.Name));
continue;
}
//Call the nodes save func
var nodeData = saveNode.Call("Save");
//Store the save dictionary as a new line in the save file
saveGame.StoreLine(JSON.Print(Save()));
saveGame.StoreLine(JSON.Print(nodeData));
}
}
saveGame.Close();
}
public void LoadScene()
{
var saveGame = new File();
if (!saveGame.FileExists("user://player.dat"))
return;
var saveNodes = GetTree().GetNodesInGroup("Persist");
foreach (Node saveNode in saveNodes)
{
if (IsAParentOf(saveNode))
{
saveNode.Free();
}
}
//Load the file line by line and process that dictionary to restore the object
//it represents
saveGame.Open("user://player.dat", File.ModeFlags.Read);
bool firstLine = true;
while (saveGame.GetPosition() < saveGame.GetLen())
{
//get the saved dictionary from the next line in the save file
var nodeData = new Godot.Collections.Dictionary<string, object>(
(Godot.Collections.Dictionary)JSON.Parse(saveGame.GetLine()).Result
);
if (!firstLine)
{
//Firstly, we need to create the object and add it to the tree and sets its position
var newObjectScene = (PackedScene)ResourceLoader.Load(nodeData["Filename"].ToString());
Ship newObject = newObjectScene.Instance<Ship>();
newObject.Set("global_position", new Vector2((float)nodeData["PosX"], (float)nodeData["PosY"]));
foreach (KeyValuePair<string, object> entry in nodeData)
{
string key = entry.Key.ToString();
if (key == "Filename" || key == "Parent" || key == "PosX" || key == "PosY")
continue;
newObject.Set(key, entry.Value);
}
AddChild(newObject);
//Now set the remaining variables
//Set up weapons
{
newObject.LoadGameShipWeapons();
}
}
else
{
foreach (KeyValuePair<string, object> entry in nodeData)
{
string key = entry.Key.ToString();
if (key == "Filename")
continue;
string v = "" + entry.Value;
Set(key, entry.Value);
if(key == "credit")
{
credit = Int32.Parse(v);
}
}
firstLine = false;
}
}
saveGame.Close();
}
and here is my normal npc ships load and save:
public void SaveScene()
{
File saveGame = new File();
saveGame.Open("user://npcships.dat", File.ModeFlags.Write);
Godot.Collections.Array saveNodes = GetTree().GetNodesInGroup("Persist");
foreach (Node saveNode in saveNodes)
{
if (IsAParentOf(saveNode))
{
//Check the node is an instanced scene so it can be instanceds again during load
if (saveNode.Filename.Empty())
{
GD.Print(String.Format("persistent node '{0}' is not an instanced scene, skipping", saveNode.Name));
continue;
}
if (!saveNode.HasMethod("Save"))
{
GD.Print(String.Format("persistent node '{0}' is missing a Save() function, skipping", saveNode.Name));
continue;
}
//Call the nodes save func
var nodeData = saveNode.Call("Save");
//Store the save dictionary as a new line in the save file
saveGame.StoreLine(JSON.Print(nodeData));
}
}
saveGame.Close();
}
public void LoadScene()
{
var saveGame = new File();
if (!saveGame.FileExists("user://npcships.dat"))
return;
var saveNodes = GetTree().GetNodesInGroup("Persist");
foreach (Node saveNode in saveNodes)
{
if (IsAParentOf(saveNode))
{
saveNode.QueueFree();
}
}
//Load the file line by line and process that dictionary to restore the object
//it represents
saveGame.Open("user://npcships.dat", File.ModeFlags.Read);
while (saveGame.GetPosition() < saveGame.GetLen())
{
//get the saved dictionary from the next line in the save file
var nodeData = new Godot.Collections.Dictionary<string, object>(
(Godot.Collections.Dictionary)JSON.Parse(saveGame.GetLine()).Result
);
//Firstly, we need to create the object and add it to the tree and sets its position
var newObjectScene = (PackedScene)ResourceLoader.Load(nodeData["Filename"].ToString());
Ship newObject = newObjectScene.Instance<Ship>();
//Now set the remaining variables
foreach (KeyValuePair<string, object> entry in nodeData)
{
string key = entry.Key.ToString();
if (key == "Filename" || key == "Parent" || key == "PosX" || key == "PosY")
continue;
newObject.Set(key, entry.Value);
}
AddChild(newObject);
newObject.Set("global_position", new Vector2((float)nodeData["PosX"], (float)nodeData["PosY"]));
//Set up weapons
{
newObject.LoadGameShipWeapons();
}
}
saveGame.Close();
}
They are different and I don’t know how to make all of this in one function…
I also made sure the game auto saves every 10 minutes and when the user exists so it does not waste cycles so much.
Also the ship sometimes that the player is using loads before the player and it crashes the game. Its so complicated I just think that having three separate functions is good enough.
Then what happens is some nodes require those nodes and those some nodes are or arentloaded first and it crashes ah … my brain is completely gone , if I even had one…
GoldSpark | 2022-11-20 16:25
All I do is call this one function `time += delta;
if (time > increase)
{
Godot.Collections.Array saveNodes = GetTree().GetNodesInGroup("Saveable");
foreach(Node node in saveNodes)
{
node.Call("SaveScene");
}
GD.Print("Game Saved");
increase = 8 * 60;
time = 0f;
}
`
and in turn it calls the nodes that has those children and its all well…
GoldSpark | 2022-11-20 16:53