Page 1 of 2
Restoring a previous state
Posted: Mon Oct 15, 2018 8:08 am
by GorkaGames
Hi,
With the save system, is it possible to restore to a previos state without loading?
What I wont is to restore the state of all my objects to a state that I already saved reaching a node on the quest.
For instance on my game if the player dies, it destroys, but I don't load the scene again (its an open world and it takes a while to load it), so I'd like to call the save system and "load" the previous state game on the previous node on the quest.
If not, any work around you may think?
Thanks.
Re: Restoring a previous state
Posted: Mon Oct 15, 2018 10:32 am
by Tony Li
Hi,
You can certainly restore quest states and GameObject positions. But if you've destroyed an object, Quest Machine's save system will not bring it back. It assumes that you're going to reload the scene to get the original objects back. After reloading the scene, the save system's Persistent Destructible component will check if the GameObject has been marked as destroyed in the saved game. If so, it will destroy the GameObject.
What if you break the scene into smaller parts? You can use something like SECTR Stream to stream in pieces of the world as the player moves around.
Re: Restoring a previous state
Posted: Mon Oct 15, 2018 12:48 pm
by GorkaGames
Ok, if I reload the scene, I guess I need also 2 different states (I think we need therefore 2 saving slots)
- In case the player quits the game, once he comes back everything must be on the same position and the same enemies killed, so we should save the game on the slot "quit game" and load again the slot "quit game" once he is back.
- In case the player dies, the scene reloads and I want the player on the BEGGINING (Start) of the Node / Quest he was in, so once the Scene reloads everything must be at the same position and none enemies killed, as it was at the beginning of this Node. So we should save the game on the slot "player death" and load again the slot "player death" once he is back.
Is this ok? what's the code do I need and where to accomplish that? Thanks
Re: Restoring a previous state
Posted: Mon Oct 15, 2018 1:32 pm
by Tony Li
Hi,
Let's say you define the slot numbers like this:
Code: Select all
public const int SavedGameSlot = 0;
public const int RestartQuestSlot = 1;
When the player quits, save to SavedGameSlot. It would be best to put this in a save/load/quit menu. But as a quick test you can do this:
Code: Select all
void OnApplicationQuit() {
PixelCrushers.SaveSystem.SaveToSlot(SavedGameSlot);
}
To resume the game:
Code: Select all
PixelCrushers.SaveSystem.LoadFromSlot(SavedGameSlot);
When a quest becomes active, save to RestartQuestSlot:
Code: Select all
using UnityEngine;
using PixelCrushers;
using PixelCrushers.QuestMachine;
public class MyScript : MonoBehaviour, IMessageHandler) {
void OnEnable() { // Let me know when quest states change.
MessageSystem.AddListener(this, QuestMachineMessages.QuestStateChangedMessage);
}
void OnDisable() { // Unregister when disabling script.
MessageSystem.RemoveListener(this);
}
void OnMessage(MessageArgs messageArgs) {
var state = (QuestState)messageArgs.values[1];
if (messageArgs.values[0] != null) {
// Is a node. We don't care, so exit.
return;
}
if (state == QuestState.Active) { // Quest became active. Save game.
SaveSystem.SaveToSlot(RestartQuestSlot);
}
}
When the player dies, reload RestartQuestSlot:
Code: Select all
SaveSystem.LoadFromSlot(RestartQuestSlot);
Re: Restoring a previous state
Posted: Mon Oct 15, 2018 2:09 pm
by GorkaGames
ok, great!!
The following: if (state == QuestState.Active) I guess is only for the general quest, not for the individual nodes, is that correct? If so, how it would be for checking that a Node (And not the full Quest) is just Active.
And I'd like to do it preferibly only for the Main Quests, how can I ask if a quest is from the group "Main" ?
In case I want to craft manually which Nodes on every quest I want to save the state, is there any action on the editor to do so? I don't remember now....
Re: Restoring a previous state
Posted: Mon Oct 15, 2018 2:27 pm
by Tony Li
Hi,
GorkaGames wrote: ↑Mon Oct 15, 2018 2:09 pmThe following: if (state == QuestState.Active) I guess is only for the general quest, not for the individual nodes, is that correct? If so, how it would be for checking that a Node (And not the full Quest) is just Active.
The message is sent when a quest or quest node state changes. The quest node is in messageArgs.values[0]. Its type is
StringField. The example below only saves if the quest ID is "Bake Pie" and the node ID is "Get 5 Apples":
Code: Select all
public void OnMessage(MessageArgs messageArgs) {
StringField nodeID = messageArgs.values[0] as StringField;
if (nodeID == null) {
return; // Is main quest. We're only looking for the "Get 5 Apples" node.
}
QuestNodeState state = (QuestNodeState)messageArgs.values[1];
if (messageArgs.parameter == "Bake Pie" && nodeID == "Get 5 Apples" && state == QuestNodeState.Active) {
SaveSystem.SaveToSlot(RestartQuestSlot);
}
}
(Edit: Quests use QuestState. Quest nodes use QuestNodeState. I fixed my code above.)
The
QuestMachineMessages class explains all of the messages. Scroll down to "Public Attributes", and click on a message to get more information. For example, this is the information for the QuestStateChangedMessage:
Sent when a quest state or quest node state changes.
- Parameter: Quest ID.
- Argument 0: [StringField] Quest node ID, or blank for main quest state.
- Argument 1: [QuestState] / [QuestNodeState] New state.
GorkaGames wrote: ↑Mon Oct 15, 2018 2:09 pmAnd I'd like to do it preferibly only for the Main Quests, how can I ask if a quest is from the group "Main" ?
Check the Quest's group property:
Code: Select all
Quest quest = QuestMachine.GetQuestInstance("Bake Pie");
if (quest.group == "Main") {
Debug.Log("This is a Main quest.");
}
GorkaGames wrote: ↑Mon Oct 15, 2018 2:09 pmIn case I want to craft manually which Nodes on every quest I want to save the state, is there any action on the editor to do so? I don't remember now....
No, sorry. When you save a quest, it saves the state of the quest, the states of all of the nodes, and the values of the counters.
Re: Restoring a previous state
Posted: Wed Oct 17, 2018 4:40 pm
by GorkaGames
Ok, so I guess this is finally the code to save when a NODE gets active:
I'm getting 2 errors:
1. IMessageHandler Can't implement because its not public
2. AddListener Not possible 2 Arguments
NOTE: I think this can be a nice feature also for your product, people will love to save the game on every Node automatically (as control points automatically)
using UnityEngine;
using PixelCrushers;
using PixelCrushers.QuestMachine;
public class MyScript : MonoBehaviour, IMessageHandler) {
void OnEnable()
{ // Let me know when quest states change.
MessageSystem.AddListener(this, QuestMachineMessages.QuestStateChangedMessage);
}
void OnDisable()
{ // Unregister when disabling script.
MessageSystem.RemoveListener(this);
}
void OnMessage(MessageArgs messageArgs)
{
//Recoge Cambio en el estado de una Quest
//QuestState state = (QuestState)messageArgs.values[1];
//Recoge Cambio en el estado del nodo de una Quest
QuestNodeState state = (QuestNodeState)messageArgs.values[1];
StringField nodeID = messageArgs.values[0] as StringField;
if (nodeID == null)
{
return; // Is main quest.
}
//Comprueba si es activa la quest
//if (state == QuestState.Active)
//Comprueba si es activa el nodo de la quest
if (state == QuestNodeState.Active)
{ // Quest Node became active. Save game.
//Guardamos en el slot 0, que lo reservamos para guardar los estados de cambios de Nodos / quests
SaveSystem.SaveToSlot(0);
Debug.Log("Save Quest Node");
if (Invector.vCharacterController.vHUDController.instance)
Invector.vCharacterController.vHUDController.instance.ShowText("Save Quest Node");
}
}
}
Re: Restoring a previous state
Posted: Wed Oct 17, 2018 5:01 pm
by Tony Li
Hi,
GorkaGames wrote: ↑Wed Oct 17, 2018 4:40 pm1. IMessageHandler Can't implement because its not public
IMessageHandler is public. It looks like you may have a typo. Try removing the ")" after it:
Code: Select all
public class MyScript : MonoBehaviour, IMessageHandler {
If that doesn't work, then your project may have another IMessageHandler from another asset. In this case, specify the PixelCrushers one:
Code: Select all
public class MyScript : MonoBehaviour, PixelCrushers.IMessageHandler {
GorkaGames wrote: ↑Wed Oct 17, 2018 4:40 pm2. AddListener Not possible 2 Arguments
MessageSystem.AddListener() expects three arguments. The third argument is the message parameter. If you want to ignore the message parameter, pass null:
Code: Select all
MessageSystem.AddListener(this, QuestMachineMessages.QuestStateChangedMessage, null);
GorkaGames wrote: ↑Wed Oct 17, 2018 4:40 pmNOTE: I think this can be a nice feature also for your product, people will love to save the game on every Node automatically (as control points automatically)
I'll make a note for the next version after 1.0.8. I'm not adding any new features to 1.0.8 because I'm trying to release it as soon as possible. Only a few third party integrations are left to complete for 1.0.8.
Code: Select all
using UnityEngine;
using PixelCrushers;
using PixelCrushers.QuestMachine;
public class MyScript: MonoBehaviour, PixelCrushers.IMessageHandler {
void OnEnable() { // Let me know when quest states change.
MessageSystem.AddListener(this, QuestMachineMessages.QuestStateChangedMessage, string.Empty);
}
void OnDisable() { // Unregister when disabling script.
MessageSystem.RemoveListener(this);
}
public void OnMessage(MessageArgs messageArgs) {
//Recoge Cambio en el estado de una Quest
//QuestState state = (QuestState)messageArgs.values[1];
//Recoge Cambio en el estado del nodo de una Quest
QuestNodeState state = (QuestNodeState) messageArgs.values[1];
StringField nodeID = messageArgs.values[0] as StringField;
if (nodeID == null) {
return; // Is main quest.
}
//Comprueba si es activa la quest
//if (state == QuestState.Active)
//Comprueba si es activa el nodo de la quest
if (state == QuestNodeState.Active) { // Quest Node became active. Save game.
//Guardamos en el slot 0, que lo reservamos para guardar los estados de cambios de Nodos / quests
SaveSystem.SaveToSlot(0);
Debug.Log("Save Quest Node");
if (Invector.vCharacterController.vHUDController.instance)
Invector.vCharacterController.vHUDController.instance.ShowText("Save Quest Node");
}
}
}
Re: Restoring a previous state
Posted: Wed Oct 17, 2018 5:27 pm
by GorkaGames
I have updated the code but the same. Those arre the errors on the editor: (I have already updated to the beta you sent me). Add Listener is with errors also in Visual Studio-The call is ambiguous:
Assets/Scripts/Quests/QuestNodeControllerSaver.cs(11,23): error CS0121: The call is ambiguous between the following methods or properties: `PixelCrushers.MessageSystem.AddListener(PixelCrushers.IMessageHandler, string, string)' and `PixelCrushers.MessageSystem.AddListener(PixelCrushers.IMessageHandler, string, PixelCrushers.StringField)'
Assets/Scripts/Quests/QuestNodeControllerSaver.cs(6,14): error CS0737: `QuestNodeControllerSaver' does not implement interface member `PixelCrushers.IMessageHandler.OnMessage(PixelCrushers.MessageArgs)' and the best implementing candidate `QuestNodeControllerSaver.OnMessage(PixelCrushers.MessageArgs)' is not public
Re: Restoring a previous state
Posted: Wed Oct 17, 2018 5:41 pm
by Tony Li
Sorry, use string.Empty instead of null.