- Игровой мир площадью 16 км2 (4х4 км).
- Всего в игровом мире будет примерно 16 000 000 объектов. Усредненно 1 объект на 1 м2.
- Каждый объект будет иметь 11 значений, которые надо сохранить: string с адресом префаба в папке Resources, 6 float'ов позиции и вращения и 4 дополнительных byte-переменных для сохранения важных скриптовых данных, типа здоровья, уровня топлива в транспорте и т.д.
Синтаксис:
Используется csharp
class Entity
{
public string prefabAddress;
public float positionX;
public float pozitionY;
public float positionZ;
public float rotationX;
public float rotationY;
public float rotationZ;
public byte extra1;
public byte extra2;
public byte extra3;
public byte extra4;
}
{
public string prefabAddress;
public float positionX;
public float pozitionY;
public float positionZ;
public float rotationX;
public float rotationY;
public float rotationZ;
public byte extra1;
public byte extra2;
public byte extra3;
public byte extra4;
}
Так вот проблема в том, что 1 млн. объектов (это 1 км2 площади мира) занимает 46 мб. При попытке сохранить 16 млн. объектов, редактор зависает. Нехитрым умножением получаем 736 мб. Это слишком много.
Как мне сохранить нужные данные для столь большого числа объектов, чтобы размер данных, как на жестком диске, так и в оперативной памяти, не превышал... ну предположим, 100 мб?
Может это звучит как требование чуда, но экспериментируя, я узнал, что если бы можно было все 11 переменных записать в одну строку string через пробел, то размер файла с 1 млн. объектов составил бы 19 мб (против 46 мб, если все сохранять отдельно). Выигрыш почти в 2.5 раза. А можно придумать еще эффективнее? Например, шифровать несколько переменных в одну переменную, а при необходимости расшифровывать обратно.
Вот и спрашиваю у вас, может подкинете идейку.
P.S.
Вот тестовый образец, в котором я экспериментирую. Ничего особенного, просто экспериментальная модель сохранения всего мира игры. Поэтому там что-то про сектора, на которые поделен мир (размер сектора 10х10 м), каждый сектор как бы хранит список объектов, которых на самом деле нет (сериализуются случайные данные). Главное- sectorsCount (количество секторов размером 10х10 м) определяют размер мира, entitiesCountInSector- сколько объектов внутри каждого сектора. Все это в данный момент просто случайные данные.
Синтаксис:
Используется csharp
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.Runtime.Serialization.Formatters.Binary;
using System.IO;
using System.Threading;
namespace Test
{
[System.Serializable]
class Container
{
public Sector[] sectors;
}
[System.Serializable]
class Sector
{
public List<Entity> entities;
}
[System.Serializable]
class Entity
{
public string prefabAddress;
public float positionX;
public float pozitionY;
public float positionZ;
public float rotationX;
public float rotationY;
public float rotationZ;
public byte extra1;
public byte extra2;
public byte extra3;
public byte extra4;
public void SetValue(int f)
{
//Тупо напихиваем в класс случайные данные, чтобы симулировать,
//будто мы сериализовали настоящий объект в Entity.
this.positionX = f;
this.pozitionY = f;
this.positionZ = f;
this.rotationX = f;
this.rotationY = f;
this.rotationZ = f;
this.extra1 = 111;
this.extra2 = 12;
this.extra3 = 23;
this.extra4 = 64;
}
}
public class SaveLoadAsync : MonoBehaviour
{
int sectorsCount = 10000; //количество секторов 10х10 м
int entitiesCountInSector = 100; //среднее количество объектов в 1 секторе
Sector[] sectorsArray; //все сектора мира
string path; //путь сохранения
private void Start()
{
sectorsArray = new Sector[sectorsCount];
path = Application.persistentDataPath + "/AsyncSave.dat";
CreateSectors();
}
//создаем сектора и заполняем их случайными данными, т.к. нам важен лишь размер
void CreateSectors()
{
for (int i = 0; i < sectorsCount; i++)
{
List<Entity> list = new List<Entity>();
for (int j = 0; j < entitiesCountInSector; j++)
{
Entity e = new Entity();
e.SetValue(i);
list.Add(e);
}
sectorsArray[i] = new Sector();
sectorsArray[i].entities = list;
}
}
//асинхронная загрузка и сохранение секторов
private void Update()
{
if (Input.GetKeyDown(KeyCode.R))
{
var thread = new Thread(Save);
thread.Start();
}
if (Input.GetKeyDown(KeyCode.F))
{
var thread = new Thread(Load);
thread.Start();
}
}
void Save()
{
BinaryFormatter bf = new BinaryFormatter();
FileStream fs = File.Create(path);
Container container = new Container();
container.sectors = sectorsArray;
bf.Serialize(fs, container);
fs.Close();
Debug.Log("Save! " + sectorsArray.Length);
}
void Load()
{
if (File.Exists(path))
{
BinaryFormatter bf = new BinaryFormatter();
FileStream fs = File.Open(path, FileMode.Open);
Container container = (Container)bf.Deserialize(fs);
sectorsArray = container.sectors;
fs.Close();
Debug.Log("Load! " + sectorsArray.Length);
}
}
}
}
using System.Collections.Generic;
using UnityEngine;
using System.Runtime.Serialization.Formatters.Binary;
using System.IO;
using System.Threading;
namespace Test
{
[System.Serializable]
class Container
{
public Sector[] sectors;
}
[System.Serializable]
class Sector
{
public List<Entity> entities;
}
[System.Serializable]
class Entity
{
public string prefabAddress;
public float positionX;
public float pozitionY;
public float positionZ;
public float rotationX;
public float rotationY;
public float rotationZ;
public byte extra1;
public byte extra2;
public byte extra3;
public byte extra4;
public void SetValue(int f)
{
//Тупо напихиваем в класс случайные данные, чтобы симулировать,
//будто мы сериализовали настоящий объект в Entity.
this.positionX = f;
this.pozitionY = f;
this.positionZ = f;
this.rotationX = f;
this.rotationY = f;
this.rotationZ = f;
this.extra1 = 111;
this.extra2 = 12;
this.extra3 = 23;
this.extra4 = 64;
}
}
public class SaveLoadAsync : MonoBehaviour
{
int sectorsCount = 10000; //количество секторов 10х10 м
int entitiesCountInSector = 100; //среднее количество объектов в 1 секторе
Sector[] sectorsArray; //все сектора мира
string path; //путь сохранения
private void Start()
{
sectorsArray = new Sector[sectorsCount];
path = Application.persistentDataPath + "/AsyncSave.dat";
CreateSectors();
}
//создаем сектора и заполняем их случайными данными, т.к. нам важен лишь размер
void CreateSectors()
{
for (int i = 0; i < sectorsCount; i++)
{
List<Entity> list = new List<Entity>();
for (int j = 0; j < entitiesCountInSector; j++)
{
Entity e = new Entity();
e.SetValue(i);
list.Add(e);
}
sectorsArray[i] = new Sector();
sectorsArray[i].entities = list;
}
}
//асинхронная загрузка и сохранение секторов
private void Update()
{
if (Input.GetKeyDown(KeyCode.R))
{
var thread = new Thread(Save);
thread.Start();
}
if (Input.GetKeyDown(KeyCode.F))
{
var thread = new Thread(Load);
thread.Start();
}
}
void Save()
{
BinaryFormatter bf = new BinaryFormatter();
FileStream fs = File.Create(path);
Container container = new Container();
container.sectors = sectorsArray;
bf.Serialize(fs, container);
fs.Close();
Debug.Log("Save! " + sectorsArray.Length);
}
void Load()
{
if (File.Exists(path))
{
BinaryFormatter bf = new BinaryFormatter();
FileStream fs = File.Open(path, FileMode.Open);
Container container = (Container)bf.Deserialize(fs);
sectorsArray = container.sectors;
fs.Close();
Debug.Log("Load! " + sectorsArray.Length);
}
}
}
}