Forum rules - please read before posting.

saving during a cutscene?

edited December 2024 in Technical Q&A

I'm making a visual novel with adventure game elements and exploration. About 80% of the game is cutscenes (Dialogue: Play Speech and Conversation choices, with the other 20% controlling a player character and interacting with NPCs and puzzles.

It dawned on me today that I can't save during a cutscene (whoops!). Is there a workaround for this? I thought** "Clickable in cutscenes"** in the Save menu would solve the issue but nope! Do I need to redo these dialogue scene action lists...?

Comments

  • edited December 2024

    I think I figured it out. In the cutscene's Inspector under Properties I set the "When Running:" form to "Run In Background" and it gave me the freedom I needed.

    Now the issue I'm running into is the dialogue don't display forever until user skips them. :(

  • Be caredul with this adjustment - AC prevents saving during cutscenes on purpose, as the state of ActionLists (and that of objects being affected) can't be guaranteed to be properly recorded within the save data.

    Now the issue I'm running into is the dialogue don't display forever until user skips them.

    That'll be a byproduct of the ActionLists now running in the background, since their display no longer blocks gameplay. I would recommend reverting this change and looking into other options.

  • My only end goal is to be able to save between subtitles for the heavier reading parts of the game. Conversations are on a timer so I wouldn’t want saving there. And during the adventure mode and puzzle sections everything works as intended. If only I could find a way to pause/save during the dialogue. Hmm.

    I read that the state of action lists could be saved using “ActionList: Pause or resume.” Could I leverage this somehow?

    I’m open to suggestions.

    I do have Dialogue System as a plug-in but I’ve never used it before and it’d be a shame if I had to redo all the writing!

    I’m also using unity 6 LTS and AC 1.82 if that helps.
  • I read that the state of action lists could be saved using “ActionList: Pause or resume.” Could I leverage this somehow?

    Possibly. How are your dialgoue sections arranged? A series of separate Cutscenes for each section, or are you relying on ActionList asset files?

  • I use a series of separate cutscenes for each section.

  • Firstly, you'll need to make a correction to AC's ActionListManager file. Open it up and replace its LoadData function with the following:

    public void LoadData (string dataString, SubScene subScene = null)
    {
        if (string.IsNullOrEmpty (dataString)) return;
    
        string[] dataArray = dataString.Split (SaveSystem.colon[0]);
    
        // ID
        string listName = AdvGame.PrepareStringForLoading (dataArray[0]);
        resumeIndices = new int[0];
    
        // Resume
        string[] resumeData = dataArray[1].Split ("]"[0]);
        if (resumeData.Length > 0)
        {
            List<int> resumeIndexList = new List<int>();
            for (int i=0; i<resumeData.Length; i++)
            {
                int resumeIndex = -1;
                if (int.TryParse (resumeData[i], out resumeIndex) && resumeIndex >= 0)
                {
                    resumeIndexList.Add (resumeIndex);
                }
            }
            resumeIndices = resumeIndexList.ToArray ();
        }
    
        // StartIndex
        int.TryParse (dataArray[2], out startIndex);
    
        // Skip queue
        int j = 0;
        int.TryParse (dataArray[3], out j);
        inSkipQueue = (j == 1) ? true : false;
    
        // IsRunning
        j = 0;
        int.TryParse (dataArray[4], out j);
        isRunning = (j == 1) ? true : false;
    
        // Conversation on end
        int convID = 0;
        int.TryParse (dataArray[5], out convID);
        if (convID != 0)
        {
            if (subScene != null)
            {
                conversationOnEnd = ConstantID.GetComponent <Conversation> (convID, subScene.gameObject.scene);
            }
            else
            {
                conversationOnEnd = ConstantID.GetComponent <Conversation> (convID);
            }
        }
    
        // Parameter data
        parameterData = dataArray[6];
    
        // ActionList
        if (!string.IsNullOrEmpty (listName))
        {
            // Scene component?
            int ID = 0;
            if (int.TryParse (listName, out ID))
            {
                // Scene
                ConstantID constantID = (subScene != null)
                    ? ConstantID.GetComponent (ID, subScene.gameObject.scene)
                    : ConstantID.GetComponent (ID);
    
                if (constantID)
                {
                    actionList = constantID.GetComponent <ActionList>();
                    if (actionList)
                    {
                        KickStarter.actionListManager.AddToList (this);
                    }
                    return;
                }
            }
    
            // Asset file
            #if AddressableIsPresent
            if (KickStarter.settingsManager.saveAssetReferencesWithAddressables)
            {
                Addressables.LoadAssetAsync<ActionListAsset> (listName).Completed += OnCompleteLoad;
            }
            else
            #endif
            {
                ActionListAsset tempAsset = ScriptableObject.CreateInstance<ActionListAsset> ();
                actionListAsset = AssetLoader.RetrieveAsset<ActionListAsset> (tempAsset, listName);
    
                if (actionListAsset == null || actionListAsset == tempAsset)
                {
                    ACDebug.LogWarning ("Could not restore data related to the ActionList asset '" + listName + "' - to restore it correctly, the asset must be placed in a folder named Resources.");
                }
                else
                {
                    KickStarter.actionListAssetManager.AddToList (this);
                }
            }
        }
    }
    

    (I will make the same change to the next release)

    Next, you'll need to bypass AC's save Action in favour of a Button that runs a custom script to save. Test this out with a simple menu named SaveTest, and a Button named Save, set to appear during Cutscenes and "Clickable during cutscenes".

    Set the Button's click type to Custom Script, and then attach this to the scene:

    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    using AC;
    
    public class CustomSave : MonoBehaviour
    {
    
        public List<ActionList> saveableActionLists = new List<ActionList> ();
    
        void OnEnable ()
        {
            EventManager.OnMenuElementClick += OnMenuElementClick;
            EventManager.OnFinishLoading += OnFinishLoading;
        }
    
        void OnDisable ()
        {
            EventManager.OnMenuElementClick -= OnMenuElementClick;
            EventManager.OnFinishLoading -= OnFinishLoading;
        }
    
        void OnMenuElementClick (Menu menu, MenuElement element, int slot, int input)
        {
            if (menu.title == "SaveTest" && element.title == "Save")
            {
                StartCoroutine (SaveCo ());
            }
        }
    
        IEnumerator SaveCo ()
        {
            foreach (var activeList in KickStarter.actionListManager.ActiveLists)
            {
                if (activeList.IsRunning () && activeList.actionList && saveableActionLists.Contains (activeList.actionList))
                {
                    activeList.actionList.Pause ();
                }
            }
            KickStarter.dialog.KillDialog (true, false);
    
            yield return null;
            yield return null;
    
            SaveSystem.SaveAutoSave ();
        }
    
        void OnFinishLoading (int saveID)
        {
            foreach (var actionList in saveableActionLists)
            {
                for (int i = 0; i < KickStarter.actionListManager.ActiveLists.Count; i++)
                {
                    var activeList = KickStarter.actionListManager.ActiveLists[i];
                    if (!activeList.IsRunning () && activeList.IsNecessary () && activeList.actionList && activeList.actionList == actionList)
                    {
                        KickStarter.actionListManager.Resume (actionList, true);
                    }
                }
            }
        }
    
    }
    

    Assign the Cutscenes you want the save to work for in its Inspector. Clicking the Button should then save the game in the Autosave slot, and loading this file should restore the state of the cutscenes involved.

    This is more of a test than anything, however, so just see how things go for now.

  • Is the CustomSave script dependent on the adjustment to ActionListManger change?

    I'll admit I'm a little green around the gills when it comes to anything non-visual scripted, but I ran into this after tweaking the ActionListManger file.

    https://imgur.com/a/AMUCZ51

    Maybe I did something wrong? In order to get everything running again I restore the ActionListManager with a backup I made.

  • Oh dear, I do apologise.

    It's actually the ActiveList script - not ActionListManager, sorry about that.

Sign In or Register to comment.

Howdy, Stranger!

It looks like you're new here. If you want to get involved, click one of these buttons!

Welcome to the official forum for Adventure Creator.