2
\$\begingroup\$

Many times I need to adjust the position or rotation angle of object in play mode but when I got back to editor mode I found all my work is gone! I looking on InterNet for a solution and I found this scrip. Where using tags to identify the object that I need to adjust it In "play mode". What ever I do with transform values of this object is saved when exit from "playMode".

using System.Collections;
using System.IO;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;

public class saveobjectPositions : MonoBehaviour
{

     // Made by Filmstorm - Author: Kieren Hovasapian 
     // Object Minder - Keeps In-Game Object Changes in Sync with Editor


    [Header("State of Play")]

    public bool InEditor;
    public bool InGame;

    [Header("Controls")]

    public bool setValuesToLastSave;
    public string tagToSave;

    public GameObject[] FoundObjectsInGame;
    public List<GameObject> InGameObjects;


    string savePath;



    private void Awake()
    {
        InGameObjects = new List<GameObject>();

        //Setup save path 
        savePath = Path.Combine(Application.persistentDataPath, "saveFile");
    }

    //Save our data from the scenes objects
    void Save()
    {
        using (var writer = new BinaryWriter(File.Open(savePath, FileMode.Create)))
        {
            for (int i = 0; i < InGameObjects.Count; i++)
            {
                Transform t = InGameObjects[i].transform;
                writer.Write(t.localPosition.x);
                writer.Write(t.localPosition.y);
                writer.Write(t.localPosition.z);
                writer.Write(t.localRotation.w);
                writer.Write(t.localRotation.x);
                writer.Write(t.localRotation.y);
                writer.Write(t.localRotation.z);
                writer.Write(t.localScale.x);
                writer.Write(t.localScale.y);
                writer.Write(t.localScale.z);

            }
        }

        //Debug.Log("I tried to save!");
    }

    //Load specific data we saved previously
    void Load()
    {



        using (var reader = new BinaryReader(File.Open(savePath, FileMode.Open)))
        {
            for (int i = 0; i < InGameObjects.Count; i++)
            {
                Vector3 p;
                Quaternion r;
                Vector3 s;
                p.x = reader.ReadSingle();
                p.y = reader.ReadSingle();
                p.z = reader.ReadSingle();
                r.w = reader.ReadSingle();
                r.x = reader.ReadSingle();
                r.y = reader.ReadSingle();
                r.z = reader.ReadSingle();
                s.x = reader.ReadSingle();
                s.y = reader.ReadSingle();
                s.z = reader.ReadSingle();

                //set position from load data
                InGameObjects[i].transform.localPosition = p;
                InGameObjects[i].transform.localRotation = r;
                InGameObjects[i].transform.localScale = s;




                if (i == InGameObjects.Count - 1)
                {
                    //Last operation now we break 
                    setValuesToLastSave = false;
                }

            }
        }
    }
    // Use this for initialization
    void Start()
    {

        //Check if the game is in play or in editor mode
        if (Application.isPlaying)
        {
            //Debug.Log("Running in play mode");
            InEditor = false;
            InGame = true;
            setValuesToLastSave = true;
        }
        else
        {
            //Debug.Log("In editor");
            InEditor = true;
            InGame = false;
            setValuesToLastSave = true;
        }



    }

    // Update is called once per frame
    void Update()
    {

        //If in game -- find all objects and store their transforms
        if (tagToSave != null)
        {
            FoundObjectsInGame = GameObject.FindGameObjectsWithTag(tagToSave);
            foreach (GameObject go in FoundObjectsInGame)
            {
                if (go.activeInHierarchy)
                {
                    if (!InGameObjects.Contains(go))
                        //Save Gameobject to List?
                        InGameObjects.Add(go);

                }
            }

            //If in editor run the updated loader
            if (InEditor)
            {
                if (setValuesToLastSave == true)
                {
                    Load();

                }
            }

        }
    }

    //When we stop playing save object locations
    private void OnApplicationQuit()
    {
        Save();
    }

}

Here is the issue, When I get back to "Editor mode" I can't get these values. To get them, I have to run again "play mode" and click on "InEditor" bool to get them. Is there a way to change this and get the transform values that saved in play mode into Editor mode ?

