Upgrading from Entities 0.51 to 1.0
To upgrade from Entities 0.51 to 1.0, you need to do the following:
- Update ISystem
- Update Transforms in your project
- Update conversions in your project
- Remove GenerateAuthoringComponent
- Update code that gets and creates a world's systems
- Remove Entities.ForEach in ISystem-based systems
- Update IJobChunk to handle enableable components
- Convert IJobEntityBatch and IJobEntityBatchWithIndex to IJobChunk
- Rename asynchronous EntityQuery gather-scatter methods
- Convert IJobEntityBatch and IJobEntityBatchWithIndex to IJobChunk
- Update system state components to cleanup components
- Update TypeManager code to use TypeIndex instead of int
- Update EntityCommandBufferSystem that have direct access
- Change system update code
- Rename EntityQueryDescBuilder
- Update SystemBase.Time and SystemState.Time
- Update SystemBase helper methods to SystemAPI
- Add the Entities Graphics package to your project
- Modify blob assets that use new or default
- Update partials in your project
- Update SceneSystem
Update ISystem
ISystem now uses C#'s default implemented methods, so you don't have to implement every function:
// Before
partial struct MySystem : ISystem {
public void OnCreate(ref SystemState state){}
public void OnUpdate(ref SystemState state){
// code goes here...
}
public void OnDestroy(ref SystemState state){}
}
// After
partial struct MySystem : ISystem {
public void OnUpdate(ref SystemState state){
// code goes here...
}
}
You now no longer need to put [BurstCompile]
on the struct of an ISystem
, but it's still needed on OnCreate
, OnStartRunning
, OnUpdate
, OnStopRunning
and OnDestroy
. For example:
// Before
[BurstCompile]
partial struct MySystem : ISystem {
[BurstCompile]
public void OnUpdate(ref SystemState state){
// code goes here...
}
}
// After
partial struct MySystem : ISystem {
[BurstCompile]
public void OnUpdate(ref SystemState state){
// code goes here...
}
}
Update Transforms in your project
The way that Transforms work in Entities 1.0 has changed. This section contains information on how to upgrade your project to work with the new Transforms. For further information on how Transforms work in Entities, see the Transforms in Entities documentation.
Note: The Transform system is under active development and subject to change up until the 1.0 release.
LocalTransform
The three components Translation
, Rotation
, and Scale
have been combined into one component, named LocalTransform
.
public struct LocalTransform : IComponentData
{
public float3 Position;
public float Scale;
public quaternion Rotation;
}
The following is an example of how to convert your code to use the LocalTransform
component:
// BEFORE
void Execute(ref Translation translation)
{
translation.Value += math.up();
}
// AFTER
void Execute(ref LocalTransform transform)
{
transform.Position += math.up();
}
Other transform components (CompositeRotation
, RotationPivotTranslation
, RotationPivot
, PostRotation
, CompositeRotation
, RotationEulerXYZ
(etc), PostRotationEulerXYZ
(etc), NonUniformScale
, ScalePivot
, ScalePivotTranslation
, CompositeScale
, ParentScaleInverse
) have been removed.
Scale
LocalTransform
has a single uniform scale property. The NonUniformScale
component has been removed. To non-uniformly scale the geometry, use a PostTransformMatrix
. For example:
EntityManager.AddComponent(myEntity, new PostTransformMatrix { Value = float4x4.Scale(1, 2, 3) });
Relativity
The LocalTransform
component is relative to its parent. If there is no Parent
component, LocalTransform
is relative to the origin of the world.
Initialization
The following is an example of how to initialize the LocalTransform
component:
// BEFORE
var t = new Translation { Value = new float3(1, 2, 3) };
var r = new Rotation { Value = quaternion.RotateZ(1) };
var s = new Scale { Value = 2 }
// AFTER
var transform = LocalTransform.FromPositionRotationScale(new float3(1, 2, 3), quaternion.RotateZ(1), 2);
To see the full list of initializer variations available, see the API documentation for LocalTransform
. All initializers begin with From
.
You must initialize all transforms. The C# default behavior is to initialize a struct with all zeroes, and that's an invalid transform. Where necessary, use LocalTransform.Identity
as a default value, like so:
var t = LocalTransform.Identity;
Changing an individual transform property
You may set the Position, Rotation, and Scale components of LocalTransform
directly. The LocalTransform
API also includes helper functions to create a new transform with one or more properties changed. For example, to create a new transform with a new position, leaving rotation and scale at their original value, you could:
SomeFunction(myTransform.WithPosition(1, 2, 3));
To see the full list of modifier variations available, see the API documentation for LocalTransform
.
Update conversions in your project
The previous conversion system has been replaced with Baking. For more information, see the documentation on Baking
Bakers are close to IConvertGameObjectToEntity
and are where you directly interface with the Unity objects, such as your authoring component. Bakers are also where dependencies are implicitly or explicitly captured, and all components added are capable of automatically being reverted if a Baker re-runs. A Baker can only add components to the primary entity that it's baking and the additional entities created by itself.
Basic Baker
This example shows how to change the usage of IConvertGameObjectToEntity
to a Baker:
public struct MyComponent : IComponentData
{
public float Value;
}
// BEFORE
public class MyMonoBehaviour : MonoBehaviour, IConvertGameObjectToEntity
{
public float value;
public void Convert(Entity entity, EntityManager dstManager,
GameObjectConversionSystem conversionSystem)
{
dstManager.AddComponentData(entity, new MyComponent {Value = value});
}
}
// AFTER
public class MyMonoBehaviour : MonoBehaviour
{
public float value;
}
public class MyBaker : Baker<MyMonoBehaviour>
{
public override void Bake(MyMonoBehaviour authoring)
{
AddComponent(new MyComponent {Value = authoring.value} );
}
}
The code inside the Baker is almost the same as the one in Convert with the following exceptions:
- You access the data via the authoring parameter.
- You don’t need to specify the
EntityManager
. - Because you're adding a component to the primary entity, you don’t need to specify it.
A Baker can also be inside a MyMonoBehaviour
class or in a completely separate file.
Prefabs in Bakers
Previously, to declare and convert Prefabs you had to implement the IDeclareReferencedPrefabs
interface. Now you just need to call GetEntity
in the Baker:
public struct MyComponent : IComponentData
{
public Entity Prefab;
}
public class MyMonoBehaviour : MonoBehaviour
{
public GameObject prefab;
}
public class MyBaker : Baker<MyMonoBehaviour>
{
public override void Bake(MyMonoBehaviour authoring)
{
AddComponent(new MyComponent { Prefab = GetEntity(authoring.prefab) } );
}
}
Runtime conversion
Runtime Conversion will be fully removed for 1.0 and the only way to use Baking is through Sub Scenes. This means that ConvertToEntity
and GameObjectConversionUtility.ConvertGameObjectHierarchy
don’t work with Baking.
Remove GenerateAuthoringComponent
GenerateAuthoringComponent
has been removed and you need to use a regular MonoBehaviour and Baker instead.
To migrate without losing the data in the scenes, you need to write the MonoBehaviours before upgrading to 1.0. These are the steps to preserve the data. Make sure the scripts aren't compiled until you complete step 2:
- Remove
[GenerateAuthoringComponent]
from your component.//[GenerateAuthoringComponent] public struct MyComponent : IComponentData { public float Value; }
- In the same file where you got your
IComponentData
, write yourMonoBehaviour
. This can be done manually or by copy and pasting the generated code.//[GenerateAuthoringComponent] public struct MyComponent : IComponentData { public float Value; } public class MyComponentAuthoring : MonoBehaviour { public float Value; }
- At this point, errors and warnings appear in the Unity Editor. Ignore them and carry on to step 4. The errors look like:
'MyComponent' is missing the class attribute 'ExtensionOfNativeClass'! GameObject (named 'GameObject') references runtime script in scene file. Fixing!
- In the Unity Editor, rename the file to match the name of your new
MonoBehaviour
. This preserves the information in the .meta file. In the case of the sample, the file needs to be renamed toMyComponentAuthoring.cs
. The previous errors should be gone now and your data should have been preserved. - After upgrading to 1.0, you need to write the Baker for that new
MonoBehaviour
. The final file in the example will look like this (MyComponentAuthoring.cs
):public struct MyComponent : IComponentData { public float Value; } public class MyComponentAuthoring : MonoBehaviour { public float Value; } public class MyComponentAuthoringBaker : Baker<MyComponentAuthoring> { public override void Bake(MyComponentAuthoring authoring) { AddComponent(new MyComponent { Value = authoring.Value }); } }
Update code that gets and creates a world's systems
Previously, World methods for accessing particular systems would return a direct reference to the system instance. This includes GetExistingSystem
, GetOrCreateSystem
, and CreateSystem
.
They now return a SystemHandle
rather than a direct reference.
If you have code that accesses system data directly, you should move system-associated data into a component. These components can exist in either a singleton entity, or they can belong to a system-associated entity through EntityManager.GetComponentData<T>(SystemHandle)
and similar methods. The latter is recommended when data lifetime should be tied to the system lifetime.
This change enables Burst compiled ISystem
systems to do all the things that SystemBase
systems that aren't Burst compiled can do.
To access managed system instances directly, replace the following calls:
GetExistingSystem
toGetExistingSystemManaged
GetOrCreateSystem
toGetOrCreateSystemManaged
CreateSystem
toCreateSystemManaged
Remove Entities.ForEach in ISystem-based systems
Entities.ForEach
is deprecated in ISystem-based systems. There are now two APIs you can use to iterate over entities: IJobEntity
and Query<T>
.
The IJobEntity
job interface was introduced in 0.50. For information on how to use this interface, see the documentation on Iterating over data with IJobEntity.
To iterate directly over an enumerable that gives access to component data or aspects with Query<T>
you can use an idiomatic C# foreach statement:
foreach (var (myAspect, myWriteData, myReadData) in Query<MyAspect,RefRW<WriteComponent>, RefRO<ReadComponent>>())
{
// Do stuff;
}
The Query method is part of the SystemAPI
helper class. Use a static statement at the top of the source file to implicitly reference this:
using static Unity.Entities.SystemAPI;
Limitations
- Code in the
foreach
body always runs on the main thread (untilforeach
is supported insideIJobEntity
). - You must use the
RefRO
andRefRW
generic types to iterate through components.RefRO
indicates read-only access andRefRW
indicates read-write. - You can iterate up to 8 aspects or components in the same
foreach
statement. - You can't query dynamic buffers in a
foreach
statement. - You can't save a query in a variable, field, or property and then reuse it multiple times.
- You can only perform a
foreach
iteration over aQuery<T>()
inside a method that has aref SystemState
parameter.
Update IJobChunk to handle enableable components
IJobChunk now supports enableable components. When an IJobChunk
implementation iterates over the entities in a chunk, you must now identify and skip over individual entities with disabled components that cause them not to match the job’s EntityQuery
. As such, you must update all existing IJobChunk
implementations to handle enableable components.
The parameters passed to IJobChunk.Execute()
have changed:
- The
chunkIndex
parameter is now calledunfilteredChunkIndex
, to emphasize that it's the index of the chunk within the list of all chunks that match the job’s query. - A new
chunkEnabledMask
parameter is now provided, which contains a 128-bit mask that describes which of the chunk’s entities match the job’s query, and should be processed. - A new
useEnabledMask
parameter is now provided, which indicates whether thechunkEnabledMask
parameter contains valid data. - The
firstEntityIndex
parameter is removed. This value was expensive to calculate, and mostIJobChunk
implementations don't use this parameter. If you have any jobs that use this value as a sort key when recording anEntityCommandBuffer
, you should useunfilteredChunkIndex
instead. If you need this value for example, to read or write per-entity data to or from a tightly packed array, see the upgrade information below.
For more information on these parameters, see the IJobChunk.Execute()
API documentation.
Required updates
To upgrade an existing IJobChunk implementation:
- Change the signature of the
Execute()
method:// Old signature void Execute(ArchetypeChunk chunk, int chunkIndex, int firstEntityIndex) // New signature void Execute(in ArchetypeChunk chunk, int unfilteredChunkIndex, bool useEnabledMask, in v128 chunkEnabledMask)
- If the job contains a loop over the chunk’s entities, update the loop to skip entities that don’t match the job’s query. The new
ChunkEntityEnumerator
helper encapsulates all the necessary logic:// Old loop for(int i=0, chunkEntityCount=chunk.Count; i < chunkEntityCount; ++i) { } // New loop var enumerator = new ChunkEntityEnumerator(useEnabledMask, chunkEnabledMask, chunk.Count); while(enumerator.NextEntityIndex(out int i)) { }
- The overhead of
ChunkEntityEnumerator
is as low as possible, so you can leave in the original for loop in cases where you are certain that theEntityQuery
used to schedule the job doesn't contain any enableable components. However, it's best practice to add an assert that fires if this precondition ever changes:// Loop where enableable components are not needed Assert.IsFalse(useEnabledMask); // This job is not written to handle enableable components for(int i=0, chunkEntityCount=chunk.Count; i < chunkEntityCount; ++i) { }
- At all call sites where the job is scheduled, change
job.Schedule(query,dependsOn)
tojob.ScheduleParallel(query, dependsOn)
. Without this change, the job compiles and runs, but its execution happens on a single worker thread instead of “going wide” across multiple worker threads in parallel. Important: ThedependsOn
parameter is no longer optional: most jobs should already have an explicit input dependency, but if they don't, passdefault
.
Jobs that need firstEntityIndex
By default, IJobChunk
no longer passes the firstEntityIndex
parameter to its Execute()
method. This is because most IJobChunk
operations don't use this parameter, and it requires a prefix sum operation to compute which isn't performant. The two most common use cases for firstEntityIndex
were as a sortKey
parameter when recording an EntityCommandBuffer
, and as an index into an array of per-entity data that the job is reading or writing.
You can now use chunkIndex
or unfilteredChunkIndex
as an EntityCommandBuffer
sort key. For more information, see the IJobChunk.Execute()
API documentation.
For jobs that need the firstEntityIndex
value for any other reason, use the optional helper function EntityQuery.CalculateBaseEntityIndexArrayAsync()
to compute the index for each chunk. You should call this method directly before scheduling the job that needs firstEntityIndex
, and use the same EntityQuery
that schedules the job, plus whatever input dependency the job requires. You can then pass the resulting array of per-chunk indices into the user job:
NativeArray<int> chunkBaseEntityIndices = query.CalculateBaseEntityIndexArrayAsync(
Allocator.TempJob, myJobInputDependency, out JobHandle baseIndexJobHandle);
JobHandle myJobHandle = new MyJob
{
ChunkBaseEntityIndices = chunkBaseEntityIndices,
// other job fields here
}.ScheduleParallel(query, baseIndexJobHandle);
In the user job, you can use a chunk's unfilteredChunkIndex to look up the baseEntityIndex for each chunk. You must mark the array as [DeallocateOnJobCompletion]
to prevent a memory leak. You can compute the index of a given entity in the query like so:
struct MyJob : IJobChunk
{
[ReadOnly] [DeallocateOnJobCompletion] public NativeArray<int> ChunkBaseEntityIndices;
// other job fields here
public void Execute(in ArchetypeChunk chunk, int unfilteredChunkIndex,
bool useEnabledMask, in v128 chunkEnabledMask)
{
int baseEntityIndex = ChunkBaseEntityIndices[unfilteredChunkIndex];
int validEntitiesInChunk = 0;
var enumerator = new ChunkEntityEnumerator(useEnabledMask,
chunkEnabledMask, chunk.Count);
while(enumerator.NextEntityIndex(out int i))
{
// INCORRECT: may count entities that do not match the query
int incorrectEntityIndexInQuery = baseEntityIndex + i;
// CORRECT: gives the index of the entity relative to all matching entities
int entityIndexInQuery = baseEntityIndex + validEntitiesInChunk;
++validEntitiesInChunk;
}
}
}
Convert IJobEntityBatch and IJobEntityBatchWithIndex to IJobChunk
The IJobEntityBatch
and IJobEntityBatchWithIndex
job types have been removed. You must convert existing implementations of these job types to IJobChunk
, which has been rewritten to efficiently support enableable components. This migration is similar to the information in the Update IJobChunk section of this upgrade guide:
- Change the job struct to implement
IJobChunk
instead ofIJobEntityBatch
orIJobEntityBatchWithIndex
- Update the signature of
Execute()
. The old parameter names are slightly different, but they map to the samechunkIndex
and firstEntityIndex
parameters of the oldIJobChunk
API. - Update any loops over entities to use
ChunkEntityEnumerator
(if enableable components might be present), or a raw for loop with an assert (if not).
You don't need to change Schedule(query,dependsOn)
to ScheduleParallel(query,dependsOn)
. IJobEntityBatch.Schedule()
and IJobEntityBatchWithIndex.Schedule()
already have the same semantics as the new IJobChunk
API.
For IJobEntityBatchWithIndex
, see the information in the Jobs that need firstEntityIndex section of this upgrade guide.
Rename asynchronous EntityQuery gather-scatter methods
The following methods in the EntityQuery
namespace have been renamed:
ToEntityArrayAsync
: Renamed toToEntityListAsync
.ToComponentDataArrayAsync
: Renamed toToComponentDataListAsync
.CopyFromComponentDataArrayAsync
: Renamed toCopyFromComponentDataListAsync
.
This is a result of the new enableable components feature. The set of entities that match an EntityQuery might depend on the results of a previously-scheduled job if the query references any enableable components. The asynchronous gather operations (previously EntityQuery.ToEntityArrayAsync
and EntityQuery.ToComponentArrayAsync
) now return a NativeList
rather than a NativeArray
. This is because the final length of a list that has enableable components can't be known until the gather job completes. A NativeList
can be returned to the caller before its final size is known, unlike a NativeArray
, whose size needs to be set at array creation time. So, the asynchronous methods have been renamed to reflect this.
To upgrade existing calls to these methods:
- Change the method name to its new one.
- Change the return type from
NativeArray<T>
toNativeList<T>
- Optionally, pass an additional input dependency
JobHandle
. The scheduled gather-scatter job automatically depends on any previously registered jobs which have write access to the query's enableable components. If you need additional input dependencies, you can pass them as an optionaladditionalInputDep
parameter. - If you want to access the output
NativeList
(including reading any of its fields), you need to either wait for the gather job to complete, or pass the list to a follow up job. This is because attempting to access the list triggers a safety error. If you create a follow up job, make sure it expects aNativeList
rather than aNativeArray
. If the follow up job is anIJobParallelFor
, you should convert it toIJobParallelForDefer
, which supports scheduling with aNativeList
. - Change
[DeallocateOnJobCompletion]
attributes to an allocator that doesn't require allocations to be explicitly disposed, such asWorld.UpdateAllocator.ToAllocator
. If you can't do this, then schedule a follow up job withlist.Dispose(JobHandle)
to asynchronously dispose the list once it's no longer required. For example:// old code struct ProcessHealthArrayJob : IJobParallelFor { [DeallocateOnJobCompletion] public NativeArray<Health> HealthArray; void Execute(int i) { // process HealthArray[i] here } } // inside system.OnUpdate() NativeArray<Health> healthArray = query.ToComponentDataArrayAsync(Allocator.TempJob, out JobHandle gatherJob); var processJob = new ProcessHealthArrayJob{ HealthArray = healthArray } .Schedule(healthArray.Count, 64, gatherJob); // new code struct ProcessHealthJob : IJobParallelForDefer { public NativeList<Health> HealthList; void Execute(int i) { // process HealthList[i] here } } // inside system.OnUpdate() NativeList<Health> healthList = query.ToComponentDataListAsync(World.UpdateAllocator.ToAllocator, out JobHandle gatherJob); var processJob = new ProcessHealthListJob{ HealthList = healthList } .Schedule(healthList, gatherJob);
Update system state components to cleanup components
The following APIs have been renamed:
ISystemStateComponentData
renamed toICleanupComponentData
ISystemStateSharedComponentData
renamed toICleanupSharedComponentData
ISystemStateBufferElementData
renamed toICleanupBufferElementData
You should replace all occurrences of these if the script updater doesn't automatically handle them following. Using the old type names won’t function properly and should trigger warnings.
Update TypeManager code to use TypeIndex instead of int
TypeManager
now uses TypeIndex
to represent a type index, rather than an int. This provides better type-safety, and less confusion on how the int should be used in code paths. TypeIndex
implicitly converts the int back and forth, so this has little impact on existing code, however this conversion has a performance cost. You should therefore upgrade any code that uses into to prefer TypeIndex
instead.
You must manually update any code that uses an int*
for an indirection to a type index, or array of type indices to TypeIndex*
.
Update EntityCommandBufferSystem that have direct access
EntityCommandBufferSystem
has been updated so that if you want to play back a particular EntityCommandBufferSystem
, you can now Burst compile it.
Previously, you couldn't Burst compile an entity command buffer that would get played back by a particular EntityCommandBufferSystem
because it directly accessed the EntityCommandBufferSystem
. For example:
public void OnUpdate(ref SystemState state) // ‘state’ unused in this example
{
var ecb = World.GetOrCreateSystemManaged<EndSimulationEntityCommandBufferSystem>()
.CreateCommandBuffer();
var deferredEntity = ecb.CreateEntity();
ecb.AddComponentData(deferredEntity, new myData(42, 255));
}
To do the same thing and Burst compile it, you can use SystemAPI
do the following:
[BurstCompile]
public void OnUpdate(ref SystemState state)
{
var ecb = SystemAPI.GetSingleton
<EndSimulationEntityCommandBufferSystem.Singleton>()
.CreateCommandBuffer(state.WorldUnmanaged);
var deferredEntity = ecb.CreateEntity();
ecb.AddComponentData(deferredEntity, new myData(42, 255));
}
While the nested singleton type has been implemented for built in top-level entities ComponentSystemGroup
s, you need to define the singleton type and implementation in any EntityCommandBufferSystems
you've defined to follow the same design.
Change system update code
Systems have been changed so that they update by default.
Previously, systems would early-out before calling OnUpdate
if none of the entity queries created by the system matched any entities (as determined by calling EntityQuery.IsEmptyNoFilter
), unless you used the AlwaysUpdateSystem
attribute.
If you don't make any changes to your code, some systems might call OnUpdate
when they wouldn't before. This causes a decrease in performance, because the early-out is used to avoid overhead of updating a system when it hasn't any work to do. It might also cause exceptions if the implementation of these systems assumes that certain components exist, based on the previous behavior.
To maintain the previous behavior, review each system:
- If the system has any calls to
RequireForUpdate
inOnCreate
, you don't need to make any changes. - If the system doesn't make any calls to
GetEntityQuery
, then you don't need to make any changes.- Calls to
GetSingleton
,SetSingleton
,HasSingleton
, and similar, make calls toGetEntityQuery
internally. - Source generation from
Entities.ForEach
orIJobEntity
can generate calls toGetEntityQuery
.
- Calls to
- If the system uses the obsolete
[AlwaysUpdateSystem]
attribute, remove it. - If none of the above are true, add the
[RequireMatchingQueriesForUpdate]
attribute.
For the best performance, you should specify the system update requirements explicitly with RequireForUpdate
and RequireAnyForUpdate
, and provide the minimal number of required queries or components.
Rename EntityQueryDescBuilder
EntityQueryDescBuilder
has been renamed to EntityQueryBuilder
, and its methods have been changed to match the syntax. EntityQueryBuilder
is now the recommended method to create an EntityQuery
, because it's Burst-compatible and more performant than other options, even if it isn't Burst compiled.
Previous methods such as EntityQueryDesc
and GetEntityQuery(ComponentType[])
will be removed in a future version of Entities.
For example, you can rewrite a query in a system inheriting from SystemBase
like so:
/// Old way
var query = GetEntityQuery(typeof(Translation), typeof(Rotation));
/// New way
var query = new EntityQueryBuilder(Allocator.Temp).WithAllRW<Translation, Rotation>().Build(this);
This is an example of a more complex query:
/// Old way
var query2 = GetEntityQuery(
new EntityQueryDesc
{
All = new[]
{
ComponentType.ReadWrite<LocalToWorld>(),
ComponentType.ReadOnly<Translation>(),
ComponentType.ReadOnly<Rotation>(),
},
None = new[]
{
ComponentType.ReadOnly<Parent>(),
ComponentType.ReadOnly<Child>(),
}
});
/// New way
var query2 = new EntityQueryBuilder(Allocator.Temp)
.WithAllRW<LocalToWorld>()
.WithAll<Translation, Rotation>()
.WithNone<Parent, Child>()
.Build(this);
Update SystemBase.Time and SystemState.Time
SystemBase.Time
and SystemState.Time
are now deprecated and you should use SystemAPI.Time
instead. Previously SystemBase.Time
and SystemState.Time
acted as aliases for World.Time
. You should now useSystemAPI.Time
which works in both ISystem
and SystemBase
. In cases where you can’t do that, because you’re outside a system, get hold of the world instead either as World
or WorldUnmanged
, which both have a Time
property.
Update SystemBase helper methods to SystemAPI
SystemBase helpers like GetComponent
, SetComponent
, GetSingleton
have been marked for deprecation. Instead use SystemAPI
inside systems. This also works for Entities.ForEach
In places where SystemAPI
doesn't work, you can do the following things:
For singleton APIs you can get an EntityQuery
and use their equating method:
// Before
void MyMethod(SystemBase mySystem) {
var mySingleton = mySystem.GetSingleton<MySingleton>();
}
// After
void MyMethod(SystemBase mySystem) {
var myEntityQueryBuilder = new EntityQueryBuilder(mySystem.WorldUpdateAllocator).WithAll<MySingleton>();
var myQuery = myEntityQueryBuilder.Build(mySystem);
var mySingleton = myQuery.GetSingleton<MySingleton>();
}
For GetComponent
, SetComponent
, HasComponent
, GetBuffer
and Exists
, you can either use the equating EntityManager
methods, or cache your own lookups:
// Before
void MyMethod(SystemBase mySystem, Entity e) {
var myComponent = mySystem.GetComponent<MyComponent>(e);
}
// After (Option A)
void MyMethod(SystemBase mySystem, Entity e) {
var myComponent = mySystem.EntityManager.GetComponentData<MyComponent>(e);
}
// After (Option B)
void MyMethod(SystemBase mySystem, Entity e, ComponentLookup<MyComponent> alreadyUpdatedLookupOfMyComponent) {
var myComponent = alreadyUpdatedLookupOfMyComponent[e];
}
GetBuffer
in SystemAPI
doesn't take in a bool of whether or not it's read only. Therefore it assumes it's always ReadWrite. To convert to read only, do the following:
// Before
var readonlyBuffer = this.GetBuffer<MyElement>(e, true);
// After
EntityManager.CompleteDependencyBeforeRO<MyElement>(); // Lookups don't cause syncing so this is needed for giving the exact same effect as before.
var readonlyBuffer = SystemAPI.GetBufferLookup<MyElement>(true)[e];
Add the Entities Graphics package to your project
The Hybrid Rendering package (com.unity.rendering.hybrid) has been renamed to Entities Graphics (com.unity.entities.graphics) for consistency. The Hybrid Renderer still exists as an empty utility package with a dependency on Entities Graphics so you won't encounter any problem. However, you should add the Entities Graphics package to your project directly to avoid any errors in the future when the Hybrid Renderer package is removed completely.
If your project uses stock hybrid rendering you don't need to change your code. If you're using hybrid rendering classes or structs in your custom code, you might need to rename some classes. As a rule of thumb, any Hybrid
in a class, struct, or enum has been replaced with EntitiesGraphics
. For example, UpdateOldHybridChunksJob
is now UpdateOldEntitiesGraphicsChunksJob
.
For further information upgrading to Entities Graphics, see the Entities Graphics upgrade guide.
Modify blob assets that use new or default
Blob assets created with new
or default
now produce an error. To fix this, do the following:
/// Before
var myBlob = new MyBlob(...);
struct MyBlob {
public MyBlob(...){...}
}
/// After
ref var myBlob = ref builder.ConstructRoot<MyBlob>();
myBlob.Setup(...);
struct MyBlob {
public void Setup(...){...}
}
You can't use fixed type varName[n]
syntax inside a field on a blob because it creates a pointer, which you can't use in blob assets. To fix this, do the following:
/// Before
unsafe struct MyBlob {
public fixed bool Values[128];
}
ref var blob = ...; // construct it
blob.Values[0] = true;
/// After
using Unity.Collections.LowLevel.Unsafe;
unsafe struct MyBlob {
[StructLayout(LayoutKind.Sequential, Size = 128)]
public struct Values128
{
byte @byte;
public bool this[int i] {
get => UnsafeUtility.ReadArrayElement<bool>(UnsafeUtility.AddressOf(ref @byte), i);
set => UnsafeUtility.WriteArrayElement(UnsafeUtility.AddressOf(ref @byte), i, value);
}
}
public Values128 Values;
}
ref var blob = ...; // construct it
blob.Values[0] = true;
Update partials in your project
Unity now generates a backing partial for ISystem
, and SystemBase
in most cases, and for IAspect
and IJobEntity
in all cases. Previously, every generator had their own Missing Partial
error message implementation. This error message has been replaced with an analyzer that throws EA0007
. This makes it easier to maintain, and means that you can disable specific sections. However, it also means that it's more conservative because it can't always detect when a generator does actually need the partial. As a result, ISystem
and SystemBase
now always has to have a partial.
To fix this, you can use Roslyn. In Rider and Visual Studio you can hover over a snippet that needs fixing, then select Add partial. You should see the replaced snippet along with options to apply the fix for your entire document, project, solution or containing type.
If you want to disallow sourcegen in a specific system, you can use the following syntax:
using Unity.Entities;
#pragma warning disable EA0007 // Force no sourcegen to take place for this system. E.g. SystemAPI, and IJobEntity scheduling will not be avaiable in this system.
struct ManualSystem : ISystem {}
#pragma warning enable EA0007
Update SceneSystem
SceneSystem is used to manage entity scenes loading. In Entities 0.51 you had to access the SceneSystem instance first and then call its methods to manage a scene. In 1.0 all those methods in SceneSystem are static, so you don't use the system instance. For example in 0.51 the code to load a scene would be:
var sceneSystem = World.GetOrCreateSystem<SceneSystem>();
sceneSystem.LoadSceneAsync(sceneGUID);
For the same example in 1.0 you can call the static method directly, but pass the unmanaged world as the first parameter:
SceneSystem.LoadSceneAsync(state.WorldUnmanaged, sceneGUID);