Saturday, July 5, 2014

Task #8: Save and load game state. Time player.

Practical use of Unity3d engine.


Task #8: Save and load game state. Time player.

  There is such task: we need to implement “Scene player in Unity3d”. This player is able to save positions of all objects in the scene and after saving and can restore game objects’ positions. This functionality may be used in games for “back to time” task implementation and also it is a very cool feature :).
  The application should have such functionality: there are a lot of objects fall on the plane. They push each other and fall from the plane. There is button “Record” on the screen. This button allows to start the recording of the scene. After user presses the “Record” button, the button “Recording…Stop” will appear. This button stops the recording of the scene and shows the slider. This slider allows to revert scene state to any recorded time frame.
  Let’s solve this task. The main idea of the application it is to save positions of all objects on the scene in determinate time frames. So, we should create data types and variables. This will help us to save the scene state.

/// 
/// This is data stored for one frame of the saved data
/// 
public class SavedState
{
    public GameObject GO;
    public float Time;
    public Vector3 Position;
    public Quaternion Rotation;
    public Vector3 ScaleLocal;
}


  Array of Dictionaries it is a variable that will save positions’ of scene objects. Each item of the array will store the scene state in current time frame.

private List< Dictionary < GameObject, SavedState > > mSavedStates = new List < Dictionary < GameObject, SavedState > >();

  Now we should determine how we will enable and disable physical influence on the objects. Any objects of the scene that uses physics has “Rigidbody” component (see: http://docs.unity3d.com/ScriptReference/Rigidbody.html). To enable or disable physical influence on objects we should use the “Rigidbody.isKinematic” property (see: http://docs.unity3d.com/ScriptReference/Rigidbody-isKinematic.html). So, function of enabling or disable the physical influence will be such:

/// 
/// Just set "Kinematic" for all game objects
/// 
/// 
private void EnablePhysics(bool enable)
{
    Rigidbody[] rigid = GameObject.FindObjectsOfType();
    foreach (Rigidbody r in rigid)
    {
        r.isKinematic = !enable;
    }
}


  As far as application has several states (simple, recording, playing) we will create enumeration and variable that will be responsible for the current game state. This variable will be used in all main functions: Update, OnGUI.

/// 
/// Possible application states
/// 
public enum EGameState
{
    None = 0,
    Recording,
    Reproduce
}


  And here is variable:

// current game state
private EGameState mGameState = EGameState.None;


  To save state of each Unity3d object in the scene we should save position, rotation and scale of this object. In such a way save of the state function will be such:

private void SaveGosCurrentState()
{
    if (mGameState != EGameState.Recording)
    {
        return;
    }

    GameObject[] gos = GameObject.FindObjectsOfType();

    // create current time state
    Dictionary stateData = new Dictionary();

     // add game objects to dict
     foreach (GameObject go in gos)
     {
         SavedState state = new SavedState() 
         { 
             GO = go,
             Position = go.transform.position,
             Rotation = go.transform.rotation,
             ScaleLocal = go.transform.localScale,
             Time = Time.realtimeSinceStartup
         };
         stateData[go] = state;
     }

     // add current time state to list
     mSavedStates.Add(stateData);
 }

  And setting the determinate time state is presented here:

private void SetGosStateByDict(Dictionary currentStateData)
{
    // restore time data
    foreach (KeyValuePair kvp in currentStateData)
    {
         SavedState savedData = kvp.Value;
         GameObject goToRestore = savedData.GO;

         // restore transformations
         goToRestore.transform.position = savedData.Position;
         goToRestore.transform.rotation = savedData.Rotation;
         goToRestore.transform.localScale = savedData.ScaleLocal;
    }
 }


  If you need more detailed article description, please write it in comments.
  Demo of the application you can download here:
  https://dl.dropboxusercontent.com/u/20023505/Articles/Unity3d/Lesson8/Build/Build.zip
  Results of development you can see here:
  https://dl.dropboxusercontent.com/u/20023505/Articles/Unity3d/Lesson8/WebPlayer/WebPlayer.html
  Free source codes you can download here:
  https://dl.dropboxusercontent.com/u/20023505/Articles/Unity3d/Lesson8/Sources/Sources.zip
  https://github.com/den-potapenko/Unity3dArticles/tree/master/Lesson8

No comments:

Post a Comment