\$\endgroup\$

3 Answers 3

2
\$\begingroup\$

The only way to save the values I know of is to go to the transform component, press the gear icon and copy the component, then after leaving play mode you can paste the values back using "paste" button in the same menu

\$\endgroup\$
1
  • \$\begingroup\$ This thing is common. I need something updating transform values from "PlayMode" to "EditorMode". \$\endgroup\$
    – user43474
    May 1, 2019 at 2:45
1
\$\begingroup\$

You can use console window to record values in playMode and then copy it. This script will help you to get last details of XYZ axises. Just try to tag these objects that you want to record there values.

    public GameObject[] Objects_List;

// Update is called once per frame
void Update()
{
    Objects_List = GameObject.FindGameObjectsWithTag("changer");

    foreach (GameObject Objects in Objects_List)
    {
        print(Objects.transform.parent.name + " , " + Objects.name + "( " + Objects.transform.localPosition.x + " )*( " + Objects.transform.localPosition.y + " )*( " + Objects.transform.localPosition.z + " )");
    }
}

Here you will get first parent object name. then the object that you tagged with his values. You can delete the parent part if you want. It's simple and helpful.

\$\endgroup\$
1
  • \$\begingroup\$ I have to copy manually the values from the console window. No save to load when i get back editor mode. good and better than copy paste one by one. Thank you. \$\endgroup\$
    – user43474
    May 3, 2019 at 4:46
1
\$\begingroup\$

I have a heavy solution, but it is working very good. For serialization use mem.cs. This is a scriptable object and a new one must be created from the asset menu in the projects area. It is used like a memory card, name it and insert it into any script to store specific data. **You must call mem?.Load() ** from any script you are using it in to load up the data, it is not done by default.


using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;

[CreateAssetMenu(menuName ="mem")]
public class mem : ScriptableObject {

    public string recordID;
    Dictionary<string, object> items = new Dictionary<string, object>();
    Dictionary<string, object> loadedItems = new Dictionary<string, object>();
    bool loadCalled;
    //must call Load() before use
    public void Load()
    {
        try
        {
            byte[] g = File.ReadAllBytes(Application.persistentDataPath + "/" + recordID);
           items = (Dictionary<string,object>) ByteArrayToObject(g);
            Debug.Log("MEMORY LOADED: "+items.Count);
        }
        catch (Exception e)
        {
            Debug.Log(e.ToString());
        }

        loadCalled = true;
    }

    public object GetFromMem(string nm)
    {
        if(!loadCalled)
        {
            Debug.LogWarning("Load Not Called " + nm);
        }
        object o;
        if (items.TryGetValue(nm, out o))
        {
              Debug.Log(nm+ " got " + o);
              if (!loadedItems.ContainsKey(nm))
              loadedItems.Add(nm,o);
            return o;
        }
        else
            return default(object);

    }

    public bool AddToMem(object o,string nm)
    {
        try
        {
            if (!items.ContainsKey(nm))
                items.Add(nm, o);
            else
                items[nm] = o;
     //      Debug.Log("Add "+ nm+" vl:"+o);
            File.WriteAllBytes(Application.persistentDataPath + "/" + recordID, ObjectToByteArray(items));
            return true;
        }
        catch(Exception e) { Debug.Log(e.ToString()); }

        return false;

    }

    public void ClearUnused()
    {
        items.Clear();
        items=loadedItems;
     File.WriteAllBytes(Application.persistentDataPath + "/" + recordID, ObjectToByteArray(items));
    }

    // Convert a byte array to an Object
    public static object ByteArrayToObject(byte[] arrBytes)
    {
        using (var memStream = new MemoryStream())
        {
            var binForm = new BinaryFormatter();
            memStream.Write(arrBytes, 0, arrBytes.Length);
            memStream.Seek(0, SeekOrigin.Begin);
            var obj = binForm.Deserialize(memStream);
            return obj;
        }
    }

    // Convert an object to a byte array
    public static byte[] ObjectToByteArray(object obj)
    {
        BinaryFormatter bf = new BinaryFormatter();
        using (var ms = new MemoryStream())
        {
            bf.Serialize(ms, obj);
            return ms.ToArray();
        }
    }
}

