Search Unity

  1. Unity 2019.1 beta is now available.
    Dismiss Notice
  2. The Unity Pro & Visual Studio Professional Bundle gives you the tools you need to develop faster & collaborate more efficiently. Learn more.
    Dismiss Notice
  3. We're looking for insight from anyone who has experience with game testing to help us better Unity. Take our survey here. If chosen to participate you'll be entered into a sweepstake to win an Amazon gift card.
    Dismiss Notice
  4. On February 28th the Feedback website will shut down and be redirected to the Unity forums. See the full post for more information.
    Dismiss Notice
  5. Want to provide direct feedback to the Unity team? Join the Unity Advisory Panel.
    Dismiss Notice
  6. Unity 2018.3 is now released.
    Dismiss Notice
  7. Improve your Unity skills with a certified instructor in a private, interactive classroom. Watch the overview now.
    Dismiss Notice

Simulation and Rendering in Different Worlds

Discussion in 'Entity Component System and C# Job system' started by PublicEnumE, Feb 10, 2019.

  1. PublicEnumE

    PublicEnumE

    Joined:
    Feb 3, 2019
    Posts:
    15
    At the last Unite, @Joachim_Ante talked about one potential use of being able to create multiple Worlds: running your game's simulation and rendering in different worlds, at different frame-rates.

    Since then, the team has released support for multiple Worlds. But I haven't be able to find much info about controlling the rendering pipeline, or running these worlds at different frame-rates.

    I would love to find out more information about how this should be done. Can anyone point to information or examples out there that I may be missing? Many thanks!
     
    learc83 and MostHated like this.
  2. PublicEnumE

    PublicEnumE

    Joined:
    Feb 3, 2019
    Posts:
    15
    I would be grateful to anyone with info on this topic. :)
     
    Last edited: Feb 11, 2019
  3. MostHated

    MostHated

    Joined:
    Nov 29, 2015
    Posts:
    643
    I am with you there. The streaming aspect of the Mega City demo (which utilizes what you are asking about) is something I can't wait to see some more info on.
     
  4. tertle

    tertle

    Joined:
    Jan 25, 2011
    Posts:
    1,059
    They already released the scene conversion and the world methods you need to stream them in the most recent version of ecs; it's wonderful. It's not the same as using a separate world for simulation though.

    To do scene conversion just call

    GameObjectConversionUtility.ConvertScene(Scene scene, World dstEntityWorld);


    That's really all there is to it. It's not really a 1 click solution yet, but doesn't take a lot to get working. My specific scene conversion looks like this. It also grabs the baked NavMesh and I'll probably add baked lightmaps at some point when I need it.

    Code (CSharp):
    1. // <copyright file="SceneConversion.cs" company="BovineLabs">
    2. //     Copyright (c) BovineLabs. All rights reserved.
    3. // </copyright>
    4. #if UNITY_EDITOR
    5.  
    6. namespace BovineLabs.Common.Utility
    7. {
    8.     using System.IO;
    9.     using Unity.Entities;
    10.     using Unity.Entities.Serialization;
    11.     using UnityEditor;
    12.     using UnityEngine;
    13.     using UnityEngine.AI;
    14.     using UnityEngine.Assertions;
    15.     using UnityEngine.SceneManagement;
    16.  
    17.     /// <summary>
    18.     /// Tools for converting a scene to ECS representation.
    19.     /// </summary>
    20.     public static class SceneConversion
    21.     {
    22.         private const string ParentFolder = "Assets/Prefabs";
    23.         private const string Folder = "Scenes";
    24.  
    25.         /// <summary>
    26.         /// Get or Create a new scene directory.
    27.         /// </summary>
    28.         /// <param name="name">The name of the scene.</param>
    29.         /// <returns>The directory path.</returns>
    30.         public static string GetOrCreateDirectory(string name)
    31.         {
    32.             var directory = Path.Combine(ParentFolder, Folder);
    33.  
    34.             if (!AssetDatabase.IsValidFolder(directory))
    35.             {
    36.                 AssetDatabase.CreateFolder(ParentFolder, Folder);
    37.             }
    38.  
    39.             var sceneDirectory = Path.Combine(directory, name);
    40.  
    41.             if (!AssetDatabase.IsValidFolder(sceneDirectory))
    42.             {
    43.                 AssetDatabase.CreateFolder(directory, name);
    44.             }
    45.  
    46.             return sceneDirectory;
    47.         }
    48.  
    49.         /// <summary>
    50.         /// Convert a scene to ECS representation.
    51.         /// </summary>
    52.         /// <returns>The converted scene.</returns>
    53.         public static ConvertedScene Convert()
    54.         {
    55.             var scene = SceneManager.GetActiveScene();
    56.  
    57.             var directory = Path.Combine(ParentFolder, Folder);
    58.             var savePath = Path.Combine(directory, $"{scene.name}.asset");
    59.             var dataDirectory = GetOrCreateDirectory(scene.name);
    60.  
    61.             ConvertScene(scene, dataDirectory, out var sharedComponentPrefab, out var serializedData);
    62.             var navMeshData = GetNavMeshData(scene);
    63.  
    64.             var save = AssetDatabase.LoadAssetAtPath<ConvertedScene>(savePath);
    65.  
    66.             if (save == null)
    67.             {
    68.                 save = ScriptableObject.CreateInstance<ConvertedScene>();
    69.                 save.SetData(serializedData, sharedComponentPrefab, navMeshData);
    70.                 AssetDatabase.CreateAsset(save, savePath);
    71.             }
    72.             else
    73.             {
    74.                 save.SetData(serializedData, sharedComponentPrefab, navMeshData);
    75.                 EditorUtility.SetDirty(save);
    76.                 AssetDatabase.SaveAssets();
    77.             }
    78.  
    79.             return save;
    80.         }
    81.  
    82.         private static void ConvertScene(Scene scene, string dataDirectory, out GameObject sharedComponentPrefab, out TextAsset serializedData)
    83.         {
    84.             var world = new World("world");
    85.             GameObjectConversionUtility.ConvertScene(scene, world);
    86.  
    87.             var entityManager = world.GetOrCreateManager<EntityManager>();
    88.  
    89.             var writerPath = Path.Combine(Path.Combine(dataDirectory, $"{scene.name}.bytes"));
    90.             var prefabPath = Path.Combine(dataDirectory, $"{scene.name}_SharedComponents.prefab");
    91.  
    92.             sharedComponentPrefab = null;
    93.  
    94.             using (Unity.Entities.Serialization.BinaryWriter writer = new StreamBinaryWriter(writerPath))
    95.             {
    96.                 SerializeUtilityHybrid.Serialize(entityManager, writer, out var sharedComponents);
    97.  
    98.                 if (sharedComponents != null)
    99.                 {
    100.                     sharedComponentPrefab =
    101.                         PrefabUtility.SaveAsPrefabAsset(sharedComponents, prefabPath, out var success);
    102.                     Assert.IsTrue(success);
    103.                     Object.DestroyImmediate(sharedComponents);
    104.                 }
    105.             }
    106.  
    107.             AssetDatabase.Refresh();
    108.             serializedData = AssetDatabase.LoadAssetAtPath<TextAsset>(writerPath);
    109.  
    110.             Assert.IsNotNull(serializedData);
    111.  
    112.             world.Dispose();
    113.             WordStorage.Instance.Dispose();
    114.             WordStorage.Instance = null;
    115.         }
    116.  
    117.         private static NavMeshData GetNavMeshData(Scene scene)
    118.         {
    119.             var scenePath = Path.GetDirectoryName(scene.path);
    120.             Assert.IsNotNull(scenePath);
    121.  
    122.             var path = Path.Combine(scenePath, scene.name);
    123.             path = Path.Combine(path, "NavMesh.asset");
    124.             var navMeshData = AssetDatabase.LoadAssetAtPath<NavMeshData>(path);
    125.             return navMeshData;
    126.         }
    127.     }
    128. }
    129. #endif
     
    The5 and MostHated like this.
  5. MostHated

    MostHated

    Joined:
    Nov 29, 2015
    Posts:
    643
    Oh! Nice! I can't wait to check more into this tomorrow. : D
     
  6. tertle

    tertle

    Joined:
    Jan 25, 2011
    Posts:
    1,059
    You can also add custom conversions by implementing your own GameObjectConversionSystem

    For example, if I want to convert UnityEngine.AI.NavMeshLink to an ECS version I created

    Code (CSharp):
    1. // <copyright file="NavMeshLinkConversion.cs" company="BovineLabs">
    2. //     Copyright (c) BovineLabs. All rights reserved.
    3. // </copyright>
    4.  
    5. namespace BovineLabs.Systems.Pathfinding
    6. {
    7.     using UnityEngine;
    8.  
    9.     /// <summary>
    10.     /// Converts UnityEngine.AI.NavMeshLink to ECS presentation.
    11.     /// </summary>
    12.     public class NavMeshLinkConversion : GameObjectConversionSystem
    13.     {
    14.         /// <inheritdoc />
    15.         protected override void OnUpdate()
    16.         {
    17.             this.ForEach((UnityEngine.AI.NavMeshLink navMeshLink) =>
    18.             {
    19.                 var entity = this.GetPrimaryEntity(navMeshLink);
    20.  
    21.                 var data = new NavMeshLink
    22.                 {
    23.                     AgentTypeID = navMeshLink.agentTypeID,
    24.                     StartPosition = navMeshLink.startPoint,
    25.                     EndPosition = navMeshLink.endPoint,
    26.                     CostModifier = navMeshLink.costModifier,
    27.                     Width = navMeshLink.width,
    28.                     Bidirectional = navMeshLink.bidirectional,
    29.                     Area = navMeshLink.area,
    30.                 };
    31.  
    32.                 this.DstEntityManager.AddComponentData(entity, data);
    33.             });
    34.         }
    35.     }
    36. }
     
    The5 and MostHated like this.
  7. MostHated

    MostHated

    Joined:
    Nov 29, 2015
    Posts:
    643
    Nice, that is some good stuff.
     
  8. tertle

    tertle

    Joined:
    Jan 25, 2011
    Posts:
    1,059
    Should add, all you need to do to load is specify the entity manager you want to load the entities into.

    Code (CSharp):
    1.         public void Deserialize(EntityManager entityManager)
    2.         {
    3.             using (BinaryReader reader = new TextAssetReader(this.world))
    4.             {
    5.                 SerializeUtilityHybrid.Deserialize(entityManager, reader, this.sharedComponentData);
    6.             }
    7.         }
     
    MostHated likes this.
  9. learc83

    learc83

    Joined:
    Feb 28, 2018
    Posts:
    16
    I would also love to see an example of the 2 worlds syasys. At a high level, what is the mechanism for passing data between worlds?

    In the simplest case, say I have some spheres that only have positions and rendermeshes, and all they do is move horizontally.

    I update their positions in the simulation world...what next?
     
    MostHated and PublicEnumE like this.
  10. PublicEnumE

    PublicEnumE

    Joined:
    Feb 3, 2019
    Posts:
    15
    I would be interested to know if rendering can be delayed in one world, until a second world says it's allowed to proceed.

    Another way to say that: I want to run my simulation loop a variable number of times each frame. And for the game to only render the results when the sim loop has decided it's done making changes.

    That might sound like a strange thing to want, but it could be helpful in at least one case I'm looking at (though unfortunately I can't give details).

    But regardless, I would just love to hear more about this feature, even if they don't have what I want. Since the knowledge doesn't seem to be widespread in the community, and since no Unity devs have commented here, it may be that we could hear more next month. :)
     
  11. Hans-D

    Hans-D

    Joined:
    Sep 24, 2013
    Posts:
    32
    I'm been dabbling a bit on the two world (Sim/Pres) concept, and the biggest limitation I've found is synchronization between them. The only way I could find to do so is through what other networking approaches out there are doing (except without the serialization part).

    The basic gist is that you need a way to quickly copy component/entity data (preferably in a generic way, and preferably only things that have changed) from one world to the other, and you need to do it at a specific synchronization point where both worlds are "still" since otherwise there might be race conditions between the worlds (if you are using jobs on any of them). In my case, I have a simulation world that operates at 20Hz in a semi-asynchronous manner (most things are done in jobs that are not limited to one frame, so the sim tick's workload is distributed across many frames), and whenever a simulation tick finishes I need to synchronize data into the presentation world, which is updated along the actual framerate.

    For example, my physics happens on a sim tick and calculates new positions for things that are moving. On the presentation world (which is hybrid ECS with game objects) the position of things changes each frame based on interpolation between the last sim tick's results and the previous position. I copy the position from the sim world into the presentation world when the sim tick finishes. The presentation world sends queued up commands to the sim. It's very similar to an authoritative networked game, except instead of sending serialized updates you could memory copy directly between worlds. However, I haven't found an easy and fast way to do it yet. Closest I got was using generic methods created through reflection that added/removed entities and copied components from one world to the other having a reference to both.
     
    e199 likes this.
  12. jdtec

    jdtec

    Joined:
    Oct 25, 2017
    Posts:
    18
    I'm doing something very similar to Hans-D. I have a Sim and View world. The Sim world creates output entities that get copied into and processed by the View world.

    I haven't hit sync issues or anything like that yet but I'm carefully controlling quite a few things atm such as the update order of systems.

    I'm also waiting on cleaner & more efficient ways to pass data between worlds.