Load and save data in files on HoloLens device (txt, xml)

In this tutorial, I will guide you through the process of saving and loading data from both text and XML files. The data we’ll be storing consists of three-dimensional points within the mixed reality environment, which you can add to a list using a gesture event, such as air tapping.

Include the ‘Camera’ GameObject with gaze functionality, as well as support for air tap gestures, as described in the guide. “Gaze and Gestures on HoloLens“.

crud.png

Create three GameObjects equipped with Box Colliders to serve as buttons. The ‘Start Recording’ button allows you to initiate the recording of new three-dimensional points in the space using an air tap gesture. The ‘Save Points’ button enables you to save the recorded points to both text and XML files, while the ‘Load Points’ button lets you load points from text and XML files. Each of these GameObjects should have a script attached to handle the ‘OnSelect’ event.

OnSelectSave.cs file

    void OnSelect()
    {
        #if WINDOWS_UWP
        CRUDManager.Instance.SavePins();
        #endif
    }

OnSelectRecord.cs file

    void OnSelect()
    {
        CRUDManager.Instance.RecordCursorPins();
    }

OnSelect.cs file

    void OnSelect()
    {
        CRUDManager.Instance.LoadPins();
    }
public class Pin {
    
    public float posX { get; set; }
    public float posY { get; set; }
    public float posZ { get; set; }
}

Create a GameObject named ‘Manager’ and attach the following scripts to it: ‘GazeGestureManager.cs,’ ‘CRUDManager.cs,’ which contains code for recording and loading data from text and XML files, and ‘TextMessage.cs,’ responsible for displaying the list of three-dimensional points in the ‘Border’ GameObject. Additionally, incorporate spatial mapping to enable selection of surfaces from the real world.

CRUDManager.cs file

public class CRUDManager : MonoBehaviour
{

    public static CRUDManager Instance { get; private set; }

    public GameObject cursor;
    public GameObject recordMessage;

    private bool isrecording = false;
    private List<Pin> pins = new List<Pin>();
    private static string fileNameXML = "pins.xml";
    private static string fileNameTXT = "pins.txt";
#if WINDOWS_UWP
    private StorageFolder storageFolder = ApplicationData.Current.LocalFolder;
    private static string filePathXML = Path.Combine(ApplicationData.Current.LocalFolder.Path, fileNameXML);
    private static string filePathTXT = Path.Combine(ApplicationData.Current.LocalFolder.Path, fileNameTXT);
#endif

    void Awake()
    {
        Instance = this;
    }

    #region Record
    public void RecordCursorPins()
    {
        isrecording = !isrecording;

        if (isrecording)
        {
            pins.Clear();
            InvokeRepeating("AddPins", 2f, 2f);
            recordMessage.GetComponent<TextMesh>().text = "Stop Recording";
        }
        else
        {
            CancelInvoke("AddPins");
            recordMessage.GetComponent<TextMesh>().text = "Start Recording";
        }
    }

    private void AddPins()
    {
        pins.Add(new Pin { posX = cursor.transform.position.x, posY = cursor.transform.position.y, posZ = cursor.transform.position.z });
    }
    #endregion
    
    #region Save
#if WINDOWS_UWP
    public async void SavePins()
    {
        if (isrecording || pins.Count == 0)
        {
            TextMessage.Instance.ChangeTextMessage_Border("Try to record pins.");
            return;
        }
        
        //await WriteTXTToLocalStorage(); //Save to TXT file
    }

    public async Task WriteXMLToLocalStorage()
    {
        XmlSerializer serializer = new XmlSerializer(typeof(List));
        StorageFolder storageFolder = ApplicationData.Current.LocalFolder;

        if (File.Exists(filePathXML))
            File.Delete(filePathXML);

        StorageFile storageFile = await storageFolder.CreateFileAsync(fileNameXML);
        using (FileStream fs = new FileStream(storageFile.Path, FileMode.Create))
        {
            serializer.Serialize(fs, pins);
        }
     }

    public async Task WriteTXTToLocalStorage()
    {
        XmlSerializer serializer = new XmlSerializer(typeof(List));
        
        if (File.Exists(filePathTXT))
            File.Delete(filePathTXT);

        StorageFile storageFile = await storageFolder.CreateFileAsync(fileNameTXT);
        
        StringBuilder builder = new StringBuilder();
        for (int i = 0; i < pins.Count; i++)
        {
            builder.Append(string.Format("{0} {1} {2}", pins[i].posX.ToString("n5"), pins[i].posY.ToString("n5"), pins[i].posZ.ToString("n5"))).AppendLine();
        }

        //await FileIO.WriteTextAsync(storageFile, builder.ToString());
    }
#endif
    #endregion

    #region Load
    public void LoadPins()
    {
#if WINDOWS_UWP
        if (isrecording || pins.Count == 0)
        {
            TextMessage.Instance.ChangeTextMessage_Border("Try to record pins.");
            return;
        }
        
        if (!File.Exists(filePathXML))
        {
            TextMessage.Instance.ChangeTextMessage_Border("XML file does not exist.");
            return;
        }

        List savedPins = ReadXMLFromLocalStorage();
        string pinMessage = string.Empty;
        for(int i = 0; i < savedPins.Count; i++)
        {
            pinMessage = string.Format("{0} {1} {2}{3}", savedPins[i].posX.ToString("n5"), savedPins[i].posY.ToString("n5"), savedPins[i].posZ.ToString("n5"), Environment.NewLine);
        }

        //pinMessage = LoadTXTFromLocalStorage().Result; //Load TXT file

        TextMessage.Instance.ChangeTextMessage_Border(pinMessage);
#endif
    }
#if WINDOWS_UWP
    public List ReadXMLFromLocalStorage()
    {

        List savedPins;
        XmlSerializer serializer = new XmlSerializer(typeof(List));
        
        using (FileStream fs = new FileStream(filePathXML, FileMode.Open, FileAccess.Read))
        {
            savedPins = (List)serializer.Deserialize(fs);
        }

        return (savedPins != null)? savedPins : new List();
}

    public async Task LoadTXTFromLocalStorage()
    {
        StorageFile storageFile = await storageFolder.GetFileAsync(fileNameTXT);
        
        return await FileIO.ReadTextAsync(storageFile);
    }
#endif
    #endregion
}

TextMessage.cs file

public class TextMessage : MonoBehaviour {

    public static TextMessage Instance { get; private set; }
    
    public GameObject textMessageBorderGmObj;

    void Awake()
    {
        Instance = this;
    }

    public void ChangeTextMessage_Border(string textMessage)
    {
        textMessageBorderGmObj.GetComponent<TextMesh>().text = textMessage;
    }
}

You can find and download this sample from the following GitHub link: https://github.com/gntakakis/Hololens-CRUD-TXT-XML-Storage-Unity

Leave a comment