Clean default world setup without top level groups InitializeSystemGroup and PresentationSystemGroup, with only SimulationSystemGroup included (plus sorting);
Other system groups should work as well, as long they’re part of SimulationSystemGroup;
Excluded subscene conversion;
Is there any example how to achieve that?
Main issue for me is that DefaultWorldInitialization.GetAllSystems(WorldSystemFilterFlags.All) for some reason gets systems that are included as tests, so not sure how to exclude them properly;
Edit: WorldSystemFilterFlags.Default returns correct systems for runtime, however I’m still not sure how to exclude by system groups;
Okay, got it by excluding actual types before creation.
Edit: Had to use UNITY_DISABLE_AUTOMATIC_SYSTEM_BOOTSTRAP in the end to exclude SubScenes completely, and some extra systems has to be modified as well;
Taken some of the methods from DefaultWorldInitialization. Also, made some of the Entities internal methods public. As hacky as is, it seems to be working:
using System;
using System.Collections.Generic;
using Unity.Entities;
using Unity.Transforms;
using UnityEngine;
namespace Bootstrap {
#if UNITY_DISABLE_AUTOMATIC_SYSTEM_BOOTSTRAP
// Mimic what AutomaticWorldBootstrap does, but without calling into SubScene / SceneCatalog loading
public static class AutomaticCustomWorldBootstrap {
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)]
private static void Initialize() { DefaultWorldInitialization.Initialize("Custom Default World"); }
}
#endif
/// <summary>
/// Custom bootstrap that excludes default system groups and SubScene linking / initialization /
/// other default groups except SimulationSystemGroup
/// </summary>
// ReSharper disable once UnusedType.Global -> Fetched by reflection from DefaultWorldInitialization
public class CustomBootstrap : ICustomBootstrap {
public bool Initialize(string defaultWorldName) {
World world = new World("Custom Default World");
World.DefaultGameObjectInjectionWorld = world;
var systems = DefaultWorldInitialization.GetAllSystems(WorldSystemFilterFlags.Default);
HashSet<Type> excludeGroups = CompileExcludeGroupTypes();
AddSystemToRootLevelSystemGroups(world, systems, excludeGroups);
ScriptBehaviourUpdateOrder.AddWorldToCurrentPlayerLoop(world);
return true;
}
private static HashSet<Type> CompileExcludeGroupTypes() {
return new HashSet<Type>
{
typeof(InitializationSystemGroup),
typeof(PresentationSystemGroup),
typeof(TransformSystemGroup),
typeof(FixedStepSimulationSystemGroup),
typeof(CompanionGameObjectUpdateTransformSystem),
typeof(CompanionGameObjectUpdateSystem),
};
}
private static void AddSystemToRootLevelSystemGroups(World world,
IEnumerable<Type> systemTypesOrig,
HashSet<Type> excludedTypes) {
SimulationSystemGroup simulationSystemGroup = world.GetOrCreateSystem<SimulationSystemGroup>();
List<Type> managedTypes = new List<Type>();
List<Type> unmanagedTypes = new List<Type>();
foreach (Type type in systemTypesOrig) {
if (excludedTypes.Contains(type)) continue;
if (typeof(ComponentSystemBase).IsAssignableFrom(type))
managedTypes.Add(type);
else if (typeof(ISystemBase).IsAssignableFrom(type))
unmanagedTypes.Add(type);
else
throw new InvalidOperationException("Bad type");
}
var systems = world.GetOrCreateSystemsAndLogException(managedTypes, managedTypes.Count);
// Add systems to their groups, based on the [UpdateInGroup] attribute.
foreach (ComponentSystemBase system in systems) {
if (system == null)
continue;
// Skip the built-in root-level system groups
var type = system.GetType();
if (type == typeof(InitializationSystemGroup)
|| type == typeof(SimulationSystemGroup)
|| type == typeof(PresentationSystemGroup)) {
continue;
}
Attribute[] updateInGroupAttributes = TypeManager.GetSystemAttributes(system.GetType(), typeof(UpdateInGroupAttribute));
if (updateInGroupAttributes.Length == 0) {
simulationSystemGroup.AddSystemToUpdateList(system);
}
foreach (Attribute attr in updateInGroupAttributes) {
ComponentSystemGroup group = FindGroup(world, type, attr, excludedTypes);
group?.AddSystemToUpdateList(system);
}
}
#if !UNITY_DOTSRUNTIME
// Add unmanaged systems
foreach (var type in unmanagedTypes) {
ProcessUnmanagedGroup(type, world, simulationSystemGroup, excludedTypes);
}
#endif
// Update player loop
simulationSystemGroup.SortSystems();
}
#if !UNITY_DOTSRUNTIME
private static void ProcessUnmanagedGroup(Type type,
World world,
ComponentSystemGroup simulationSystemGroup,
HashSet<Type> excludedTypes) {
SystemHandleUntyped sysHandle = world.Unmanaged.CreateUnmanagedSystem(world, type);
Attribute[] updateInGroupAttributes = TypeManager.GetSystemAttributes(type, typeof(UpdateInGroupAttribute));
// Add systems to their groups, based on the [UpdateInGroup] attribute.
if (updateInGroupAttributes.Length == 0) {
simulationSystemGroup.AddUnmanagedSystemToUpdateList(sysHandle);
}
foreach (var attr in updateInGroupAttributes) {
ComponentSystemGroup groupSys = FindGroup(world, type, attr, excludedTypes);
groupSys?.AddUnmanagedSystemToUpdateList(sysHandle);
}
}
#endif
private static ComponentSystemGroup FindGroup(World world,
Type systemType,
Attribute attr,
HashSet<Type> excludedTypes) {
UpdateInGroupAttribute uga = attr as UpdateInGroupAttribute;
if (uga == null) return null;
if (!TypeManager.IsSystemAGroup(uga.GroupType)) {
throw new InvalidOperationException($"Invalid [UpdateInGroup] attribute for {systemType}: {uga.GroupType} must be derived from ComponentSystemGroup.");
}
if (uga.OrderFirst && uga.OrderLast) {
throw new InvalidOperationException($"The system {systemType} can not specify both OrderFirst=true and OrderLast=true in its [UpdateInGroup] attribute.");
}
Type groupType = uga.GroupType;
ComponentSystemBase groupSys = world.GetExistingSystem(groupType);
if (groupSys == null && !excludedTypes.Contains(groupType)) {
// Warn against unexpected behaviour combining DisableAutoCreation and UpdateInGroup
bool parentDisableAutoCreation = TypeManager.GetSystemAttributes(uga.GroupType, typeof(DisableAutoCreationAttribute)).Length > 0;
Debug.LogWarning(parentDisableAutoCreation
? $"A system {systemType} wants to execute in {uga.GroupType} but this group has [DisableAutoCreation] and {systemType} does not. The system will not be added to any group and thus not update."
: $"A system {systemType} could not be added to group {uga.GroupType}, because the group was not created. Fix these errors before continuing. The system will not be added to any group and thus not update.");
}
return groupSys as ComponentSystemGroup;
}
}
}
If anyone has solution that doesn’t have hacking into Entities package involved - please share it.
Been a while since I set it up, but it’s something along the lines of setting UNITY_DISABLE_AUTOMATIC_SYSTEM_BOOTSTRAP at Player in ProjectConfigs like so:
Missed one more thing worth mentioning, ISystemBase registration with custom bootstrap or due to disabled automatic system bootstrap doesn’t work. Which is basically any unmanaged system won’t be added correctly.
E.g. attempting to add BurstCompatibleSystem (which is part of Entities) fails when burst is enabled:
I ran into this problem too, particularly trying to add in TRSToLocalToWorldSystem. Wish unity would of just not added these things, seems like a regression (ISystem) when the prior (JobSystem or SystemBase) would work fine.
Edit: I copied it, and made it use systembase. A good work around. I think they’re converting to the struct, possibly to make it burstable and thus faster. Yes… I think that’s their plan.
ISystem adding should work with Entities 0.51 as of right now, unless you’re adding system incorrectly;
Unmanaged systems are added separately during bootstrap phase.
Look into DefaultWorldInitialization to see how its done for the specific Entities version.
As an example, here’s my current custom bootstrap. Although beware that it does call into some internal methods that I’ve patched as public. Plus it strips out subscenes, and some groups & buffers that aren’t used. So don’t copy & paste it unless you don’t need them (also InitializationSystemGroup have WorldUpdateAllocatorResetSystem, make sure to add it somewhere else, otherwise there will be a nasty memory leak without it. Or just don’t strip InitializationSystemGroup).
using System;
using System.Collections.Generic;
using Unity.Collections;
using Unity.Entities;
using Unity.Transforms;
using UnityEngine;
// ReSharper disable once CheckNamespace -> Its okay
namespace Bootstrap {
#if UNITY_DISABLE_AUTOMATIC_SYSTEM_BOOTSTRAP
// Mimic what AutomaticWorldBootstrap does, but without calling into SubScene conversion
public static class AutomaticCustomWorldBootstrap {
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)]
private static void Initialize() { DefaultWorldInitialization.Initialize("Custom Default World"); }
}
#endif
/// <summary>
/// Custom bootstrap that excludes default system groups and SubScene linking / initialization /
/// other default groups except SimulationSystemGroup
/// </summary>
// ReSharper disable once UnusedType.Global -> Fetched by reflection from DefaultWorldInitialization
public class CustomBootstrap : ICustomBootstrap {
public bool Initialize(string defaultWorldName) {
World world = new World(defaultWorldName);
World.DefaultGameObjectInjectionWorld = world;
IReadOnlyList<Type> systems = DefaultWorldInitialization.GetAllSystems(WorldSystemFilterFlags.Default);
#if UNITY_EDITOR
CompileTestExcludes();
#endif
HashSet<Type> excludeGroups = CompileExcludeGroupTypes();
var simulationSystemGroup = world.GetOrCreateSystem<SimulationSystemGroup>();
AddSystemToRootLevelSystemGroupsInternal(world,
systems,
simulationSystemGroup,
new DefaultWorldInitialization.DefaultRootGroups(),
excludeGroups);
simulationSystemGroup.SortSystems();
ScriptBehaviourUpdateOrder.AppendWorldToCurrentPlayerLoop(world);
return true;
}
private static HashSet<Type> CompileExcludeGroupTypes() {
return new HashSet<Type>
{
typeof(InitializationSystemGroup),
/*typeof(PresentationSystemGroup),*/ // Required for LateUpdate logic to run in
typeof(BeginPresentationEntityCommandBufferSystem), // But not buffer
typeof(LateSimulationSystemGroup),
typeof(TransformSystemGroup),
typeof(FixedStepSimulationSystemGroup),
typeof(VariableRateSimulationSystemGroup),
/*typeof(CompanionGameObjectUpdateTransformSystem),
typeof(CompanionGameObjectUpdateSystem),*/
};
}
internal static void AddSystemToRootLevelSystemGroupsInternal<T>(World world,
IEnumerable<Type> systemTypesOrig,
ComponentSystemGroup defaultGroup,
T rootGroups,
HashSet<Type> excludedTypes)
where T : struct, DefaultWorldInitialization.IIdentifyRootGroups {
var managedTypes = new List<Type>();
var unmanagedTypes = new List<Type>();
foreach (Type type in systemTypesOrig) {
#if UNITY_EDITOR
if (ShouldSkipTestType(type)) continue;
#endif
if (excludedTypes.Contains(type)) continue;
if (typeof(ComponentSystemBase).IsAssignableFrom(type))
managedTypes.Add(type);
else if (typeof(ISystem).IsAssignableFrom(type))
unmanagedTypes.Add(type);
else
throw new InvalidOperationException("Bad type");
}
var systems = world.GetOrCreateSystemsAndLogException(managedTypes, managedTypes.Count);
// Add systems to their groups, based on the [UpdateInGroup] attribute.
foreach (ComponentSystemBase system in systems) {
if (system == null)
continue;
// Skip the built-in root-level system groups
Type type = system.GetType();
if (rootGroups.IsRootGroup(type)) {
continue;
}
Attribute[] updateInGroupAttributes = TypeManager.GetSystemAttributes(system.GetType(),
typeof(UpdateInGroupAttribute));
if (updateInGroupAttributes.Length == 0) {
defaultGroup.AddSystemToUpdateList(system);
}
foreach (Attribute attr in updateInGroupAttributes) {
ComponentSystemGroup group = FindGroup(world, type, attr, excludedTypes);
group?.AddSystemToUpdateList(system);
}
}
// Create unmanaged systems in batch
NativeArray<SystemHandleUntyped> handles = world.Unmanaged.GetOrCreateUnmanagedSystems(world, unmanagedTypes);
// Add systems to their groups, based on the [UpdateInGroup] attribute.
for (int i = 0; i < unmanagedTypes.Count; ++i) {
Type type = unmanagedTypes[i];
SystemHandleUntyped sysHandle = handles[i];
var updateInGroupAttributes = TypeManager.GetSystemAttributes(type, typeof(UpdateInGroupAttribute));
if (updateInGroupAttributes.Length == 0) {
defaultGroup.AddUnmanagedSystemToUpdateList(sysHandle);
}
foreach (Attribute attr in updateInGroupAttributes) {
ComponentSystemGroup groupSys = FindGroup(world, type, attr, excludedTypes);
groupSys?.AddUnmanagedSystemToUpdateList(sysHandle);
}
}
handles.Dispose();
}
private static ComponentSystemGroup FindGroup(World world,
Type systemType,
Attribute attr,
HashSet<Type> excludedTypes) {
UpdateInGroupAttribute uga = attr as UpdateInGroupAttribute;
if (uga == null) return null;
Type groupType = uga.GroupType;
if (!TypeManager.IsSystemAGroup(groupType)) {
throw new InvalidOperationException($"Invalid [UpdateInGroup] attribute for {systemType}: {uga.GroupType} must be derived from ComponentSystemGroup.");
}
if (uga.OrderFirst && uga.OrderLast) {
throw new InvalidOperationException($"The system {systemType} can not specify both OrderFirst=true and OrderLast=true in its [UpdateInGroup] attribute.");
}
var groupSys = world.GetExistingSystem(groupType);
if (groupSys == null && !excludedTypes.Contains(groupType)) {
// Warn against unexpected behaviour combining DisableAutoCreation and UpdateInGroup
var parentDisableAutoCreation = TypeManager.GetSystemAttributes(uga.GroupType, typeof(DisableAutoCreationAttribute)).Length > 0;
Debug.LogWarning(parentDisableAutoCreation
? $"A system {systemType} wants to execute in {uga.GroupType} but this group has [DisableAutoCreation] and {systemType} does not. The system will not be added to any group and thus not update."
: $"A system {systemType} could not be added to group {uga.GroupType}, because the group was not created. Fix these errors before continuing. The system will not be added to any group and thus not update.");
}
return groupSys as ComponentSystemGroup;
}
#if UNITY_EDITOR
private static readonly List<string> TestExcludes = new List<string>();
private void CompileTestExcludes() {
TestExcludes.Clear();
TestExcludes.Add("TransformTests");
TestExcludes.Add("TransformPerformanceTests");
}
/// <summary>
/// Checks whether this type is part of excluded test types
/// </summary>
private static bool ShouldSkipTestType(Type type) {
Debug.Assert(type != null);
string typeFullName = type.FullName;
Debug.Assert(typeFullName != null);
foreach (string typeName in TestExcludes) {
if (typeFullName.Contains(typeName)) return true;
}
return false;
}
#endif
}
}
Hi, so just confirming, you’re adding the ISystem structs with:
var systems = world.GetOrCreateSystemsAndLogException(managedTypes, managedTypes.Count);
A list of types. Does this have any performance benefits over SystemBase classes? I’m assuming they’ve bursted it perhaps?
Thank you for the info on BeforeSceneLoad, I’m just creating my systems during this event now (was monobehaviour before). Although I imagine for a game with multiple scenes, would BeforeSplashScreen be better for permanency?
ISystems iteration speed is better from my profiling / testing, even without burst. Plus, no heap allocs from system creation → faster system creation, since its a struct. Burst compilation is somewhat supported if used with specific jobs that can be scheduled with burst. Con’s that you can’t use them with .WithoutBurst / managed queries. Also, some native containers does not support scheduled disposes due to non-bursted safety system managed objects.
This does not make any difference performance wise, as method executed only once in both cases. Just timing of execution is different. Feel free to use whatever suits your needs.
Thank you for the comments!! I appreciate the information. I posted below roughly what I am doing now to control my bootstrap. I think I’ll convert more of my systems, the heavier ones, to ISystemBase’s in the future to boost performance where needed. It took me a while to release why memory was overflowing, which was caused by not running WorldUpdateAllocatorResetSystem (That wasn’t there a couple of years ago)… I spent alot of time digging through the entities package files looking at how it works in my pursuit.
public void CreateSystems()
{
Unity.Entities.World.DisposeAllWorlds();
UnityEngine.Debug.Log("Creating Zoxel ECS Systems.");
entitiesWorld = new Unity.Entities.World("Zoxel");
var initializationSystemGroup = entitiesWorld.GetOrCreateSystem<InitializationSystemGroup>();
initializationSystemGroup.AddSystemToUpdateList(entitiesWorld.GetOrCreateSystem<UpdateWorldTimeSystem>());
initializationSystemGroup.AddUnmanagedSystemToUpdateList(entitiesWorld.GetOrCreateUnmanagedSystem(typeof(WorldUpdateAllocatorResetSystem)));
.....
simulationSystemGroup.AddSystemToUpdateList(lateSimulationSystemGroup);
simulationSystemGroup.AddSystemToUpdateList(entitiesWorld.GetOrCreateSystem<EndSimulationEntityCommandBufferSystem>());
simulationSystemGroup.SortSystems();
World.DefaultGameObjectInjectionWorld = entitiesWorld;
ScriptBehaviourUpdateOrder.AppendWorldToCurrentPlayerLoop(entitiesWorld);
}
On an unrelated but kinda related note, should we be calling WithDisposeOnCompletion for ComponentDataEntity’s injected a job. I couldn’t see this NativeContainer used internally by unity. Previously I was doing so only for entities pushed in (with ToEntityArrayAsync).
Its ISystem now, ISystemBase is obsolete and will be removed in the future. But its basically the same thing pretty much.
That’s not required. CDFE is a struct with a pointer plus safety mechanisms. No memory allocation involved there. Also, its not IDisposable, so you can’t.
Thank you, was poking through the packages code and NativeArray seems to be in the closed source library so it was hard to tell. But yeah, we only need to dispose IDisposable I suppose, and it won’t work unless it uses that. Are there ways to use IDisposable to automatically dispose our IComponent’s upon deletion? That would save me the hassle of writing Dispose Systems!
[NativeContainer]
public unsafe struct ComponentDataFromEntity where T : struct, IComponentData
Edit: Also noob question, how did you patch out the methods from the entities package? Is there good way to do patches on them? (automatically with updates) that would be nice. I could write a bash script to do so, but does this mean you just copy and paste the entities package into your project and remove the package manager one? There’s alot I want to strip out of the entities package anyway so I can just use it barebones without any of the other unity things.
Edit2: I’ve written in a custom automatic bootstrap, posting here for others to use. Uses namespace fetching instead to grab systems you want. Removed the fluff from unitys class.
#if UNITY_DISABLE_AUTOMATIC_SYSTEM_BOOTSTRAP && ZOXEL_AUTO_BOOTSTRAP
using System;
using System.Collections.Generic;
using System.Reflection;
using Unity.Collections;
using Unity.Entities;
using Unity.Transforms;
using UnityEngine;
namespace Zoxel.Realms.Bootstrap
{
//! Automatically spawns systems and groups.
public class AutoBootstrap
{
private static AutoBootstrap instance;
private Unity.Entities.World world;
// or BeforeSplashScreen
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)]
private static void Initialize()
{
if (instance == null)
{
instance = new AutoBootstrap();
}
Unity.Entities.World.DisposeAllWorlds();
instance.CreateWorld();
}
//! Create a entities world and ccall spawn systems.
public void CreateWorld()
{
world = new World("Zoxel_Auto");
World.DefaultGameObjectInjectionWorld = world;
SpawnSystems();
ScriptBehaviourUpdateOrder.AppendWorldToCurrentPlayerLoop(world);
}
//! Spawns our systems using namespace fetches.
private void SpawnSystems()
{
// find system groups and systems
var systemGroupTypes = GetNamespaceTypes("Zoxel", typeof(ComponentSystemGroup));
var systemTypes = GetNamespaceTypes("Zoxel", typeof(SystemBase));
// spawn system groups
var initializationSystemGroup = world.GetOrCreateSystem<InitializationSystemGroup>();
var simulationSystemGroup = world.GetOrCreateSystem<SimulationSystemGroup>();
var lateSimulationSystemGroup = world.GetOrCreateSystem<LateSimulationSystemGroup>();
var presentationSystemGroup = world.GetOrCreateSystem<PresentationSystemGroup>();
// spawn systems
initializationSystemGroup.AddSystemToUpdateList(world.GetOrCreateSystem<UpdateWorldTimeSystem>());
initializationSystemGroup.AddUnmanagedSystemToUpdateList(world.GetOrCreateUnmanagedSystem(typeof(WorldUpdateAllocatorResetSystem)));
simulationSystemGroup.AddSystemToUpdateList(world.GetOrCreateSystem<EndSimulationEntityCommandBufferSystem>());
simulationSystemGroup.AddSystemToUpdateList(lateSimulationSystemGroup);
var systemGroups = world.GetOrCreateSystemsAndLogException(systemGroupTypes.ToArray());
AddSystemsToParent(world, simulationSystemGroup, systemGroups);
var systems = world.GetOrCreateSystemsAndLogException(systemTypes.ToArray());
AddSystemsToParent(world, simulationSystemGroup, systems);
simulationSystemGroup.SortSystems();
}
//! Adds a bunch of systems to their proper parents.
private void AddSystemsToParent(World world, in ComponentSystemGroup defaultGroup, in ComponentSystemBase[] systems)
{
// Add systems to their groups, based on the [UpdateInGroup] attribute.
foreach (var system in systems)
{
if (system == null)
{
continue;
}
var updateInGroupAttributes = TypeManager.GetSystemAttributes(system.GetType(), typeof(UpdateInGroupAttribute));
if (updateInGroupAttributes.Length == 0)
{
defaultGroup.AddSystemToUpdateList(system);
}
var type = system.GetType();
foreach (var attr in updateInGroupAttributes)
{
var group = FindGroup(world, type, attr);
group?.AddSystemToUpdateList(system);
}
}
}
//! Uses System.Reflection to find types.
private List<Type> GetNamespaceTypes(string desiredNamespace, Type targetBaseType)
{
var allowedTypes = new List<Type>();
foreach(var assembly in AppDomain.CurrentDomain.GetAssemblies())
{
foreach(var type in assembly.GetTypes())
{
if (type.Namespace != null && type.Namespace.Contains(desiredNamespace) && IsSubclassOfType(type, targetBaseType))
{
// UnityEngine.Debug.LogError("Allowed System in [" + desiredNamespace + "]: " + type.ToString());
allowedTypes.Add(type);
}
}
}
return allowedTypes;
}
//! Uses Recurseion to check if type inherits another type.
private static bool IsSubclassOfType(Type myType, Type targetBaseType)
{
while (myType != null && myType != typeof(object))
{
if (myType == targetBaseType)
{
return true;
}
myType = myType.BaseType;
}
return false;
}
//! Finds the parent system groups, using the attributes on the systems.
private static ComponentSystemGroup FindGroup(World world, Type systemType, Attribute attr)
{
var uga = attr as UpdateInGroupAttribute;
if (uga == null)
return null;
var groupType = uga.GroupType;
if (!TypeManager.IsSystemAGroup(groupType))
{
throw new InvalidOperationException($"Invalid [UpdateInGroup] attribute for {systemType}: {uga.GroupType} must be derived from ComponentSystemGroup.");
}
if (uga.OrderFirst && uga.OrderLast)
{
throw new InvalidOperationException($"The system {systemType} can not specify both OrderFirst=true and OrderLast=true in its [UpdateInGroup] attribute.");
}
var groupSys = world.GetExistingSystem(groupType);
if (groupSys == null) // && !excludedTypes.Contains(groupType))
{
// Warn against unexpected behaviour combining DisableAutoCreation and UpdateInGroup
var parentDisableAutoCreation = TypeManager.GetSystemAttributes(uga.GroupType, typeof(DisableAutoCreationAttribute)).Length > 0;
Debug.LogWarning(parentDisableAutoCreation
? $"A system {systemType} wants to execute in {uga.GroupType} but this group has [DisableAutoCreation] and {systemType} does not. The system will not be added to any group and thus not update."
: $"A system {systemType} could not be added to group {uga.GroupType}, because the group was not created. Fix these errors before continuing. The system will not be added to any group and thus not update.");
}
return groupSys as ComponentSystemGroup;
}
}
}
#endif
Don’t need to dispose them. CDFE is not designed to be disposed, and you don’t need to.
Other native containers (such as NativeArray) can be disposed on job completion by passing job handle to the .Dispose(jobHandle) method. E.g. that handle could be system’s Dependency, if used after .ForEach, or any other JobHandle.
Not really. There’s assembly definition references if you want to add something e.g. by writing extra methods via partial classes / files;
Personally, I just copy & paste package from Library/PackageCache to /Packages folder, then manually update & expose whatever needed. Logic will break upon package update / version change, but at least you’d would see what have changed in the package, and would be able to fix it.
Thank you, that’s good information about the Packages folder. I’m assuming any folder will work, but making a packages folder sounds ideal. I may do this in the future with a bash script to make edits (making functions public for example). Then again these packages are rarely updated anymore lol. Also I assume that Dispose(*jobHandle) funnction does the same as Entities WithDisposeOnCompletion function. I don’t have any custom jobs functions, just systems logic, so I probably don’t need that atm, but its good to know that function exists.