Уникальный id при дублировнии(клонировнии) объекта в Unity3d
В своём проекте на Unity3d пытаюсь вынести в ScriptableObject важные данные об объектах на сцене.
Объекты однотипные, но информация о каждом экземпляре в сцене должна храниться в List<T> в экземпляре SceneData : ScriptableObject. К этой информации объект сцены будет обращается в процессе игры.
Например есть класс с данными:
public class PersonData
{
long id;
string firstName;
Gender gender;
float cash;
}
Он сохраняется в List<PersonData> ScriptableObject и ссылка на экземпляр PersonData будет импортироваться в класс PersonView:
public class PersonView : MonoBehaviour
{
long id;
PersonData data;
}
Ссылка на PersonData будет добавятся PersonView при открытии сцены в редакторе и после загрузки сцены в игре. Загрузка сцены будет из assetBundle, скачиваемого через интернет, поэтому связывание данных должно быть по id, который будет генерироваться при добавлении объекта PersonView на сцену в редакторе, в методе OnEnable:
[CustomEditor(typeof(PersonView))]
public class PersonViewEditor : Editor
{
void OnEnable()
{
var pv = this.target as PersonView;
if(pv.id == 0)
{
pv.data = new PersonData();
pv.data = pv.id = System.DateTime.Now.Ticks;
EditorUtility.SetDirty(target);
}
}
}
При сохранении сцены, данные из PersonView сохраняются в PersonData.
Проблема: при дублировании объекта(Ctrl+d), id старого объекта копируется в новый, а мне нужно чтобы она создавалась заново.
Единственный вариант, который я вижу: это в методе OnEnable() искать дубликаты, и если объект с таким же id присутствует на сцене, генерировать новый id и PersonData:
[CustomEditor(typeof(PersonView))]
public class PersonViewEditor : Editor
{
PersonView pv;
void OnEnable()
{
pv = this.target as PersonView;
if(pv.id == 0) { SetNewId(); return; }
else {
var views = FindObjectsOfType<PersonView>()
foreach(var view in views)
{
if(pv.id == view.id) { SetNewId(); return; }
}
}
}
private void SetNewId()
{
pv.data = new PersonData();
pv.data = pv.id = System.DateTime.Now.Ticks;
EditorUtility.SetDirty(target);
}
}
Ответы (1 шт):
Если я правильно понял персоны записывают в SceneData свои дефолтные значения выставленные в инспекторе если их там нет или читают если они там уже есть. Все персоны неповторимы и загвоздка в том что-бы понять какие данные для какой персоны.
Вообще данные всегда разделяют на константы и изменяемые чтобы была возможность сохранять только изменяемые. То есть firstName и gender не меняются в отличие от cash которые нужно сохранять.
[Serializable]
public struct PersonData
{
public PersonConstants Constants;
public PersonalProperties Properties;
}
[Serializable]
public struct PersonConstants
{
public string FirstName;
public Gender Gender;
}
[Serializable]
public struct PersonalProperties
{
public float Cash;
}
Сохранять нужно будить только PersonalProperties в обертке с неким id (int или string) в некой сущности с массивом через JsonUtility, а json хранить в PlayerPrefs или на сервере. В сохранении PersonConstants никакого смысла нет.
[Serializable]
public struct PersonalPropertiesSaveArray
{
public PersonalPropertiesSave[] Persons;
}
[Serializable]
public struct PersonalPropertiesSave
{
public string ID;
public PersonalProperties Properties;
}
PersonalPropertiesSaveArray save;
string json = JsonUtility.ToJson(save);
PersonalPropertiesSaveArray load;
load = JsonUtility.FromJson<PersonalPropertiesSaveArray>(json);
Касаемо идентификации кто есть кто. Я не понял почему сущность называется SceneData, вроде к конкретной сцене это не привязано, скорее PersonalDataProvider. В каждой сцене должен быть объект имеющий ссылки на всех персон на сцене, некий ScenePersonals. Именно он и отвечает за идентификацию.
public class PersonalDataProvider : MonoBehaviour
{
[SerializeField] private PersonalDataProvider _provider;
[SerializeField] private Personal[] _personals;
private void OnEnable ()
{
Load();
}
private void OnDisable ()
{
Save();
}
private void Load ()
{
_provider.Load();
for (int i = 0; i < _personals.Length; i++)
{
string id = GetID(i);
if (_provider.HasPersonal(id))
_personals[i].Data.Properys = _provider.GetPropertys(id);
}
}
private void Save ()
{
for (int i = 0; i < _personals.Length; i++)
{
string id = GetID(i);
// по id провайдер сам разберется это новые данные которые
// нужно добавить в текущий справочник или изменить имеющиеся
_provider.SetPropertys(id, _personals[i].Data.Properys);
}
_provider.Save();
}
private string GetID (int index)
{
// простой вариант, имя сцены + индекс в списке
return gameObject.scene.name+index;
}
}