Entitas icon indicating copy to clipboard operation
Entitas copied to clipboard

By using Entitas, is there a proper way to serialize a frame?

Open ijovi23 opened this issue 5 years ago • 5 comments

Hi,

As we all know that ECS makes data and behavior separated. So I wanna get a proper way to serialize all entities and their components (excluding rendering components). Given that a component is not supposed to present methods, how can I serialize them? It's useful to save a state and recover it at a later time.

Cheers

ijovi23 avatar Dec 21 '18 16:12 ijovi23

You can write a flag component like "RecordableComponent" and use JSON.net to serialize instances. However, I'm almost sure that this is not what you want by practice. For cool things like kill cam and rewindable gameplay, it's important to filter out unnecessary data, otherwise, the player's memory/harddisk would be blown up quickly because you're storing data at the 60Hz frequency.

So as you can see, it's not an Entitas related problem. IIRC, Zaks made a talk about how to implement a playback system, you can google that video, it's insightful.

zhuchun avatar Dec 27 '18 09:12 zhuchun

You can write a flag component like "RecordableComponent" and use JSON.net to serialize instances. However, I'm almost sure that this is not what you want by practice. For cool things like kill cam and rewindable gameplay, it's important to filter out unnecessary data, otherwise, the player's memory/harddisk would be blown up quickly because you're storing data at the 60Hz frequency.

So as you can see, it's not an Entitas related problem. IIRC, Zaks made a talk about how to implement a playback system, you can google that video, it's insightful.

Thanks for your reply. It's really not an easy thing to do, I think so too. I found the following demo but, its solution so damn sucks if the systems grow more and more complex. https://github.com/coding2233/Entitas-Replay-Demo

I won't store the state data at every frame. Instead, I can save a state every 30 frames, and save the user-action every frame. What you said 'RecordableComponent' may be not enough, cause when I have collected all Recordable Entities, I still don't know what to record. I need manually organize all components I want to record in a recording-system. Maybe the best way is writing a code generator to automatically generate a component includes all components that have the attribute "Recordable".

Considering server/client communication, it's necessary to do serializing/deserializing. The code may look like

    public RecordSystem(Contexts contexts)
    {
        _recordableEntities = contexts.game.GetGroup(GameMatcher.Recordable);
    }

    public void Execute()
    {
        Dictionary<int, byte[]> serialized = new Dictionary<int, byte[]>();
        foreach (GameEntity e in _recordableEntities.GetEntities())
        {
            serialized[e.EntityId] = e.GetComponents<IRecordableComponent>().SerializeToBytes()
        }

       //do next...
    }

JSON.Net needs more device resources, and bytes serialization such as protobuf breaks ECS rules.

ijovi23 avatar Dec 30 '18 13:12 ijovi23

I recorded and serialized the content data of the component. Next I wanna find a way to know what component the data belongs to.


public interface IRecordableComponent {}

[Game]
public class PositionComponent : IComponent, IRecordableComponent
{
    public float x;
    public float y;
}

[Game]
public class RecordableComponent : IComponent {}

public class RecordSystem : IExecuteSystem
{
    readonly GameContext _context;

    public RecordSystem(Contexts contexts)
    {
        _context = contexts.game;
    }

    public void Execute()
    {
        Dictionary<int, IComponent[]> entityData = new Dictionary<int, IComponent[]>();
        var entities = _context.GetGroup(GameMatcher.Recordable);
        foreach (var e in entities)
        {
            var comps = e.GetComponents().Where((c) => c is IRecordableComponent).ToArray();
            entityData[e.creationIndex] = comps;
        }

        if (entityData.Count > 0)
        {
            Debug.Log(JsonConvert.SerializeObject(entityData));
        }
    }
}

ijovi23 avatar Dec 30 '18 15:12 ijovi23

If you are making a multiplayer game, don't use JSON, it's still too big unless your game is simple enough. We usually use a code generator to create contracts for State, Input and other stuff then serialize them as a dict/hashtable byte[]. Google GafferOnGames, there are lots of essential posts about networking on it and you will see how picky it is to transfer data in a real game. In short, every bit you saved is $$$ :wink:

zhuchun avatar Dec 31 '18 03:12 zhuchun

If you are making a multiplayer game, don't use JSON, it's still too big unless your game is simple enough. We usually use code generator to create contracts for State, Input and other stuff then serialize them as a dict/hashtable byte[]. Google GafferOnGames, there are lots of network essential posts on it and you will see how picky it is to transfer data in a real game. In short, every bit you saved is $$$ 😉

You are so cool!

ijovi23 avatar Dec 31 '18 04:12 ijovi23