首次提交
This commit is contained in:
@@ -0,0 +1,82 @@
|
||||
#if ES3_TMPRO && ES3_UGUI
|
||||
|
||||
using System;
|
||||
using TMPro;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
|
||||
/// <summary>
|
||||
/// A script attached to the Create Slot button to manage slot creation.
|
||||
/// </summary>
|
||||
public class ES3CreateSlot : MonoBehaviour
|
||||
{
|
||||
[Tooltip("The button used to bring up the 'Create Slot' dialog.")]
|
||||
public Button createButton;
|
||||
[Tooltip("The ES3SlotDialog Component of the Create Slot dialog")]
|
||||
public ES3SlotDialog createDialog;
|
||||
[Tooltip("The TMP_Text input text field of the create slot dialog.")]
|
||||
public TMP_InputField inputField;
|
||||
[Tooltip("The ES3SlotManager this Create Slot Dialog belongs to.")]
|
||||
public ES3SlotManager mgr;
|
||||
|
||||
protected virtual void OnEnable()
|
||||
{
|
||||
// Whether we should show or hide this Create Slot button based on the settings in the slot manager.
|
||||
gameObject.SetActive(mgr.showCreateSlotButton);
|
||||
|
||||
// Make it so the Create Slot button brings up the Create Slot dialog.
|
||||
createButton.onClick.AddListener(() => createDialog.gameObject.SetActive(true));
|
||||
|
||||
// Add listener to the confirmation button.
|
||||
createDialog.confirmButton.onClick.AddListener(TryCreateNewSlot);
|
||||
}
|
||||
|
||||
protected virtual void OnDisable()
|
||||
{
|
||||
// Make sure the text field is empty for next time.
|
||||
inputField.text = string.Empty;
|
||||
// Remove all listeners.
|
||||
createButton.onClick.RemoveAllListeners();
|
||||
createDialog.confirmButton.onClick.RemoveAllListeners();
|
||||
}
|
||||
|
||||
// Called when the Create button is pressed in the Create New Slot dialog.
|
||||
public virtual void TryCreateNewSlot()
|
||||
{
|
||||
// If the user hasn't specified a name, throw an error.
|
||||
// Note that no other validation of the name is a required as this is handled using a REGEX in the TMP_InputField Component.
|
||||
if (string.IsNullOrEmpty(inputField.text))
|
||||
{
|
||||
mgr.ShowErrorDialog("You must specify a name for your save slot");
|
||||
return;
|
||||
}
|
||||
|
||||
// If a slot with this name already exists, require the user to enter a different name.
|
||||
if (ES3.FileExists(mgr.GetSlotPath(inputField.text)))
|
||||
{
|
||||
mgr.ShowErrorDialog("A slot already exists with this name. Please choose a different name.");
|
||||
return;
|
||||
}
|
||||
|
||||
// Create the slot.
|
||||
CreateNewSlot(inputField.text);
|
||||
// Clear the input field so the value isn't there when we reopen it.
|
||||
inputField.text = "";
|
||||
// Hide the dialog.
|
||||
createDialog.gameObject.SetActive(false);
|
||||
}
|
||||
|
||||
|
||||
// Creates a new slot by instantiating it in the UI and creating a save file for it.
|
||||
protected virtual void CreateNewSlot(string slotName)
|
||||
{
|
||||
// Get the current timestamp.
|
||||
var creationTimestamp = DateTime.Now;
|
||||
// Create the slot in the UI.
|
||||
var slot = mgr.InstantiateSlot(slotName, creationTimestamp);
|
||||
// Move the slot to the top of the list.
|
||||
slot.transform.SetSiblingIndex(1);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2f4ce35662bad7843b3b34f527102454
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
150
Assets/Plugins/Easy Save 3/Scripts/Save Slots/ES3Slot.cs
Normal file
150
Assets/Plugins/Easy Save 3/Scripts/Save Slots/ES3Slot.cs
Normal file
@@ -0,0 +1,150 @@
|
||||
#if ES3_TMPRO && ES3_UGUI
|
||||
|
||||
using TMPro;
|
||||
using UnityEngine;
|
||||
using UnityEngine.SceneManagement;
|
||||
using UnityEngine.UI;
|
||||
|
||||
/// <summary>
|
||||
/// A Component added to a save slot to allow it to be selected, deleted, and un-deleted.
|
||||
/// </summary>
|
||||
public class ES3Slot : MonoBehaviour
|
||||
{
|
||||
[Tooltip("The text label containing the slot name.")]
|
||||
public TMP_Text nameLabel;
|
||||
[Tooltip("The text label containing the last updated timestamp for the slot.")]
|
||||
public TMP_Text timestampLabel;
|
||||
|
||||
[Tooltip("The confirmation dialog to show if showConfirmationIfExists is true.")]
|
||||
public GameObject confirmationDialog;
|
||||
|
||||
// The manager this slot belongs to. This is set by the manager which creates it.
|
||||
public ES3SlotManager mgr;
|
||||
|
||||
[Tooltip("The button for selecting this slot.")]
|
||||
public Button selectButton;
|
||||
[Tooltip("The button for deleting this slot.")]
|
||||
public Button deleteButton;
|
||||
[Tooltip("The button for undoing the deletion of this slot.")]
|
||||
public Button undoButton;
|
||||
|
||||
// Whether this slot has been marked for deletion.
|
||||
protected bool markedForDeletion = false;
|
||||
|
||||
#region Initialisation and Clean-up
|
||||
|
||||
// See Unity's docs for more info: https://docs.unity3d.com/ScriptReference/MonoBehaviour.OnEnable.html
|
||||
public virtual void OnEnable()
|
||||
{
|
||||
// Add the button press listeners.
|
||||
selectButton.onClick.AddListener(TrySelectSlot);
|
||||
deleteButton.onClick.AddListener(MarkSlotForDeletion);
|
||||
undoButton.onClick.AddListener(UnmarkSlotForDeletion);
|
||||
}
|
||||
|
||||
// See Unity's docs for more info: https://docs.unity3d.com/ScriptReference/MonoBehaviour.OnDisable.html
|
||||
public virtual void OnDisable()
|
||||
{
|
||||
// Remove all button press listeners.
|
||||
selectButton.onClick.RemoveAllListeners();
|
||||
deleteButton.onClick.RemoveAllListeners();
|
||||
undoButton.onClick.RemoveAllListeners();
|
||||
|
||||
// If this slot is marked for deletion, delete it.
|
||||
if (markedForDeletion)
|
||||
DeleteSlot();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Select methods
|
||||
|
||||
// Called when the Select Slot button is pressed.
|
||||
protected virtual void TrySelectSlot()
|
||||
{
|
||||
// Manage the confirmation dialog if necessary.
|
||||
if(mgr.showConfirmationIfExists)
|
||||
{
|
||||
if (confirmationDialog == null)
|
||||
Debug.LogError("The confirmationDialog field of this ES3SelectSlot Component hasn't been set in the inspector.", this);
|
||||
|
||||
// Display a confirmation dialog if we're overwriting a save slot.
|
||||
if (ES3.FileExists(GetSlotPath()))
|
||||
{
|
||||
// Show the dialog.
|
||||
confirmationDialog.SetActive(true);
|
||||
// Register the event for the confirmation button.
|
||||
confirmationDialog.GetComponent<ES3SlotDialog>().confirmButton.onClick.AddListener(SelectSlot);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
SelectSlot();
|
||||
}
|
||||
|
||||
// Selects a slot and calls post-selection events if applicable.
|
||||
protected virtual void SelectSlot()
|
||||
{
|
||||
// Hide the confirmation dialog if it's open.
|
||||
confirmationDialog?.SetActive(false);
|
||||
|
||||
// Set the path used by Auto Save.
|
||||
ES3SlotManager.selectedSlotPath = GetSlotPath();
|
||||
|
||||
// When the default path used by Easy Save's methods.
|
||||
ES3Settings.defaultSettings.path = ES3SlotManager.selectedSlotPath;
|
||||
|
||||
// If we've specified an event to be called after the user selects a slot, invoke it.
|
||||
mgr.onAfterSelectSlot?.Invoke();
|
||||
|
||||
// If we've specified a scene to load after the user selects a slot, load it.
|
||||
if (!string.IsNullOrEmpty(mgr.loadSceneAfterSelectSlot))
|
||||
SceneManager.LoadScene(mgr.loadSceneAfterSelectSlot);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Delete methods
|
||||
|
||||
// Marks a slot to be deleted and displays an undo button.
|
||||
protected virtual void MarkSlotForDeletion()
|
||||
{
|
||||
markedForDeletion = true;
|
||||
// Make the Undo button visible and hide the Delete button.
|
||||
undoButton.gameObject.SetActive(true);
|
||||
deleteButton.gameObject.SetActive(false);
|
||||
}
|
||||
|
||||
// Unmarks a slot to be deleted and displays an delete button again.
|
||||
protected virtual void UnmarkSlotForDeletion()
|
||||
{
|
||||
markedForDeletion = false;
|
||||
// Make the Undo button visible and hide the Delete button.
|
||||
undoButton.gameObject.SetActive(false);
|
||||
deleteButton.gameObject.SetActive(true);
|
||||
}
|
||||
|
||||
// Deletes a save slot.
|
||||
protected virtual void DeleteSlot()
|
||||
{
|
||||
// Delete the file linked to this slot.
|
||||
ES3.DeleteFile(GetSlotPath());
|
||||
// Destroy this slot.
|
||||
Destroy(this.gameObject);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Utility methods
|
||||
|
||||
// Gets the relative file path of the slot with the given slot name.
|
||||
public virtual string GetSlotPath()
|
||||
{
|
||||
// Get the slot path from the manager.
|
||||
return mgr.GetSlotPath(nameLabel.text);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a276080975dbd4a46b4ef11085e19980
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,29 @@
|
||||
#if ES3_TMPRO && ES3_UGUI
|
||||
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
|
||||
/// <summary>
|
||||
/// A script attached to the error dialog and confirm dialog to provide events which other scripts can receive.
|
||||
/// </summary>
|
||||
public class ES3SlotDialog : MonoBehaviour
|
||||
{
|
||||
[Tooltip("The UnityEngine.UI.Button Component for the Confirm button.")]
|
||||
public Button confirmButton;
|
||||
[Tooltip("The UnityEngine.UI.Button Component for the Cancel button.")]
|
||||
public Button cancelButton;
|
||||
|
||||
protected virtual void OnEnable()
|
||||
{
|
||||
// Make it so that the cancel button deactivates the dialog.
|
||||
cancelButton.onClick.AddListener(() => gameObject.SetActive(false));
|
||||
}
|
||||
|
||||
protected virtual void OnDisable()
|
||||
{
|
||||
// Remove any listeners when disabling this dialog.
|
||||
cancelButton.onClick.RemoveAllListeners();
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 343f4b43e9b862d42873f3e0af2c1f7e
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
208
Assets/Plugins/Easy Save 3/Scripts/Save Slots/ES3SlotManager.cs
Normal file
208
Assets/Plugins/Easy Save 3/Scripts/Save Slots/ES3SlotManager.cs
Normal file
@@ -0,0 +1,208 @@
|
||||
using UnityEngine;
|
||||
|
||||
#if UNITY_EDITOR
|
||||
using UnityEditor;
|
||||
#endif
|
||||
|
||||
#if ES3_TMPRO && ES3_UGUI
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.IO;
|
||||
using TMPro;
|
||||
using System.Text.RegularExpressions;
|
||||
using UnityEngine.Events;
|
||||
using UnityEngine.SceneManagement;
|
||||
|
||||
public class ES3SlotManager : MonoBehaviour
|
||||
{
|
||||
[Tooltip("Shows a confirmation if this slot already exists when we select it.")]
|
||||
public bool showConfirmationIfExists = true;
|
||||
[Tooltip("Whether the Create new slot button should be visible.")]
|
||||
public bool showCreateSlotButton = true;
|
||||
|
||||
[Space(16)]
|
||||
|
||||
[Tooltip("The name of a scene to load after the user chooses a slot.")]
|
||||
public string loadSceneAfterSelectSlot;
|
||||
|
||||
[Space(16)]
|
||||
|
||||
[Tooltip("An event called after a slot is selected, but before the scene specified by loadSceneAfterSelectSlot is loaded.")]
|
||||
public UnityEvent onAfterSelectSlot;
|
||||
|
||||
[Space(16)]
|
||||
|
||||
[Tooltip("The subfolder we want to store our save files in. If this is a relative path, it will be relative to Application.persistentDataPath.")]
|
||||
public string slotDirectory = "slots/";
|
||||
[Tooltip("The extension we want to use for our save files.")]
|
||||
public string slotExtension = ".es3";
|
||||
|
||||
[Space(16)]
|
||||
|
||||
[Tooltip("The template we'll instantiate to create our slots.")]
|
||||
public GameObject slotTemplate;
|
||||
[Tooltip("The dialog box for creating a new slot.")]
|
||||
public GameObject createDialog;
|
||||
[Tooltip("The dialog box for displaying an error to the user.")]
|
||||
public GameObject errorDialog;
|
||||
|
||||
// The relative path of the slot which has been selected, or null if none have been selected.
|
||||
public static string selectedSlotPath = null;
|
||||
|
||||
// A list of slots which have been created.
|
||||
protected List<GameObject> slots = new List<GameObject>();
|
||||
|
||||
// If a file doesn't have a timestamp, it will return have this DateTime.
|
||||
static DateTime falseDateTime = new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc);
|
||||
|
||||
// See Unity's docs for more info: https://docs.unity3d.com/ScriptReference/MonoBehaviour.OnEnable.html
|
||||
protected virtual void OnEnable()
|
||||
{
|
||||
// Deactivate the slot template so it's not visible.
|
||||
slotTemplate.SetActive(false);
|
||||
// Destroy any existing slots and start from fresh if necessary.
|
||||
DestroySlots();
|
||||
// Create our save slots if any exist.
|
||||
InstantiateSlots();
|
||||
}
|
||||
|
||||
// Finds the save slot files and instantiates a save slot for each of them.
|
||||
protected virtual void InstantiateSlots()
|
||||
{
|
||||
// A list used to store our save slots so we can order them.
|
||||
List<(string Name, DateTime Timestamp)> slots = new List<(string Name, DateTime Timestamp)>();
|
||||
|
||||
// If there are no slots to load, do nothing.
|
||||
if (!ES3.DirectoryExists(slotDirectory))
|
||||
return;
|
||||
|
||||
// Put each of our slots into a List so we can order them.
|
||||
foreach (var file in ES3.GetFiles(slotDirectory))
|
||||
{
|
||||
// Get the slot name, which is the filename without the extension.
|
||||
var slotName = Path.GetFileNameWithoutExtension(file);
|
||||
// Get the timestamp so that we can display this to the user and use it to order the slots.
|
||||
var timestamp = ES3.GetTimestamp(GetSlotPath(slotName));
|
||||
// Add the data to the slot list.
|
||||
slots.Add((Name: slotName, Timestamp: timestamp));
|
||||
}
|
||||
|
||||
// Now order the slots by the timestamp.
|
||||
slots = slots.OrderByDescending(x => x.Timestamp).ToList();
|
||||
|
||||
// Now create the slots.
|
||||
foreach (var slot in slots)
|
||||
InstantiateSlot(slot.Name, slot.Timestamp);
|
||||
}
|
||||
|
||||
// Instantiates a single save slot with a given slot name and timestamp.
|
||||
public virtual GameObject InstantiateSlot(string slotName, DateTime timestamp)
|
||||
{
|
||||
// Create an instance of our slot.
|
||||
var slot = Instantiate(slotTemplate, slotTemplate.transform.parent);
|
||||
|
||||
// Add it to our slot list.
|
||||
slots.Add(slot);
|
||||
|
||||
// Ensure that we make it active as the template will be inactive.
|
||||
slot.SetActive(true);
|
||||
|
||||
var es3SelectSlot = slot.GetComponent<ES3Slot>();
|
||||
es3SelectSlot.nameLabel.text = slotName.Replace('_', ' ');
|
||||
|
||||
// If the file doesn't have a timestamp, don't display the timestamp.
|
||||
if (timestamp == falseDateTime)
|
||||
es3SelectSlot.timestampLabel.text = "";
|
||||
// Otherwise, set the label for the timestamp.
|
||||
else
|
||||
es3SelectSlot.timestampLabel.text = $"{timestamp.ToString("yyyy-MM-dd")}\n{timestamp.ToString("HH:mm:ss")}";
|
||||
|
||||
return slot;
|
||||
}
|
||||
|
||||
// Shows the dialog displaying an error to the user.
|
||||
public virtual void ShowErrorDialog(string errorMessage)
|
||||
{
|
||||
errorDialog.transform.Find("Dialog Box/Message").GetComponent<TMP_Text>().text = errorMessage;
|
||||
errorDialog.SetActive(true);
|
||||
}
|
||||
|
||||
#region Utility Methods
|
||||
|
||||
// Closes the slot window.
|
||||
public virtual void Close()
|
||||
{
|
||||
gameObject.SetActive(false);
|
||||
}
|
||||
|
||||
// Destroys all slots which have been created, but doesn't delete their underlying save files.
|
||||
protected virtual void DestroySlots()
|
||||
{
|
||||
foreach (var slot in slots)
|
||||
Destroy(slot);
|
||||
}
|
||||
|
||||
// Gets the relative file path of the slot with the given slot name.
|
||||
public virtual string GetSlotPath(string slotName)
|
||||
{
|
||||
// We convert any whitespace characters to underscores at this point to make the file more portable.
|
||||
return slotDirectory + Regex.Replace(slotName, @"\s+", "_") + slotExtension;
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
#if UNITY_EDITOR
|
||||
// Manages the context menu items for creating the slots.
|
||||
public class ES3SlotMenuItems : MonoBehaviour
|
||||
{
|
||||
|
||||
[MenuItem("GameObject/Easy Save 3/Add Save Slots to Scene", false, 33)]
|
||||
[MenuItem("Assets/Easy Save 3/Add Save Slots to Scene", false, 33)]
|
||||
[MenuItem("Tools/Easy Save 3/Add Save Slots to Scene", false, 150)]
|
||||
public static void AddSaveSlotsToScene()
|
||||
{
|
||||
#if !ES3_TMPRO || !ES3_UGUI
|
||||
EditorUtility.DisplayDialog("Cannot create save slots", "The 'TextMeshPro' and 'Unity UI' packages must be installed in Window > Package Manager to use Easy Save's slot functionality.", "Ok");
|
||||
#else
|
||||
var mgr = AddSlotsToScene();
|
||||
mgr.showConfirmationIfExists = true;
|
||||
mgr.showCreateSlotButton = true;
|
||||
#endif
|
||||
}
|
||||
|
||||
[MenuItem("GameObject/Easy Save 3/Add Load Slots to Scene", false, 33)]
|
||||
[MenuItem("Assets/Easy Save 3/Add Load Slots to Scene", false, 33)]
|
||||
[MenuItem("Tools/Easy Save 3/Add Load Slots to Scene", false, 150)]
|
||||
public static void AddLoadSlotsToScene()
|
||||
{
|
||||
#if !ES3_TMPRO || !ES3_UGUI
|
||||
EditorUtility.DisplayDialog("Cannot create save slots", "The 'TextMeshPro' and 'Unity UI' packages must be installed in Window > Package Manager to use Easy Save's slot functionality.", "Ok");
|
||||
#else
|
||||
var mgr = AddSlotsToScene();
|
||||
mgr.showConfirmationIfExists = false;
|
||||
mgr.showCreateSlotButton = false;
|
||||
mgr.GetComponentInChildren<ES3CreateSlot>().gameObject.SetActive(false);
|
||||
#endif
|
||||
}
|
||||
|
||||
#if ES3_TMPRO && ES3_UGUI
|
||||
static ES3SlotManager AddSlotsToScene()
|
||||
{
|
||||
if (!SceneManager.GetActiveScene().isLoaded)
|
||||
EditorUtility.DisplayDialog("Could not add manager to scene", "Could not add Save Slots to scene because there is not currently a scene open.", "Ok");
|
||||
|
||||
var pathToEasySaveFolder = ES3Settings.PathToEasySaveFolder();
|
||||
|
||||
var prefab = AssetDatabase.LoadAssetAtPath<GameObject>(pathToEasySaveFolder + "Scripts/Save Slots/Easy Save Slots Canvas.prefab");
|
||||
var instance = (GameObject)Instantiate(prefab);
|
||||
Undo.RegisterCreatedObjectUndo(instance, "Added Save Slots to Scene");
|
||||
|
||||
return instance.GetComponentInChildren<ES3SlotManager>();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3de4c36c55c98254bb7ff7a0e8abb993
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c7b06d0054ae75b41bc4f2b9d5528b04
|
||||
PrefabImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Reference in New Issue
Block a user