This next one is Unitmon.cs, it uses mem.cs to store and retrieve all data, and monitors everything on load and start. Add it to a solo gameobject and toggle run to update manually. Run executes automatically on the editor loading and on play mode loading. The refreshed buffer allows the script to run a few times after play mode is exited. I have noticed it takes a few frames to begin updating edit mode scripts again.

//THE BIG WORKHORSE TO MANAGE ALL UNITS. 

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;

[InitializeOnLoad]
[ExecuteInEditMode]
public class Unitmon : MonoBehaviour
{
    internal static Unitmon M;
    public mem storage;
    public bool run;
    public List<Unit> collected;
    static int refreshed=5;

    void OnEnable()
    {
        Awake();
        Start();
    }

    void Awake()
    {
        if(M && M.gameObject && M !=this)
            Destroy(gameObject);

        M=this;
    }

    // Start is called before the first frame update
    void Start()
    {
        if(storage)
        {
            storage.Load();
        }
        run=true;
        Update();
    }

    // Update is called once per frame
    void Update()
    {
        if(Application.isPlaying)
            refreshed=5;//PLAY -> EDIT MODE 2 FRAME SKIP BUFFER
        else
            if(refreshed>0)
            {   
                run=true;
                refreshed--;
            }

        if(run)
        {
            collected.Clear();

            collected.AddRange(FindObjectsOfType<Unit>());

            foreach(Unit u in collected)
            {
                Unit.UnitData ud  = (Unit.UnitData)storage.GetFromMem(u.uData.hash.ToString());
                if(ud!=null)
                    u.Setup(ud);
            }
            storage.ClearUnused();


            run=false;
        }
    }


    internal static void UpdateUnit(Unit u)
    {
        if(M.collected.Contains(u))
        {
            M.storage.AddToMem(u.uData,u.uData.hash.ToString());
        }
    }
}

This final component is the Unit.cs, managed by Unitmon. Attach the Unit to any gameobject to save its properties both during runtime and edit mode. The UnitData class is used and can be extended to include any type of data other than rotation and position. Enjoy!

//THE UNIT OF THE UNITY GAME ENGINE
using UnityEngine;

[ExecuteInEditMode]
public class Unit : MonoBehaviour
{
    [System.Serializable]
    public class UnitData
    {
        public int hash;
        public float[] pos=new float[3];
        public float[] rot=new float[3];
        public Vector3 position
        {
            get{return new Vector3(pos[0],pos[1],pos[2]);}
            set{pos[0]=value.x; pos[1]=value.y; pos[2]=value.z;}
        }
            public Vector3 rotation
        {
            get{return new Vector3(rot[0],rot[1],rot[2]);}
            set{rot[0]=value.x; rot[1]=value.y; rot[2]=value.z;}
        }
    }

    internal UnitData uData=new UnitData();
    bool somethingChanged;


    internal void Setup(UnitData ud)
    {
        if(ud !=null)
        {
            print(ud.hash+" setup");
            uData=ud;
            if(uData.position != Vector3.zero)//SET ONLY IF UPDATED FROM DEFAULT
                transform.position =uData.position;
            if(uData.rotation != Vector3.zero)//SET ONLY IF UPDATED FROM DEFAULT
                transform.rotation = Quaternion.Euler(uData.rotation);
        }

    }

    void Awake()
    {

    }

    // Start is called before the first frame update
    void Start()
    {

    }

    // Update is called once per frame
    void Update()
    {
        uData.hash=GetInstanceID();
        if(Application.isPlaying)
        {
            if(uData.position!= transform.position)
            {
                uData.position=transform.position;

                somethingChanged=true;
            }
            if(uData.rotation!= transform.rotation.eulerAngles)
            {
                uData.rotation=transform.rotation.eulerAngles;

                somethingChanged=true;
            }

        }
        if(somethingChanged)
        {
            Unitmon.UpdateUnit(this);
            somethingChanged=false;
        }

    }
}
\$\endgroup\$

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .