// // Assumptions: // ----------- // 1. Player can never access save menu when they are in the middle of a dock movement (e.g. currentDockingState is not IDLE) // 2. When game is loaded, player is kicked to gameplay (e.g. Movement and MouseLook state is always ENABLED upon restoration of state) // public FPEPlayerStateSaveData getPlayerStateDataForSavedGame() { GameObject tempDock = FPEInteractionManagerScript.Instance.getCurrentDockForSaveGame(); string currentDockName = (tempDock == null ? "" : tempDock.name); FPEPlayerStateSaveData playerData = new FPEPlayerStateSaveData(transform, GetCurrentPlayerFocalPoint(), isCrouching, playerDocked, currentDockName, targetMaxAngles, targetFocalPoint, targetDockPosition, previousFocalPoint, previousWorldPosition); return(playerData); }
/// <summary> /// Places the player back at the required location based on provided data package /// </summary> /// <param name="stateData">The data from which to restore the player's previous state</param> public void relocatePlayer(FPEPlayerStateSaveData stateData) { GameObject thePlayer = FPEPlayer.Instance.gameObject; if (!thePlayer) { Debug.LogError("FPESaveLoadManager.gatherPlayerData():: Cannot find Player! Is there a object tagged 'Player' in your scene?"); } else { thePlayer.GetComponent <FPEFirstPersonController>().restorePlayerStateFromSavedGame(stateData); } }
public void restorePlayerStateFromSavedGame(FPEPlayerStateSaveData data) { // Player position and look focus transform.position = data.playerPosition(); transform.rotation = data.playerRotation(); gameObject.GetComponent <FPEMouseLook>().LookAtPosition(transform, m_Camera.transform, data.playerLookAt()); isCrouching = data.Crouching; playerDocked = data.Docked; targetMaxAngles = data.MaxAngles; targetFocalPoint = data.TargetFocalPos; targetDockPosition = data.TargetDockPos; previousFocalPoint = data.PreviousFocalPos; previousWorldPosition = data.PreviousWorldPos; // Look for dock, if required if (data.Docked && data.DockName != "") { FPEInteractableDockScript[] allDocks = GameObject.FindObjectsOfType <FPEInteractableDockScript>(); GameObject foundDock = null; for (int i = 0; i < allDocks.Length; i++) { if (allDocks[i].gameObject.name == data.DockName) { foundDock = allDocks[i].gameObject; break; } } if (foundDock != null) { gameObject.GetComponent <FPEMouseLook>().enableLookRestriction(targetMaxAngles); FPEInteractionManagerScript.Instance.restoreCurrentDockFromSavedGame(foundDock); isCrouching = false; } // If there was supposed to be a dock per the save file, but it cannot be found, try to restore the player state in a friendly way else { Debug.LogError("FPEFirstPersonController.restorePlayerStateFromSavedGame():: Saved dock named '" + data.DockName + "' could not be found in the scene. Restoring player to undocked state in last known good world position instead. Your scene must have changed, or this is an old saved game that is no longer compatible with the most recent version of your game or scene."); transform.position = data.PreviousWorldPos; transform.rotation = Quaternion.identity; gameObject.GetComponent <FPEMouseLook>().LookAtPosition(transform, m_Camera.transform, data.PreviousFocalPos); playerDocked = false; isCrouching = false; } } // We ALWAYS assume the player can move their mouse once a game is loaded we restore player state gameObject.GetComponent <FPEMouseLook>().enableMouseLook = true; }
/// <summary> /// Gathers player's data for saving (position, rotation, etc.) /// </summary> /// <returns>The packaged data based on player state</returns> public FPEPlayerStateSaveData gatherPlayerData() { FPEPlayerStateSaveData stateData = null; GameObject thePlayer = FPEPlayer.Instance.gameObject; if (!thePlayer) { Debug.LogError("FPESaveLoadManager.gatherPlayerData():: Cannot find Player! Is there a object tagged 'Player' in your scene?"); } else { stateData = thePlayer.GetComponent <FPEFirstPersonController>().getPlayerStateDataForSavedGame(); } return(stateData); }
/// <summary> /// Saves all relevant game data per the scene and specified fullSave value. /// </summary> /// <param name="fullSave">If true, 'last manually saved scene' and player location data are also saved. If false, these are skipped and just the other scene object data is saved.</param> /// <returns>True if operation was a success. False if there was an error. In the case of an error, an exception likely occured, and an exception message will be printed.</returns> private IEnumerator saveDataForCurrentScene(bool fullSave) { float operationStartTime = Time.time; bool result = true; BinaryFormatter formatter = new BinaryFormatter(); FileStream fsCore = null; FileStream fsPlayer = null; FileStream fsInventory = null; FileStream fsLevel = null; FPESceneSaveData sceneData = mySaveLoadLogic.gatherSceneData(); FPEInventorySaveData inventoryData = mySaveLoadLogic.gatherInventorySaveData(); FPEPlayerStateSaveData playerLocationData = mySaveLoadLogic.gatherPlayerData(); FPEInventoryWorldSaveData[] worldInventoryData = mySaveLoadLogic.gatherInventoryInWorld(); FPEPickupWorldSaveData[] worldPickupData = mySaveLoadLogic.gatherPickupsInWorld(); FPETriggerSaveData[] triggerSaveData = mySaveLoadLogic.gatherTriggerData(); FPEActivateSaveData[] activateSaveData = mySaveLoadLogic.gatherActivateTypeData(); FPEAttachedNoteSaveData[] attachedNoteSaveData = mySaveLoadLogic.gatherAttachedNoteTypeData(); FPEAudioDiaryPlayedStateSaveData[] playedDiarySaveData = mySaveLoadLogic.gatherAudioDiaryPlayedStateData(); FPEJournalSaveData[] journalSaveData = mySaveLoadLogic.gatherJournalSaveData(); FPEDoorSaveData[] doorSaveData = mySaveLoadLogic.gatherDoorTypeData(); FPEGenericObjectSaveData[] genericSaveData = mySaveLoadLogic.gatherGenericSaveTypeData(); // // Your additional Custom Save/Load logic for custom save data types goes here // // Try to write the gathered data to applicable save files on disk try { // We only want to save player location data on manual save operation. When "auto-saving" on change of scene via Doorway, we just want to save the non-player level data. if (fullSave) { // We also only want to write to core save file on a manual save operation. Auto-saves on level change do not count as a "full save" fsCore = new FileStream(fullCoreSaveFileFullPath, FileMode.Create); formatter.Serialize(fsCore, sceneData); fsPlayer = new FileStream(fullPlayerLocationDataSaveFileFullPath, FileMode.Create); formatter.Serialize(fsPlayer, playerLocationData); fsInventory = new FileStream(fullInventoryDataSaveFileFullPath, FileMode.Create); formatter.Serialize(fsInventory, inventoryData); } fsLevel = new FileStream(autoSavePath + "/" + levelDataFilePrefix + sceneData.LastSavedSceneIndex + levelDataFilePostfix, FileMode.Create); formatter.Serialize(fsLevel, worldInventoryData); formatter.Serialize(fsLevel, worldPickupData); formatter.Serialize(fsLevel, triggerSaveData); formatter.Serialize(fsLevel, activateSaveData); formatter.Serialize(fsLevel, attachedNoteSaveData); formatter.Serialize(fsLevel, playedDiarySaveData); formatter.Serialize(fsLevel, journalSaveData); formatter.Serialize(fsLevel, doorSaveData); formatter.Serialize(fsLevel, genericSaveData); // If performing a full save, we also want to flush all previous FULL save data from full directory, and replace it will the stuff we just saved to auto if (fullSave) { copyAllLevelDataFromPathToPath(autoSavePath, fullSavePath); } } catch (Exception e) { Debug.LogError("FPESaveLoadManager:: Failed to save game file(s). Did you make a call the SaveGame() before ever calling StartANewGame()? Reason: " + e.Message); result = false; } finally { if (fsCore != null) { fsCore.Close(); } if (fsLevel != null) { fsLevel.Close(); } if (fsPlayer != null) { fsPlayer.Close(); } if (fsInventory != null) { fsInventory.Close(); } } // Last thing is to clear the progress flag to indicate we're done savingInProgress = false; yield return(result); }
/// <summary> /// Restores saved level data to the currently loaded scene, if it exists. /// </summary> /// <param name="fullLoad">If true, player location and inventory data will be loaded, and player will be relocated in world space per that data.</param> /// <returns>True if operation was a success. False if there was an error. In the case of an error, an exception likely occured, and an exception message will be printed.</returns> private IEnumerator restoreDataForCurrentScene(bool fullLoad) { bool result = true; // If doing a full load, we want to copy all 'full' save level data files into the 'auto' directory, then restore from there. if (fullLoad) { copyAllLevelDataFromPathToPath(fullSavePath, autoSavePath); } string levelDataFilename = autoSavePath + "/" + levelDataFilePrefix + SceneManager.GetActiveScene().buildIndex + levelDataFilePostfix; if (File.Exists(levelDataFilename)) { FPEPlayerStateSaveData loadedPlayerLocationData = null; FPEInventorySaveData loadedInventoryData = null; FPEInventoryWorldSaveData[] loadedWorldInventoryData = null; FPEPickupWorldSaveData[] loadedWorldPickupData = null; FPETriggerSaveData[] loadedTriggerData = null; FPEActivateSaveData[] loadedActivateData = null; FPEAttachedNoteSaveData[] loadedAttachedNoteData = null; FPEAudioDiaryPlayedStateSaveData[] loadedAudioDiaryPlayedData = null; FPEJournalSaveData[] loadedJournalData = null; FPEDoorSaveData[] loadedDoorData = null; FPEGenericObjectSaveData[] loadedGenericData = null; BinaryFormatter formatter = new BinaryFormatter(); FileStream fsPlayer = null; FileStream fsInventory = null; FileStream fsLevel = null; // Try to actually read in the data from disk try { fsLevel = new FileStream(levelDataFilename, FileMode.Open); // Note: Read the data in from file in the same order it was written to file loadedWorldInventoryData = (FPEInventoryWorldSaveData[])formatter.Deserialize(fsLevel); loadedWorldPickupData = (FPEPickupWorldSaveData[])formatter.Deserialize(fsLevel); loadedTriggerData = (FPETriggerSaveData[])formatter.Deserialize(fsLevel); loadedActivateData = (FPEActivateSaveData[])formatter.Deserialize(fsLevel); loadedAttachedNoteData = (FPEAttachedNoteSaveData[])formatter.Deserialize(fsLevel); loadedAudioDiaryPlayedData = (FPEAudioDiaryPlayedStateSaveData[])formatter.Deserialize(fsLevel); loadedJournalData = (FPEJournalSaveData[])formatter.Deserialize(fsLevel); loadedDoorData = (FPEDoorSaveData[])formatter.Deserialize(fsLevel); loadedGenericData = (FPEGenericObjectSaveData[])formatter.Deserialize(fsLevel); // // Your additional Custom Save/Load logic for custom save data types goes here // // Triggers and Activate types mySaveLoadLogic.restoreTriggerData(loadedTriggerData); mySaveLoadLogic.restoreActivateData(loadedActivateData); // Doors mySaveLoadLogic.restoreDoorData(loadedDoorData); // Generic Saveable Objects mySaveLoadLogic.restoreGenericSaveTypeData(loadedGenericData); // Inventory type objects in the world mySaveLoadLogic.removeAllInventoryInWorld(fullLoad); mySaveLoadLogic.createWorldInventory(loadedWorldInventoryData); // Pickup type objects in the world mySaveLoadLogic.removeAllPickupsInWorld(fullLoad); mySaveLoadLogic.createWorldPickups(loadedWorldPickupData); // Attached Notes, Diaries, and Journals // Note: We restore these last, because they can be attached to Inventory Items and Pickups or other interactables. If for example, we loaded these BEFORE restoring pickups, // the associated pickups would get their attached note/diary data restored, then be deleted and replaced with a fresh copy of the prefab, thus erasing the restored note/diary // state. This way, we ensure the 'final' restored prefab of a pickup/inventory item is in place and loaded before we restore its attached note or diary status. mySaveLoadLogic.restoreAttachedNoteData(loadedAttachedNoteData); mySaveLoadLogic.restoreAudioDiaryPlaybackStateData(loadedAudioDiaryPlayedData); mySaveLoadLogic.restoreJournalData(loadedJournalData); // Only restore player location and move them if required. We do this last because the functions above may destroy held object, depending on fullLoad value. if (fullLoad) { fsPlayer = new FileStream(fullPlayerLocationDataSaveFileFullPath, FileMode.Open); loadedPlayerLocationData = (FPEPlayerStateSaveData)formatter.Deserialize(fsPlayer); mySaveLoadLogic.relocatePlayer(loadedPlayerLocationData); fsInventory = new FileStream(fullInventoryDataSaveFileFullPath, FileMode.Open); loadedInventoryData = (FPEInventorySaveData)formatter.Deserialize(fsInventory); mySaveLoadLogic.restoreInventorySaveData(loadedInventoryData); } } catch (Exception e) { Debug.LogError("FPESaveLoadManager:: Failed to load game file. Reason: " + e.Message); result = false; } finally { if (fsLevel != null) { fsLevel.Close(); } if (fsPlayer != null) { fsPlayer.Close(); } if (fsInventory != null) { fsInventory.Close(); } } } // Last thing is to clear the progress flag to indicate we're done restoringDataInProgress = false; yield return(result); }