Search Unity

Question Replay Saving (file) working in the editor, but crashing in a build game?

Discussion in 'Scripting' started by Aviation_Simmer, May 7, 2023.

  1. Aviation_Simmer

    Aviation_Simmer

    Joined:
    Aug 30, 2021
    Posts:
    110
    Hi, so I want to save & load any replays which are made in the game. the replay mode already works, and there are no issues in the editor. but as soon as I try to save or load in the build game, the game crashes at the point I click. does anybody maybe know the reason? thanks!

    btw: this is for my game Unity Flight Simulator with over 4.5k downloads, and It would be very nice, if I can implement that save & load function

    Code (CSharp):
    1. using UnityEngine;
    2. using System.IO;
    3. using System.Runtime.Serialization.Formatters.Binary;
    4. using System.Collections.Generic;
    5. using System.Globalization;
    6. using System.Linq;
    7. using System.Text;
    8. using System;
    9. using SFB;
    10.  
    11. public class ReplaySaveLoadSystem : MonoBehaviour
    12. {
    13.     public Replay replay;
    14.  
    15.     public void OpenFileBrowser()
    16.     {
    17.         string filePath = StandaloneFileBrowser.OpenFilePanel("Open Save File", "", "", false)[0];
    18.  
    19.         // Check if a file was selected
    20.         if (!string.IsNullOrEmpty(filePath))
    21.         {
    22.             LoadData(filePath);
    23.         }
    24.     }
    25.  
    26.     public void SaveData()
    27.     {
    28.         // Define the folder path
    29.         string folderPath = Path.Combine(Application.dataPath, "Data", "Replays");
    30.  
    31.         // Check if the folder exists, create it if not
    32.         if (!Directory.Exists(folderPath))
    33.         {
    34.             Directory.CreateDirectory(folderPath);
    35.         }
    36.  
    37.         string fileName = "replay_" + DateTime.Now.ToString("ddMMyyyyHHmmss") + ".rpl";
    38.         string fileFilter = "Replay Files (*.rpl)|*.rpl";
    39.         string saveFilePath = StandaloneFileBrowser.SaveFilePanel("Save Replay", folderPath, fileName, fileFilter);
    40.         if (!string.IsNullOrEmpty(saveFilePath))
    41.         {
    42.             SaveDataToFile(saveFilePath);
    43.         }
    44.     }
    45.  
    46.     private void SaveDataToFile(string filePath)
    47.     {
    48.         StringBuilder csvData = new StringBuilder();
    49.  
    50.         // Create a culture with the desired decimal separator
    51.         CultureInfo culture = new CultureInfo("en-US"); // Example: en-US for period (.), fr-FR for comma (,)
    52.  
    53.         // Append header row
    54.         csvData.AppendLine("position_x,position_y,position_z,rotation_x,rotation_y,rotation_z,rotation_w,throttle,rpm,controlInputs_x,controlInputs_y,controlInputs_z,timestamp,airspeed,verticalspeed,altitude");
    55.  
    56.         // Append data rows
    57.         foreach (ObjectReplayRecord record in replay.actionReplayRecords)
    58.         {
    59.             // Convert the data to a CSV-formatted string with specific decimal precision and culture
    60.             string rowData = string.Format(
    61.                 culture,
    62.                 "{0:F4},{1:F4},{2:F4},{3:F4},{4:F4},{5:F4},{6:F4},{7},{8},{9:F4},{10:F4},{11:F4},{12:F4},{13:F4},{14:F4},{15:F4}",
    63.                 record.position.x, record.position.y, record.position.z,
    64.                 record.rotation.x, record.rotation.y, record.rotation.z, record.rotation.w,
    65.                 record.throttle, record.rpm,
    66.                 record.ControlInputs.x, record.ControlInputs.y, record.ControlInputs.z,
    67.                 record.timestamp, record.airspeed, record.verticalspeed, record.altitude
    68.             );
    69.  
    70.             csvData.AppendLine(rowData);
    71.         }
    72.  
    73.         // Write the data to the file
    74.         File.WriteAllText(filePath, csvData.ToString());
    75.     }
    76.  
    77.  
    78.     private void LoadData(string filePath)
    79.     {
    80.         // Read all lines from the CSV file
    81.         string[] lines = File.ReadAllLines(filePath);
    82.  
    83.         // Clear the existing list
    84.         replay.actionReplayRecords.Clear();
    85.  
    86.         // Iterate over each line (excluding the header)
    87.         for (int i = 1; i < lines.Length; i++)
    88.         {
    89.             // Split the line by comma to get the individual values
    90.             string[] values = lines[i].Split(',');
    91.  
    92.             // Create a new ObjectReplayRecord and populate its fields
    93.             ObjectReplayRecord record = new ObjectReplayRecord();
    94.  
    95.             // Parse the numbers with InvariantCulture
    96.             record.position = new Vector3(
    97.                 float.Parse(values[0], CultureInfo.InvariantCulture),
    98.                 float.Parse(values[1], CultureInfo.InvariantCulture),
    99.                 float.Parse(values[2], CultureInfo.InvariantCulture)
    100.             );
    101.             record.rotation = new Quaternion(
    102.                 float.Parse(values[3], CultureInfo.InvariantCulture),
    103.                 float.Parse(values[4], CultureInfo.InvariantCulture),
    104.                 float.Parse(values[5], CultureInfo.InvariantCulture),
    105.                 float.Parse(values[6], CultureInfo.InvariantCulture)
    106.             );
    107.             record.throttle = float.Parse(values[7], CultureInfo.InvariantCulture);
    108.             record.rpm = int.Parse(values[8], CultureInfo.InvariantCulture);
    109.             record.ControlInputs = new Vector3(
    110.                 float.Parse(values[9], CultureInfo.InvariantCulture),
    111.                 float.Parse(values[10], CultureInfo.InvariantCulture),
    112.                 float.Parse(values[11], CultureInfo.InvariantCulture)
    113.             );
    114.             record.timestamp = float.Parse(values[12], CultureInfo.InvariantCulture);
    115.             record.airspeed = float.Parse(values[13], CultureInfo.InvariantCulture);
    116.             record.verticalspeed = float.Parse(values[14], CultureInfo.InvariantCulture);
    117.             record.altitude = float.Parse(values[15], CultureInfo.InvariantCulture);
    118.  
    119.             // Add the record to the list
    120.             replay.actionReplayRecords.Add(record);
    121.         }
    122.     }
    123. }
     
    Last edited: May 7, 2023
  2. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    38,697
    Gotta check the runtime logs... it's probably throwing an exception you can get some information from.

    The first place I would check is that StandaloneFileBrowser because it might need some browser or OS permissions or something. I'd even wonder if it would work at all in WebGL... I think in WebGL you have to store stuff in the PlayerPrefs or else whatever the Kool Kids are using in WebGL to get at the browser datastore.

    Basically, my usual debug blurb:

    Time to start debugging! Here is how you can begin your exciting new debugging adventures:

    You must find a way to get the information you need in order to reason about what the problem is.

    Once you understand what the problem is, you may begin to reason about a solution to the problem.

    What is often happening in these cases is one of the following:

    - the code you think is executing is not actually executing at all
    - the code is executing far EARLIER or LATER than you think
    - the code is executing far LESS OFTEN than you think
    - the code is executing far MORE OFTEN than you think
    - the code is executing on another GameObject than you think it is
    - you're getting an error or warning and you haven't noticed it in the console window

    To help gain more insight into your problem, I recommend liberally sprinkling
    Debug.Log()
    statements through your code to display information in realtime.

    Doing this should help you answer these types of questions:

    - is this code even running? which parts are running? how often does it run? what order does it run in?
    - what are the names of the GameObjects or Components involved?
    - what are the values of the variables involved? Are they initialized? Are the values reasonable?
    - are you meeting ALL the requirements to receive callbacks such as triggers / colliders (review the documentation)

    Knowing this information will help you reason about the behavior you are seeing.

    You can also supply a second argument to Debug.Log() and when you click the message, it will highlight the object in scene, such as
    Debug.Log("Problem!",this);


    If your problem would benefit from in-scene or in-game visualization, Debug.DrawRay() or Debug.DrawLine() can help you visualize things like rays (used in raycasting) or distances.

    You can also call Debug.Break() to pause the Editor when certain interesting pieces of code run, and then study the scene manually, looking for all the parts, where they are, what scripts are on them, etc.

    You can also call GameObject.CreatePrimitive() to emplace debug-marker-ish objects in the scene at runtime.

    You could also just display various important quantities in UI Text elements to watch them change as you play the game.

    Visit Google for how to see console output from builds. If you are running a mobile device you can also view the console output. Google for how on your particular mobile target, such as this answer or iOS: https://forum.unity.com/threads/how-to-capturing-device-logs-on-ios.529920/ or this answer for Android: https://forum.unity.com/threads/how-to-capturing-device-logs-on-android.528680/

    If you are working in VR, it might be useful to make your on onscreen log output, or integrate one from the asset store, so you can see what is happening as you operate your software.

    Another useful approach is to temporarily strip out everything besides what is necessary to prove your issue. This can simplify and isolate compounding effects of other items in your scene or prefab.

    Here's an example of putting in a laser-focused Debug.Log() and how that can save you a TON of time wallowing around speculating what might be going wrong:

    https://forum.unity.com/threads/coroutine-missing-hint-and-error.1103197/#post-7100494

    "When in doubt, print it out!(tm)" - Kurt Dekker (and many others)

    Note: the
    print()
    function is an alias for Debug.Log() provided by the MonoBehaviour class.
     
  3. Aviation_Simmer

    Aviation_Simmer

    Joined:
    Aug 30, 2021
    Posts:
    110
    I've tried with debugging, but there is no error / warning / info before it crashes :/
    but then why does it work perfectly fine in the editor?

    (and nope I am not using WebGL)
     
  4. Brathnann

    Brathnann

    Joined:
    Aug 12, 2014
    Posts:
    7,187
    Editor != build. That's why.

    That being said, you'll need to start tracking down the trouble code. You said when you click, but you don't specify what you were clicking on. Or what code is suppose to run when you click. Once you get to that point, start commenting out code, do a build, repeat and see if you get a crash again. Otherwise, walk through the logic.

    In the end, always remember, order of operation can be different in builds vs editor. (ie, you don't know which scripts awake runs first if you haven't specified it for example.)