Saturday, July 5, 2014

Задача 8: Сохранение и загрузка состояния сцены. Временной плеер.


Практическое использование движка Unity3d.


Задача 8: Сохранение и загрузка состояние сцены. Временной плеер.


  Решим следующую задачу: необходимо реализовать «Unity3d плеер сцены». Этот плеер умеет сохранять позиции всех объектов в сцене, а после сохранения может восстановить их позиции. Это возможность может использоваться в играх, для создания эффекта «вернуться во времени», ну и вообще это очень прикольная фича :).

  Программа должна выглядеть следующим образом: в приложении падают определенное количество физический объектов на плоскость. Соударяются и падают с плоскости. На экране имеется кнопка «Record», которая позволяет начать запись сцены. По ее нажатию появляется кнопка «Recording…Stop». После того как пользователь нажал на кнопку «Recording…Stop», программа останавливает запись сцены и показывает слайдер, с помощью которого можно вернуть сцену в любой записанный промежуток времени.
  Решим эту задачу. Основная суть приложения – это сохранять позиции объектов в определенные промежутки времени. Поэтому для этого создадим типы данных и переменные, с помощью которых мы сможем сохранять позиции всех объектов.

/// 
/// 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;
}
  Переменная, которая будет хранить позиции этих объектов это массив из Dictionary. Каждая единица массива хранить состояние сцены в текущий момент времени:
private List< Dictionary < GameObject, SavedState > > mSavedStates = new List< Dictionary< GameObject, SavedState > >();


  Теперь нужно понять, как мы будем включать и выключать физическое воздействие на объекты. У любого объекта, который работает по физическим законам в Unity3d есть компонент «Rigidbody» (см. http://docs.unity3d.com/ScriptReference/Rigidbody.html). Для того чтоб включить или выключить физическое взаимодействие физики на данный «Rigidbody», нужно использовать свойство «Rigidbody.isKinematic» (см. http://docs.unity3d.com/ScriptReference/Rigidbody-isKinematic.html). Следовательно, функция включения и выключения взаимодействия физики будет выглядеть следующим образом:
/// 
/// Just set "Kinematic" for all game objects
/// 
/// 
private void EnablePhysics(bool enable)
{
    Rigidbody[] rigid = GameObject.FindObjectsOfType();
    foreach (Rigidbody r in rigid)
    {
        r.isKinematic = !enable;
    }
}


  Т.к. в программе явно существует несколько состояний (обычное, запись, проигрывание) то создадим перечисление и переменную, которая будет отвечать за текущее состояние приложения. Эта переменная будет использоваться во всех основных функциях: Update, OnGUI.

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


  И переменная:
// current game state
private EGameState mGameState = EGameState.None;

  Для того чтоб сохранить положение любого объекта в Unity3d нужно сохранить позиции, вращение и растяжение объекта. Таким образом функция сохранения состояния будет выглядеть так:
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);
 }


  А установка определенного состояния будет выглядеть так:
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;
    }
 }


  Демонстрацию работы можно скачать здесь:
https://dl.dropboxusercontent.com/u/20023505/Articles/Unity3d/Lesson8/Build/Build.zip

  Посмотреть онлайн работу можно здесь:
https://dl.dropboxusercontent.com/u/20023505/Articles/Unity3d/Lesson8/WebPlayer/WebPlayer.html
  Исходные коды работы, как всегда бесплатно, можно скачать здесь:
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