Rewind to previous Choice Unity (QUESTION)
Hi there,
We are working on a turnbased strategy/visual novel game using Unity and we have implemented our story using ink. I'm not sure if this question has been asked before, we were wondering if it is possible to implement a similar function call that is used in the ink editor called "Rewind a single choice" in unity. This would be really useful while testing our metagame, so we can jump back to a previous choice without exiting playmode in Unity.

Thanks in advance, -Samuel Brooke
Hey! The best way to go about this is to save the story state before making a choice and then load it on rewind.
I'm afraid this isn't really possible to build into the API because of differences in the way progress gets saved in a real game v.s. in Inky.
In Inky, progress is saved in a "fragile" way by simply saving the sequence of choices that you took. When you rewind a single step, it resets and plays through up to that point by picking the same choices (by choice index).
In a proper game, we more robustly save the entire state of the ink engine - it doesn't simply play through your choices again from scratch. However this does mean you can't easily rewind.
You can implement this in your individual game by saving a copy of the save state at certain points, so you have somewhere to rewind to.
Thank you guys for the quick reply, this will definitely help us to get started! We will try saving the state as a separate copy as you've both suggested.
Cheers, -Samuel Brooke
Hey guys, I just wanted to let you know that I got it working exactly as I intended! The solution is pretty hacky, but it seems to work really well for testing purposes. Basically I'm saving the state every time we continue the dialogue and I add each saved string (Json) into a list of strings. When the rewind button is pressed you can cycle backwards through each state until the very start and after rewinding it removes the save we were previously at to avoid duplicating saved states.
Cheers and have an awesome weekend!
-Samuel Brooke
Great, glad you got it working!
And that’s not hacky at all, it’s exactly the approach we’d recommend (and what we did in Sorcery for example, with some tweaks for efficiency).
Perhaps an official implementation since many people seem to need this? (Ren'Py games do this, and it's super nice, so I want to implement it, too)
Hi Samuel, can you please share the code? I have some problems with rewinding after making a choice, so I'm interested how you solved it. Thanks
Hi Samuel, can you please share the code? I have some problems with rewinding after making a choice, so I'm interested how you solved it. Thanks
Maybe if you create options that reference other functions. And assign their selection to the scroll buttons. Maybe it's worth reserving Choice.text?
Hey anyone asking for the code snippet to get this working here is what I did back in the day. @HoustonProblems @sdad711 Keep In Mind This was written a long time ago and I haven't worked in unity since so I've no idea if it even follows current workflows of unity. this was using unity 2018 I think.
Hope it works or helps!
- Sam
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;
using UnityEngine;
using UnityEngine.SceneManagement;
public class SavedData : MonoBehaviour
{
public static SavedData instance;
[Header("Added to SaveFile")]
public List<string> savedStateHistory = new List<string>();
public int savedHistoryIndex = 0;
void Awake()
{
if (instance == null)
{
DontDestroyOnLoad(gameObject);
instance = this;
}
else if (instance != this)
{
Destroy(gameObject);
return;
}
}
public void LoadGame()
{
if (File.Exists(Application.persistentDataPath + "/playerSaveData.dat"))
{
BinaryFormatter bin = new BinaryFormatter();
FileStream file = File.Open(Application.persistentDataPath + "/playerSaveData.dat", FileMode.Open);
SavedDataObject data = (SavedDataObject)bin.Deserialize(file);
file.Close();
//Logic here
savedInk = data.savedInk;
currency = data.savedCurrency;
}
}
// for rewinding in the metagame
public void LoadPreviousState()
{
if (File.Exists(Application.persistentDataPath + "/playerSaveData.dat"))
{
BinaryFormatter bin = new BinaryFormatter();
FileStream file = File.Open(Application.persistentDataPath + "/playerSaveData.dat", FileMode.Open);
SavedDataObject data = (SavedDataObject)bin.Deserialize(file);
file.Close();
//Logic here
if(savedStateHistory.Count > 1)
{
savedHistoryIndex -= 1;
savedInk = data.savedInkHistory[savedHistoryIndex - 1];
savedStateHistory.RemoveAt(savedStateHistory.Count - 1);
data.savedInkHistory.RemoveAt(data.savedInkHistory.Count - 1);
//savedStateHistory = data.savedInkHistory;
}
else
{
savedStateHistory.Clear();
savedHistoryIndex = 0;
MetaManager.instance.ink.RefreshView();
}
}
}
public void SaveGame()
{
//Prevents new file created over again.
FileStream file;
if (File.Exists(Application.persistentDataPath + "/playerSaveData.dat"))
{
//file = File.Open(Application.persistentDataPath + "/playerSaveData.dat", FileMode.Open);
File.Delete(Application.persistentDataPath + "/playerSaveData.dat");
}
file = File.Create(Application.persistentDataPath + "/playerSaveData.dat");
BinaryFormatter bin = new BinaryFormatter();
SavedDataObject data = new SavedDataObject();
//Logic here
data.savedInk = savedInk;
savedStateHistory.Add(savedInk);
savedHistoryIndex += 1;
data.saveInkHistoryIndex = savedHistoryIndex;
data.savedInkHistory = savedStateHistory;
/*Debug.Log("saved ink added to saved ink history");
Debug.Log("saved ink history index : " + data.saveInkHistoryIndex);
foreach(string sI in data.savedInkHistory)
{
Debug.Log("savedInk History content: " + data.savedInkHistory.Count.ToString() );
}
*/
bin.Serialize(file, data);
file.Close();
}
}
[Serializable]
public class SavedDataObject
{
public string savedInk;
public List<string> savedInkHistory = new List<string>();
public int saveInkHistoryIndex;
public int savedCurrency;
}