Search Unity

How to use EntityManager.CopyAndReplaceEntitiesFrom

Discussion in 'Entity Component System' started by Ashkan_gc, Sep 2, 2019.

  1. Ashkan_gc

    Ashkan_gc

    Joined:
    Aug 12, 2009
    Posts:
    1,124
    I want to use the new CopyAndReplaceEntitiesFrom to rollback our simulation to a previous step. using it however causes the entity debugger to blink and not show stuff correctly anymore.
    Should I do anything before changing entities in the active world. I remember previously there were some APIs you had to call to start/stop a transaction when moving entities to a world.

    Currently i'm simply trying to move my sim a step forward or backward using this and store last 10 ticks in 10 worlds. I have no issue storing world.active's entities in other works I create in a start method.
    This is test code so foregive bad names.
    Code (CSharp):
    1. using FixMath.NET;
    2. using System;
    3. using System.Collections;
    4. using System.Collections.Generic;
    5. using Unity.Entities;
    6. using UnityEngine;
    7. using UnityEngine.UI;
    8.  
    9. // NOTE: Updating a manually-created system in FixedUpdate() as demonstrated below
    10. // is intended as a short-term workaround; the entire `SimulationSystemGroup` will
    11. // eventually use a fixed timestep by default.
    12. public class FixedTimestepUpdater : Singleton<FixedTimestepUpdater>
    13. {
    14.     public bool autoExecute { get; private set; }
    15.     private EntityManager[] rollbackFrames = new EntityManager[10];
    16.     private int worldToWriteOn = 0;
    17.     private int possibleRollbacks = -1;
    18.     public float fixedTimeStep = 0.05f;
    19.     public int commandsTickDelay = 25;
    20.     public int tick { get; private set; }
    21.  
    22.     private FixedRateSpawnerSystem spawnerSystem;
    23.     private MoveProjectilesSystem movementSystem;
    24.  
    25.     private void Start()
    26.     {
    27.         Time.fixedDeltaTime = fixedTimeStep;
    28.         for (int i = 0; i < rollbackFrames.Length; ++i)
    29.         {
    30.             World w = new World($"rollback{i + i}");
    31.             rollbackFrames[i] = w.EntityManager;
    32.         }
    33.         SaveTickForRollback(World.Active);
    34.     }
    35.  
    36.     private void FixedUpdate()
    37.     {
    38.         if (autoExecute)
    39.         {
    40.             Tick();
    41.         }
    42.     }
    43.  
    44.     private void OnGUI()
    45.     {
    46.         GUILayout.BeginArea(new Rect(Screen.width - 200, Screen.height - 200, 180, 190));
    47.         GUILayout.BeginVertical();
    48.         GUILayout.BeginHorizontal();
    49.         GUILayout.Label($"{tick} {possibleRollbacks} {rollbackFrames.Length}");
    50.         GUILayout.EndHorizontal();
    51.  
    52.         GUILayout.BeginHorizontal();
    53.         autoExecute = GUILayout.Toggle(autoExecute, "autoRun");
    54.         if (GUILayout.Button("<"))
    55.         {
    56.             Rollback(1);
    57.         }
    58.         if (GUILayout.Button(">"))
    59.         {
    60.             Tick();
    61.         }
    62.         GUILayout.EndHorizontal();
    63.         GUILayout.EndVertical();
    64.         GUILayout.EndArea();
    65.     }
    66.  
    67.     private void Tick()
    68.     {
    69.         if (MatchManager.Instance.state != MatchManager.MatchState.InProgress)
    70.             return;
    71.         tick++;
    72.         if (NetworkStateManager.Instance.mode == NetworkStateManager.NetworkMode.Client)
    73.             GatherInput();
    74.         //Create all systems
    75.         World mainWorld = World.Active;
    76.         if (spawnerSystem == null)
    77.         {
    78.             spawnerSystem = mainWorld.GetOrCreateSystem<FixedRateSpawnerSystem>();
    79.         }
    80.         if (movementSystem == null)
    81.         {
    82.             movementSystem = mainWorld.GetOrCreateSystem<MoveProjectilesSystem>();
    83.         }
    84.  
    85.         //update all systems in order
    86.         spawnerSystem.Update();
    87.         movementSystem.Update();
    88.         SaveTickForRollback(mainWorld);
    89.     }
    90.  
    91.     private void SaveTickForRollback(World mainWorld)
    92.     {
    93.         rollbackFrames[worldToWriteOn].CopyAndReplaceEntitiesFrom(mainWorld.EntityManager);
    94.         MoveRollbackWorldIndexForward();
    95.         if (possibleRollbacks < rollbackFrames.Length - 1)
    96.             possibleRollbacks++;
    97.     }
    98.  
    99.     private void MoveRollbackWorldIndexForward()
    100.     {
    101.         worldToWriteOn++;
    102.         if (worldToWriteOn >= rollbackFrames.Length)
    103.             worldToWriteOn = 0;
    104.     }
    105.  
    106.     public void Rollback(int frameCount)
    107.     {
    108.        
    109.         if (frameCount > possibleRollbacks)
    110.             throw new System.InvalidOperationException($"Cannot rollback {frameCount} frames at tick {tick} with {possibleRollbacks} possible rollbacks");
    111.        
    112.         World.Active.EntityManager.CopyAndReplaceEntitiesFrom(GetRollbackItem(frameCount));
    113.         tick -= frameCount;
    114.         worldToWriteOn -= frameCount;
    115.         if (worldToWriteOn < 0)
    116.             worldToWriteOn = rollbackFrames.Length + worldToWriteOn;
    117.         possibleRollbacks -= frameCount;
    118.     }
    119.  
    120.     private EntityManager GetRollbackItem(int frameCount)
    121.     {
    122.         int frame = worldToWriteOn - frameCount;
    123.         if (frame < 0)
    124.             frame = rollbackFrames.Length + frame;
    125.         return rollbackFrames[frame];
    126.     }
    127.  
    128.     private void GatherInput()
    129.     {
    130.         CustomNetworkMessages.CommandsCollectionMessage msg = new CustomNetworkMessages.CommandsCollectionMessage();
    131.         if (Input.GetKeyDown(KeyCode.Space))
    132.         {
    133.             msg.commands.Add(new SpawnCommand
    134.             {
    135.                 tick = GetCommandExecutionTickForClient(),
    136.                 player = MatchManager.Instance.localPlayerIndex
    137.             });
    138.         }
    139.         Fix64 vertical = (Fix64)Input.GetAxis("Vertical");
    140.         Fix64 horizontal = (Fix64)Input.GetAxis("Horizontal");
    141.         if (vertical != (Fix64)0 || horizontal != (Fix64)0)
    142.         {
    143.             msg.commands.Add(new MoveCommand
    144.             {
    145.                 tick = GetCommandExecutionTickForClient(),
    146.                 movement = new FixedVector3 { x = horizontal, y = vertical },
    147.                 player = MatchManager.Instance.localPlayerIndex
    148.             });
    149.         }
    150.         if (msg.commands.Count > 0)
    151.         {
    152.             NetworkStateManager.Instance.SendMessageToServer(CustomNetworkMessages.CommandRequest, msg);
    153.         }
    154.     }
    155.  
    156.     private int GetCommandExecutionTickForClient()
    157.     {
    158.         return tick + commandsTickDelay;
    159.     }
    160. }
    Btw if it helps. I only have a single object component in my set of component and the rest of pure ECS components