I’m currently working on scene loading and loading save games. I’m a bit confused about when I can manipulate the scene objects in order to restore the state from the save game.
For example, lets say I want to load a scene and then restore an object’s saved position. If I write the following code, it will not work:
So then I was trying to decide, at what point can I set the position or otherwise manipulate the scene object? I looked at the order of execution doc here: Unity - Manual: Order of execution for event functions . Do I need to use the WaitForEndOfFrame thing? I’m not sure I really understand it. I’ve done plenty of async stuff in other languages but not in C#.
But then it just never calls the code that updates the object positions.
Am I turning restoreGameObjectState into an async method with that code? If it’s yielding, something has to wake it back up, no? Why do they yield twice in the example from the doc. I tried to replicate that to a degree. I think this is maybe me not understanding C#?
I’m starting to get a better understanding just reading up more on C#.
It returns the IEnumerator. And it is intended to return multiple things potentially, thus why the two yields. The second one that returns null tells the caller that it’s done with the enumeration. And presumably, it is Unity that is generally intended to read this enumeration. So it’s probably meant to go back to one of the standard Unity lifecycle methods.
Trouble with this is, loading a game is triggered by a button click. I’m using the UI Toolkit. The button click is a callback function registered to the button. That callback method is the one calling the code to actually load the save. I’m not sure how that would get back to Unity. Maybe I can return the IEnumerator from the callback function? Also I noticed that when calling another method in a chain, in the example, they are not doing anything with the returned IEnumerator, they just call the function. I’m guessing that’s a C# thing. I guess I’ll try that and report back.
Just to give an idea of the sequence and how I imagine it could work:
private IEnumerator buttonCallback() {
controller.loadCurrentGame();
yield return null;
}
public IEnumerator loadCurrentGame() {
persistService.loadGame();
// maybe do other stuff
yield return null;
}
public IEnumerator loadGame() {
SceneManager.loadScene(sceneName);
yield return new WaitForEndOfFrame();
// set positions and stuff here
yield return null;
}
But this would only be if I’m not still confused and if Unity will actually allow that from a UI Toolkit button callback.
public void loadGame() {
SceneManager.loadScene(sceneName);
IEnumerator prepSceneCoroutine = prepScene(data);
StartCoroutine(prepSceneCoroutine);
}
private IEnumperator prepScene(data) {
yield return new WaitForEndOfFrame() // or optionally, maybe just yield return null
// do stuff with data
}
SceneLoaded method:
void OnEnable() {
SceneManager.sceneLoaded += prepScene;
}
public void loadGame() {
data = whatever
SceneManager.loadScene(sceneName);
}
void prepScene(Scene scene, LoadSceneMode mode) {
// do stuff with data
}
void OnDisable() {
SceneManager.sceneLoaded -= prepScene;
}
So far the coroutine method isn’t working. It runs without errors, and I can see it try to move the object. But when scene starts the object is back to the original starting position. I thought maybe this was complicated by the fact that because a menu is open, I have Time.timeScale set to 0, but I took that out and it didn’t change anything.
I tried the sceneLoaded method and it mostly works. It was able to restore the position of a generic game object. However, my player object, which has more complicated scripts attached, was not restored to the saved position. I’ll have to do more debugging on that to understand what else can interfere with restoring state. I think the player already thinks it knows where it is and when I try to change the position, it’s overridden in the control script. I need to implement some way to halt player control until after state is loaded.
EDIT: I bet the CharacterController is gumming up the works. I probably have to use the CharacterController.Move method for the player character since it has a CharacterController attached.
EDIT(2): It worked to use the CharacterController.Move method. Though since that method has relative movement, the actual line of code looks like this: