Search Unity

Read and set players y rotation from file

Discussion in 'Scripting' started by unity_aB0ViU4X_pf-Cg, Sep 12, 2019.

  1. unity_aB0ViU4X_pf-Cg

    unity_aB0ViU4X_pf-Cg

    Joined:
    Feb 11, 2019
    Posts:
    12
    I am working on a maze game where a player can save the level and continue it at a later time. It saves the players position and rotation so they can continue in the same direction they were heading. The position and rotation are saved in a binary file. When I load the level only the players position is working. I cannot get the rotation to work, it always loads at zero. I did verify the rotation is saved as (0, 303.7, 0).
    The player position works using
    Code (CSharp):
    1. player.transform.position = New Vector3(x, y, z);
    I have tried
    Code (CSharp):
    1. player.transform.rotation = Quaternion.Euler(new Vector3(0, 303.7, 0));
    When the game loads the rotation is always (0,0,0).

    Thanks for any help.
     
  2. palex-nx

    palex-nx

    Joined:
    Jul 23, 2018
    Posts:
    1,171
    It must be something overrides your player rotation afterwards because the code you posted is 100% working code.
     
    Joe-Censored likes this.
  3. Joe-Censored

    Joe-Censored

    Joined:
    Mar 26, 2013
    Posts:
    6,340
    This ^^^

    I'd add a Debug.Log message next to every line of code which sets player.transform.rotation in your project. Then see if something else is overwriting this rotation or if this code isn't even being run at all.
     
  4. unity_aB0ViU4X_pf-Cg

    unity_aB0ViU4X_pf-Cg

    Joined:
    Feb 11, 2019
    Posts:
    12
    I found the problem. I put the rotation line in start(). I moved it to awake() and it's working now. I am still working on learning what to put in start and awake. Something must be overriding it in start. Thanks for the help.
     
    Last edited: Sep 13, 2019
  5. Antistone

    Antistone

    Joined:
    Feb 22, 2014
    Posts:
    1,104
    Awake happens before Start, so anything that overwrite the rotation after you were setting it in Start would also be after you set it in Awake. So there might be some other problem with what you did before.

    One possibility would be an error in Start that is preventing the function from completing. Have you checked the console for error messages?
     
  6. unity_aB0ViU4X_pf-Cg

    unity_aB0ViU4X_pf-Cg

    Joined:
    Feb 11, 2019
    Posts:
    12
    I just put together a tiny project with 1 script and a couple lines of code to see if I can duplicate the problem and the rotation does not work in start, it does work in awake. Either this is normal or there is an issue with the FPC.
    Code (CSharp):
    1. void Awake()
    2.     {
    3.         // Rotation works here
    4.         player.transform.rotation = Quaternion.Euler(new Vector3(0, -90, 0));
    5.     }
    6.     void Start()
    7.     {
    8.         // Rotation does not work here
    9.         player.transform.rotation = Quaternion.Euler(new Vector3(0, 90, 0));
    10.     }
    I remember now why I put rotation in start, because I cannot check if a file exists in awake. If the player saves a level and they come back to it later it checks for a save file. If it finds one it pulls the data from it, which includes the position and rotation of the player. Checking if a file exists in awake is always false. Is there another way to get around this?

    Here is the code for awake and start, which is on the player (FPC).
    currentLlevel just returns the scene name
    loadGameData loads data if a save file exists.
    Code (CSharp):
    1. void Awake()
    2.     {
    3.         currentLevel = FindObjectOfType<CurrentLevel>();
    4.         loadGameData = FindObjectOfType<LoadGameData>();
    5.         Cursor.lockState = CursorLockMode.Locked;
    6.         gameStatus = "0";
    7.     }
    8.     void Start()
    9.     {
    10.         if (File.Exists(Application.persistentDataPath + "/" + currentLevel.GetCurrentSceneName() + ".sav"))
    11.         {
    12.             player.transform.rotation = Quaternion.Euler(new Vector3(0, 180, 0));
    13.             player.transform.position = new Vector3(float.Parse(loadGameData.OpenMazeGameData()[0]),
    14.             float.Parse(loadGameData.OpenMazeGameData()[1]),
    15.             float.Parse(loadGameData.OpenMazeGameData()[2]));
    16.             gameStatus = loadGameData.OpenMazeGameData()[7];
    17.         }
    18.     }
     
  7. unity_aB0ViU4X_pf-Cg

    unity_aB0ViU4X_pf-Cg

    Joined:
    Feb 11, 2019
    Posts:
    12
    I think I figured it out. First of all I cannot check if a file exits in the awake function on the player script, I have no idea why but I did find a work around. I read about script execution order and thought I would try that. I created a separate script that just checks to see if the save file exists, if it does it sets a bool, and surprisingly I can check it in the awake function. I put that file and the player script file in the file execution order. The check file is 100 and the player script is 200. In the player script I can check if the bool is true in the awake function and if it is the rotation code will run.

    Here is the code for checking if the save file exists.
    Code (CSharp):
    1. private CurrentLevel currentLevel;
    2.     private bool fileExists;
    3.     void Awake()
    4.     {
    5.         currentLevel = FindObjectOfType<CurrentLevel>();
    6.         if (File.Exists(Application.persistentDataPath + "/" + currentLevel.GetCurrentSceneName() + ".sav"))
    7.         {
    8.             fileExists = true;
    9.         }
    10.     }
    11.     public bool GetFileExists()
    12.     {
    13.         return fileExists;
    14.     }
    Here is the player script.
    Code (CSharp):
    1. private CheckSaveFile checkSaveFile;
    2.     private CurrentLevel currentLevel;
    3.     private LoadGameData loadGameData;
    4.     public GameObject player;
    5.     public GameObject mazeArea;
    6.     public Collider wallCollider;
    7.     public string gameStatus;
    8.     public int collectItem;
    9.     void Awake()
    10.     {
    11.         checkSaveFile = FindObjectOfType<CheckSaveFile>();
    12.         currentLevel = FindObjectOfType<CurrentLevel>();
    13.         loadGameData = FindObjectOfType<LoadGameData>();
    14.         Cursor.lockState = CursorLockMode.Locked;
    15.         gameStatus = "0";
    16.         if (checkSaveFile.GetFileExists())
    17.         {
    18.             player.transform.rotation = Quaternion.Euler(new Vector3(0, float.Parse(loadGameData.OpenMazeGameData()[4]), 0));
    19.         }
    20.     }
    21.     void Start()
    22.     {
    23.         if (File.Exists(Application.persistentDataPath + "/" + currentLevel.GetCurrentSceneName() + ".sav"))
    24.         {
    25.             player.transform.position = new Vector3(float.Parse(loadGameData.OpenMazeGameData()[0]),
    26.             float.Parse(loadGameData.OpenMazeGameData()[1]),
    27.             float.Parse(loadGameData.OpenMazeGameData()[2]));
    28.             gameStatus = loadGameData.OpenMazeGameData()[7];
    29.         }
    30.     }
    I did test it and I can complete the level and everything seems to work. No error codes.
    UPDATE: I just noticed I think I can move everything from start into awake so I don't have to check if a file exists twice.
     
    Last edited: Sep 13, 2019
  8. palex-nx

    palex-nx

    Joined:
    Jul 23, 2018
    Posts:
    1,171
    This is not guarateed to work. Awake calls order for game object components is not defined. It may work from time to time or may break on build. The common rule I use for this thing is any script may not access other scripts during awake, only initialize itself. If it needs interaction with some another script, then this interaction must happen in Start.
     
  9. unity_aB0ViU4X_pf-Cg

    unity_aB0ViU4X_pf-Cg

    Joined:
    Feb 11, 2019
    Posts:
    12
    Thanks for the info. Then I'll work on figuring out why I cannot check if a file exists in the player script but I can in the other script.
     
  10. palex-nx

    palex-nx

    Joined:
    Jul 23, 2018
    Posts:
    1,171
    Again, System.IO.File API should work equally well in any script, there's no any difference.
     
  11. unity_aB0ViU4X_pf-Cg

    unity_aB0ViU4X_pf-Cg

    Joined:
    Feb 11, 2019
    Posts:
    12
    I have no idea what I did but the script is now working. I tested it several times before and it was not working. When I went to check if the file exists I put a debug with a bool and it would come up false every time. I hate when I fix stuff without knowing what I did, then you don't learn anything. Hopefully it stays fixed.

    Thanks again for all the help
     
  12. Antistone

    Antistone

    Joined:
    Feb 22, 2014
    Posts:
    1,104
    By default, the order of Awake calls is not defined; however, it sounds like OP modified the script execution order to enforce a particular ordering, which I believe should be safe.
     
  13. unity_aB0ViU4X_pf-Cg

    unity_aB0ViU4X_pf-Cg

    Joined:
    Feb 11, 2019
    Posts:
    12
    I was working on the game most of the day today and the problem just showed up out of the blue. It stops working do to the currentLevel.GetCurrentSceneName() going to null.
    Code (CSharp):
    1. if (File.Exists(Application.persistentDataPath + "/" + currentLevel.GetCurrentSceneName() + ".sav"))
    This is the script where it gets the name of the scene from.
    Code (CSharp):
    1. using UnityEngine;
    2. using UnityEngine.SceneManagement;
    3. // This script returns the name of the scene being currently played
    4. public class CurrentLevel : MonoBehaviour
    5. {
    6.     private string sceneName;
    7.     void Awake()
    8.     {
    9.         sceneName = SceneManager.GetActiveScene().name;
    10.     }
    11.     public string GetCurrentSceneName()
    12.     {
    13.         return sceneName;
    14.     }
    15. }
    I need to find out why the script is unable to return the scene name. I use this script to supply the name for the save file. I have 120 levels in the game and each one has a unique name. Instead of me having a file that contains all the level names I came up with this script. I should note I am not storing the name of the scene in the save file. The script supplies the name every time the scene loads.
    I debugged the sceneName variable and it returns null directly from the script. Must have something to do with Awake.
     
    Last edited: Sep 14, 2019
  14. Antistone

    Antistone

    Joined:
    Feb 22, 2014
    Posts:
    1,104
    Is there a specific reason you are caching the result of
    SceneManager.GetActiveScene().name
    instead of using it live?
     
  15. unity_aB0ViU4X_pf-Cg

    unity_aB0ViU4X_pf-Cg

    Joined:
    Feb 11, 2019
    Posts:
    12
    I did that so I didn't have to put "UnityEngine.SceneManagement" on every level. I thought it would be better to have it centrally located. I will have to fix that. The player script is running 4 frames before the current level script, so the player script doesn't have chance of getting the name. That's my lack of programming experience showing.
     
  16. Antistone

    Antistone

    Joined:
    Feb 22, 2014
    Posts:
    1,104
    Well, you can still write a helper function in another class if you find that more convenient; I'm just saying that helper function should return a live result instead of a cached result.

    As a minimal possible change, you could keep your "CurrentLevel" class, just change how "GetCurrentSceneName" works:
    Code (CSharp):
    1. using UnityEngine;
    2. using UnityEngine.SceneManagement;
    3. // This script returns the name of the scene being currently played
    4. public class CurrentLevel : MonoBehaviour
    5. {
    6.     public string GetCurrentSceneName()
    7.     {
    8.         return SceneManager.GetActiveScene().name;
    9.     }
    10. }
    If you did that, you wouldn't even have to change any of the code that currently calls the function.

    But if you weren't worried about backwards compatibility and wanted to write a helper function like this from scratch, then there's no reason for this class to be a MonoBehaviour, or even for it to be instantiated at all. You could just write a static helper class, along the lines of:

    Code (CSharp):
    1. using UnityEngine.SceneManagement;
    2.  
    3. public static class SceneHelper
    4. {
    5.     public static Scene Active()
    6.     {
    7.         return SceneManager.GetActiveScene();
    8.     }
    9. }
    10.  
    11. // Since that's all static, you don't have to put it in your scene or anything;
    12. // you just need the source code to exist in your project and you can automatically access it.
    13. // Then in some other code, you could write something like:
    14. string currentSceneName = SceneHelper.Active().name;
    ...but of course, this is a fair amount of hoops to jump through just to avoid a "using" statement.