diff --git a/CHANGELOG.md b/CHANGELOG.md index c3018b72..6410f819 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,14 +1,95 @@ ---- -uid: changelog ---- - # Changelog -## [1.0.0-pre.47] - 2023-02-28 +## [1.0.0-pre.65] - 2023-03-21 + +### Added + +* Added support for automatic data mode switching to the Entities Hierarchy window and the Inspector window. +* Added BlobAsset safety check for passing blob assets into methods without using `ref`. +* Added the `LocalTransform.ComputeWorldTransformMatrix()` which synchronously computes an entity's world-space transform matrix, in the rare cases where an accurate world transform is needed in simulation code and is otherwise be unavailable. +* Added `RefRW SystemAPI.GetComponentRW(Entity,bool)` +* Bulk SetComponentEnabled operations on EntityManager: `EntityManager.SetComponentEnabled(EntityQuery, bool)` and `EntityManager.SetComponentEnabled(EntityQuery, ComponentType, bool)`. +* A `Unity.Transforms.Helpers` class with assorted transform-related helper functions: + * A simple `float4x4` extension methods for field extraction, such as `.Up()`, `.Forward()` and `.Translation()` + * Added utilities to apply a transformation matrix to a `float3` point or direction, or to a `quaternion` rotation + * A method to synchronously compute an entity's world-space transform, `.ComputeWorldTransformMatrix()` + * A method to compute the "LookAt" `quaternion` rotation for a position that would cause its "forward" direction to point towards some target. +* `TypeIndex.IsChunkSerializable` property has been added to identify if a component type is valid in a chunk that is intended to be serialized. If `SerializeUtility.SerializeWorld` (such as might be called while exporting a subscene) is used to serialize chunks that contain components whose `TypeIndex.IsChunkSerializable` returns false, an exception will be thrown telling you why the component type is inappropriate for serialization. +* Added `WeakSceneReference Unload(Scene scene)` method to unload the scene instance and release its resources. +* Added guidance to GetSingleton error message +* Transform Usage Flags +* Added support for managed (shared) components serialization in DOTS runtime. + +### Changed + +* Moved the tool for adding missing `partial` keywords to system and job types from Edit > Preferences > Entities into a Roslyn codefix. Your IDE of choice should now be able to fix this for you, and give you a red squiggly line if it's missing. +* IJobEntity no longer gives a compile error if you have a reference type field. This improves iteration time, and has the added benefit that you can now write managed code in an IJobEntity. Simply add a managed component to your IJE's Execute (that forces running without the jobsystem). Your job can now validly use that field. If you try to schedule this job rather than running it on the main thread, you'll correctly get thrown a runtime error for having a reference type in your job. +* Improved performance of IJobEntity generator, speeding up compile times. Attributes like WithAll, WithAny etc. now use syntax information directly. This mean that you can't make your own attribute on an IJobEntity named `WithAll` `WithAny`, `WithNone`, `WithDisabled`, `WithAbsent`, `WithOptions`, or `WithChangeFilter`. +* Updated Burst dependency version to 1.8.3. +* What was PostTransformScale as a float3x3 is now PostTransformMatrix as a float4x4. This is more general and offers maximum flexibility. You can, for example, use it to scale from a secondary pivot. +* ParentSystem removes the Parent component if said component points to an entity that doesn't exist anymore. +* Refactored how additive scenes are handled within the Runtime Content Manager. A scene is now returned, and that is used as the key to unload. This change required some API changes. +* Changed `WeakObjectSceneReference.LoadAsync` to return the Scene instance, which should be used to check the loading status and for unloading. +* Changed `RuntimeContentManager.UnloadScene` method to take the Scene instance as the only parameter. +* The BlobAssetStore used during baking now uses garbage collection instead of an explicit refcount. It is not required anymore to register blobs with authoring GameObjects nor to do explicit cleanup in incremental baking systems. +* Source generators for Systems and Aspects no longer default to outputting generated files in `Temp/GeneratedCode/***`. To turn it on, add `DOTS_OUTPUT_SOURCEGEN_FILES` to your Scripting Defines in Player Settings. Turning it on will cost compilation time. (The source generator for IJobEntity already made this change earlier.) +* Moved InternalCompilerInterface, EntityQueryEnumerator (now InternalEntityQueryEnumerator) and some other types and methods to the Unity.Entities.Internal namespace. These types and methods are not intended for use by user code. We would make them internal, but source generators won't work correctly that way unfortunately. + +### Deprecated + +* Deprecated `WeakSceneReference` Release method. Unload should now be used and the scene instance returned by LoadAsync needs to be passed in as a ref. +* `RegisterBindingAttribute(Type runtimeComponent, string runtimeField, bool generated)`. Vector type fields can now be registered automatically without the `generated` option. +* SceneSystem.UnloadParameters and the overload of SceneSystem.UnloadScene receiving SceneSystem.UnloadParameters as parameters. +* `EntityQuery.SetEnabledBitsOnAllChunks` as the only bulk operation on EntityQuery instead of EntityManager. Use the newly added bulk `SetComponentEnabled` overloads instead. +* WeakSceneReference properties LoadingStatus, SceneResult, SceneFileResult. +* RuntimeContentManager methods GetSceneLoadingStatus, GetSceneFileValue, GetSceneValue + +### Removed + +* `ENABLE_TRANSFORM_V1` define and existing transform v1 code. Transform v2 is now the only transform system. +* Tooling to re-write user files to add missing partial keywords to systems. +* The `TransformAspect` struct was removed. Recent changes to the Entities transform systems made the current implementation of `TransformAspect` much less useful, and we've decided to remove it from the package until we can provide a more valuable abstraction over the DOTS transform components. +* The `EntityQueryEnumerator.EntityCount` field has been removed from the public API. Note that `EntityQueryEnumerator` is only intended for use by DOTS source generators. +* `BlobAssetComputationContext` made internal. + +### Fixed + +* Baker IEntitiesPlayerSettings were not setup correctly if the com.unity.platforms package was not installed/present in the project. +* IJobEntity now no longer caches the default query when scheduling with a dynamic query. For example. `new MyJob().Schedule();` will use the query matching its execute signature whereas `new MyJob().Schedule(myQuery)` will now only use myQuery. This is useful in cases like RequireMatchingQueriesForUpdate, where you don't want to accidentally create extra queries. +* Jobs implementing IJobEntity can now be created in one assembly and scheduled in another. +* The `[WithDisabled]` attribute when applied to a job implementing `IJobEntity` now overrides the implicit `All` query defined by the signature of `Execute`. E.g. `Execute(MyComp a)` and `[WithDisabled(typeof(MyComp))]` now defines a query of EntityQuery(all={}, disabled=MyComp). This is useful in cases where you want to enable all components of type X which are present, but disabled. +* `WriteGroup` support in transform v2 `LocalToWorldSystem` code should now work correctly. +* Fixed compilation issue with 23.1/23.2* +* Detection of circular ordering dependencies between systems is now correct. +* Chaining `EntityQuery` methods with bulk operation methods is now supported. +* Docs and samples for ECB systems now accurately reflect recommended usage. Fixed issue during `TypeManager.Initialize` where managed components with a field containing a circular type definition may throw `ArgumentException: An item with the same key has already been added.` +* Calling Release on a `WeakObjectReference` will no longer log errors in the editor. +* Zero-sized ("tag") enableable components were not always correctly enabled by default, when added to an entire chunk (such as via `EntityManager.AddComponent(query)`). +* Fixed issue with DotsGlobalSettings reporting the incorrect PlayType when switching from DedicatedServer to another standalone build target. +* Fixed TypeManager initialization causing a crash in the Entities Hierarchy. +* If you schedule an `IJobEntity` instance with a custom query that doesn't contain the components required for the `Execute()` method to run, a readable and actionable runtime exception is now thrown when safety checks are enabled. +* `EntityCommandBuffer.Dispose()` can no longer trigger a stack overflow when disposing large command buffers. +* Throw a readable, actionable compile-time error informing users that `RefRO`, `RefRW`, `EnabledRefRO`, `EnabledRefRW`, `DynamicBuffer` and `UnityEngineComponent` may not be used with generic types. +* A `foreach` iterating over an `EntityQuery` with enableable components now iterates over the correct entities. +* Re-added obsolete baker functions +* The accidental exposure of package internals to "Assembly-CSharp" was reverted. +* Default the build system to use the client settings if the package com.unity.netcode is not installed when the active platform is dedicated server. +* `Entities.WithStructuralChanges().ForEach()` now correctly handles enableable components. +* Allow components to contain nested native containers. Previously the TypeManager would throw during initialization if a component contained a a nested NativeContainer field. Note: NativeContainers still cannot be verified to be safely accessed when used in jobs. So, if a component contains a nested NativeContainer field, that component can only be accessed from the main thread. +* Entities Hierarchy correctly selects the subscenes +* Invalid entity warning in Inspector window with runtime data mode is only applied to entities or game objects that can be converted to entities. +* Issue with IJobEntity source-generators not getting re-run in IDE. This could cause Rider and Visual Studio to not be able to find/debug generated code for IJobEntity types. +* Adding managed components to entities via an `EntityCommandBuffer` on the main thread no longer triggers the `NullReferenceException`. +* Fixed an issue where entities with enableable components loaded from a subscene could reference the wrong component's enabled/disabled state. +* Fixed an issue where idiomatic foreach (IFE) would not iterate over all entities that matched its query, if the query contains enableable components +* Issue where recompilation would retrigger baking unnecessarily. + + +## [1.0.0-pre.47] - 2023-03-01 ### Fixed -* Stripping (e.g. on il2cpp) now won't strip whole assemblies that have important systems, like graphics. +* Stripping (e.g. on IL2CPP) now won't strip whole assemblies that have important systems, like graphics. * Generic systems created at runtime no longer break sorting functionality. @@ -17,11 +98,11 @@ uid: changelog ### Added * Added `RegisterBindingAttribute(string authoringField, Type runtimeComponent, string runtimeField)` to provide better control when registering nested types in authoring components. -* RuntimeContentSystem.LoadContentCatalog allows for starting the content delivery and update process when ENABLE_CONTENT_DELIVERY is defined. The automatic update is no longer triggered when the applications starts. +* RuntimeContentSystem.LoadContentCatalog allows for starting the content delivery and update process when ENABLE_CONTENT_DELIVERY is defined. The automatic update is no longer triggered when the applications starts. * Streaming samples. -* RemoteContentCatalogBuildUtility.BuildContent helper method added to allow building player content without having to rebuild the player. This is needed in order for developers to create their own publishing workflow for remote content delivery. +* RemoteContentCatalogBuildUtility.BuildContent helper method added to allow building player content without having to rebuild the player. This is needed in order for developers to create their own publishing workflow for remote content delivery. * Added missing SceneSystem.UnloadScene prototype. -* Generic IJobEntity jobs are not yet supported. Added a proper error to indicate this instead of a compiler error. +* Generic IJobEntity jobs are not yet supported. Added a proper error to indicate this instead of a compiler error. * SceneSystem.UnloadScene(WorldUnmanaged world, Entity sceneEntity, bool fullyUnload) * `ManagedAPI.GetComponentTypeHandle` now let's you get a typehandle to `Class IComponentData`. * Baking systems from the excluded baking assemblies are also filtered out during baking. @@ -32,7 +113,7 @@ uid: changelog * WorldSystemFilter to the runtime version of ResolveSceneReferenceSystem. * DependsOnLightBaking can be called from bakers to register a dependency against light mapping data. * Added support for managed (shared) components serialization in DOTS runtime. -* debug only check to prevent disposal of blob assets managed by a blob asset store. +* Debug only check to prevent disposal of blob assets managed by a blob asset store. ### Changed @@ -41,16 +122,15 @@ uid: changelog * Upgraded to use Roslyn 4.0.1 * Added better support for vector type fields in `BindingRegistry`. * Unmanaged shared components are serialized as blittable data. -* `ISystem` now doesn't need `BurstCompile` on the struct. Thus bursting a system simply means to put BurstCompile on either `OnCreate`, `OnStartRunning`, `OnUpdate`, `OnStopRunning`, or `OnDestroy`. +* `ISystem` now doesn't need `BurstCompile` on the struct. To Burst compile a system, put BurstCompile on either `OnCreate`, `OnStartRunning`, `OnUpdate`, `OnStopRunning`, or `OnDestroy`. * The "a system could not be added to group" error message now contains the name of the World affected for easier debugging * Nested native containers are protected against in any type attributed with [NativeContainer] * Unmanaged shared components are no longer boxed when collecting BlobAssetReferences. -* EditorEntityScenes.GetSubScenes was made public in order to gather subscenes to pass to the BuildContent API. +* `EditorEntityScenes.GetSubScenes` was made public in order to gather subscenes to pass to the BuildContent API. * `EntityManager.GetAllUniqueSharedComponents` now takes an `AllocatorManager.AllocatorHandle` instead of an `Allocator` enum parameter allowing for custom allocators to be used when allocating the `NativeList` return value. `Allocator` implicitly converts to `AllocatorManager.AllocatorHandle` so no action is required to call the changed API. * IJobEntity refactored to IncrementalGenerator. -* IJobEntity now doesn't default to outputting generated files in `Temp/GeneratedCode/***`. To turn it on use `DOTS_OUTPUT_SOURCEGEN_FILES`. Turning it on will cost Compilation Time. +* IJobEntity now doesn't default to outputting generated files in `Temp/GeneratedCode/`. To turn it on use `DOTS_OUTPUT_SOURCEGEN_FILES`. Turning it on costs compilation time. * Replaced .Name with .FullName for duplicated component message in baking. -* Removed `IIsFullyUnmanaged` due to obtrusiveness when compilation fails. Instead gives runtime error when incorrectly scheduling managed IJobEntity. * ManagedComponents and Shared Managed Components can now be Scheduled in IJobEntity (ScheduleParallel still not allowed. Runtime error will be thrown if you try.) * Invalid entities now show their index and version when viewed in the inspector * In Bakers AddTransformUsageFlags now takes an entity instead of a GameObject or a Component. @@ -67,6 +147,7 @@ uid: changelog * SourceGen no longer outputs .cs files in `Temp/GeneratedCode` by default, because most IDEs such as Rider and Visual Studio support SourceGen output. If you want to emit the output (at the cost of significant compilation time), use the `DOTS_OUTPUT_SOURCEGEN_FILES` define. * From Unity Editor version 2022.2 and later, the **Auto Generate** mode in the Lighting window is unavailable with the Entities package. This is because when you generate lighting in a project, the Unity Editor opens all loaded subscenes, which might slow down Editor performance. On demand baking is still available and is the recommended way of generating lighting. * Tooling to re-write user files to add missing partial keywords to systems. +* Removed `IIsFullyUnmanaged` due to obtrusiveness when compilation fails. Instead gives runtime error when incorrectly scheduling managed IJobEntity. ### Fixed @@ -117,18 +198,17 @@ uid: changelog * `EntityManager.RemoveComponent(Entity, ComponentTypeSet)` and `EntityCommandBuffer.RemoveComponent(Entity, ComponentTypeSet)` no longer throw an exception if the target entity is invalid, for consistency with other RemoveComponent functions. * Entities Hierarchy: Potential crash caused by race condition when gathering the changes to update the Entities Hierarchy. * Entities Hierarchy potential crash when receiving GameObjects' OrderChanged events out of order. -* entities structural changes profiler module should no longer cause memory corruption (and crash) when recording from jobs using exclusive entity transactions. +* The Entities Structural Changes Profiler module should no longer cause memory corruption (and crash) when recording from jobs using exclusive entity transactions. * The number for buffer count in Entity Inspector is not cut anymore. * Primary entity is secured to be the first to show in the Preview window. * Potentially incorrect remapping of references when using BlobBuilder to create large blobs. This would lead to corrupted blob data in rare cases. - ## [1.0.0-pre.15] - 2022-11-16 ### Added -* Support serializing UnityEngine.AnimationCurves in managed components. +* Support for serializing UnityEngine.AnimationCurves in managed components. * Changing shared component data (managed or unmanaged) is now tracked by the entities structural changes profiler module. * WorldUnmanaged.GetAllSystems * Support for enabling or disabling components in the entity inspector, for components that derives from the `IEnableableComponent` interface. @@ -136,13 +216,12 @@ uid: changelog * Missing docs for Scratchpad and UpdateAllocator public APIs. * `ComponentTypeSet` now has a debugger type proxy, to show the list of components it contains. * DotsPlayerSettings can provide their own set of custom scripting defines. -* * UnityObjectRef now implements IEquatable>. * Support for `RefRW`, `RefRO`, `EnabledRefRW` and `EnabledRefRO` parameters in `IJobEntity.Execute()`. * Added convenience methods for adding Chunk Components to an EntityQueryBuilder. * Docs to provide an overview of prebuilt custom allocators. * `SystemAPI.ManagedAPI.HasComponent`, `SystemAPI.ManagedAPI.GetComponent`, `SystemAPI.ManagedAPI.TryGetComponent`, `SystemAPI.ManagedAPI.GetSingleton`, `SystemAPI.ManagedAPI.TryGetSingleton`. -* managed `EntityQuery.TryGetSingleton` +* Managed `EntityQuery.TryGetSingleton` * `SystemAPI.Query>` support. * `EntityQuery.TryGetSingletonRW` and `SystemAPI.TryGetSingletonRW` * Workflow for preparing builds for publishing @@ -153,7 +232,7 @@ uid: changelog * `SystemAPI.SetComponentEnabled`, `SystemAPI.SetBufferEnabled`, `SystemAPI.ManagedAPI.SetComponentEnabled` to set component enabledness from an entity. To do this in jobs, do so in ComponentLookup/BufferLookup. * `RequireForUpdateWithSystemComponent` to SystemBase and ISystem to help explain that system components won't normally partake in queries without explicitly mentioning it. * EntityQueryBuilder ChunkComponent calls to SystemAPI.EntityQueryBuilder for better symmetry. -* ArchetypeChunk.Has and ArchetypeChunk.HasChunkComponent for ease of checking (useful for `IJobEntityChunkBeginEnd`) +* `ArchetypeChunk.Has` and `ArchetypeChunk.HasChunkComponent` for ease of checking (useful for `IJobEntityChunkBeginEnd`) * `IJobEntityChunkBeginEnd` - allowing you to run code at the start and end of chunk iteration. * `SystemAPI.GetXTypeHandle` to easily get cached and `.Update`'d handles :3 * Added `EntityCommandBuffer.ParallelWriter.SetEnabled(Entity,bool)` method, for parity with the main-thread interface. @@ -167,6 +246,7 @@ uid: changelog * Added support for sticky data mode to the Entities Hierarchy window and the Inspector window. * Added support for automatic data mode switching to the Entities Hierarchy window and the Inspector window. + ### Changed * Entities package test components such as `EcsTestData` are no longer part of the package's public API; they are only intended for internal package testing. @@ -182,10 +262,10 @@ uid: changelog * Moved the baking options to the DOTS Editor preferences page. * EntityQueries created via EntityManager.CreateEntityQuery or EntityQueryBuilder.Build(EntityManager) will be owned by the EntityManager and be disposed by the EntityManager when it is destroyed. * RuntimeContentManager API for loading and managing Unity engine objects loaded from Content Archives. -* WeakObjectReference can be used to manage weak objects at runtime. -* Asset bundles are no longer build and loaded for referenced unity objects. AssetBundleManager class has been removed. +* `WeakObjectReference` can be used to manage weak objects at runtime. +* Asset bundles are no longer build and look for referenced Unity objects. AssetBundleManager class has been removed. * Bakers for base component types and decorated with `[BakeDerivedTypes]` are evaluated before bakers for derived component types. -* Renamed `EntityCommandBuffer.*ForEntityQuery` methods to be their singular overload equivalents `EntityCommandBuffer.*`. E.g. `EntityCommandBuffer.DestroyEntitiesForEntityQuery` is now an overload in `EntityCommandBuffer.DestroyEntity`. EntityCommandBuffer is now more in line with EntityManager!. +* Renamed `EntityCommandBuffer.*ForEntityQuery` methods to be their singular overload equivalents `EntityCommandBuffer.*`. E.g. `EntityCommandBuffer.DestroyEntitiesForEntityQuery` is now an overload in `EntityCommandBuffer.DestroyEntity`. EntityCommandBuffer is now more in line with EntityManager. * `EntityQuery.CalculateEntityCount(NativeArray)` * `EntityQuery.CalculateEntityCountWithoutFiltering(NativeArray)` * `EntityQuery.MatchesAny(NativeArray)` @@ -198,14 +278,12 @@ uid: changelog * Renamed `BlobAssetStore.Remove` to `BlobAssetStore.TryRemove`, to better convey its functionality, as it only removes the BlobAsset if it is present. * Renamed `SystemAPI.QueryBuilder` to `SystemAPI.EntityQueryBuilder` to better indicate that it is just caching a `Unity.Entities.EntityQueryBuilder` * Baked primary entities no longer have an implicit dependency on the Transform component. -* Removed the inner TransformData struct from WorldTransform, LocalTransform and ParentTransform (and instead use extension methods and properties). Position/Rotation/Scale can now be accessed directly on the transform type itself. * ContentDeliverySystem to ContentDeliveryGlobalState. The state is now updated from the RuntimeContentSystem. * All generic methods handling generic components with the constraint of `where T : class, IComponentData` has been changed to `where T : class, IComponentData, new()` to better indicate that all managed `IComponentData` types must be default constructable. * `ComponentTypeHandle`, `BufferTypeHandle`, `DynamicComponentTypeHandle`, and `DynamicSharedComponentTypeHandle` arguments to `ArchetypeChunk` methods are now passed by `ref` instead of by value. This facilitates a caching optimization that will be implemented in a future release. * Implement ISystem methods (OnCreate/OnUpdate/OnDestroy) as default interface methods. These no longer need to be defined in the struct implementing the ISystem interface if they are not used. * Searching the Entities hierarchy using the component filter now treats multiple entries as AND rather than OR. * Renamed the PostTransformMatrix component to PostTransformScale, and changed its value type to float3x3. -* Updated transform documentation. * It's now a compile error to schedule with managed code (for IJobEntity and IJobChunk) * EntityCommandBufferSystem.RegisterSingleton uses the system's entity rather than creating a custom singleton entity with the name of MyEcbSystem * Improved performance of `EntityManager.DestroyEntity(EntityQuery)` @@ -219,7 +297,7 @@ uid: changelog ### Deprecated -* rename `EntityManager.CompleteAllJobs` to `EntityManager.CompleteAllTrackedJobs`, to more accuractly describe what it is doing. +* Renamed `EntityManager.CompleteAllJobs` to `EntityManager.CompleteAllTrackedJobs`, to more accurately describe what it is doing. * SystemState.Time and SystemBase.Time has been deprecated in favor of World.Time and SystemAPI.Time * `[WithEntityQueryOptions]` for IJobEntity becomes `[WithOptions]` to be consistent with `EntityQueryBuilder` and `SystemAPI.QueryBuilder` * `SystemAPI.Query.WithEntityQueryOptions` becomes `SystemAPI.Query.WithOptions` to be consistent with `EntityQueryBuilder` and `SystemAPI.QueryBuilder` @@ -228,7 +306,6 @@ uid: changelog * SystemAPI duplicated API in `ComponentSystemBaseManagedComponentExtensions`. `GetSingleton`, `GetSingletonRW`, `SetSingleton`. Use SystemAPI alternatives instead. * SystemAPI duplicated API in `SystemBase`. `GetComponent`, `SetComponent`, `HasComponent`, `GetBuffer` and `Exists`. Use SystemAPI alternatives instead. - ### Removed * `ISystemBase` as the old name for good, use the new name `ISystem` @@ -244,6 +321,7 @@ uid: changelog * Removed the `DotsPlayerSettings` type. * Removed `View All Components` label for Aspects tab in Inspector. * Dependencies on `com.unity.platforms`package has been removed. +* Removed the inner TransformData struct from WorldTransform, LocalTransform and ParentTransform (and instead use extension methods and properties). Position/Rotation/Scale can now be accessed directly on the transform type itself. ### Fixed @@ -256,7 +334,7 @@ uid: changelog * Entity inspector no longer throws index out of range exception when modifying the content of integer fields. * Systems window no longer throws exceptions when encountering invalid worlds. * Using WithChangeFilter on Entities.ForEach or SystemAPI.Query would add the component type as ReadWrite. It now adds ReadOnly. -* Subscene entity names are trucated to 64 characters during incremental baking. +* Subscene entity names are truncated to 64 characters during incremental baking. * Components with `long` or `ulong` enum fields will no longer cause an exception when displayed in entity inspector. As temporary measure until 64 bit integers are supported by `UnityEngine.UIElements.EnumFlagsField`, a text field with the numerical value of the enum will be displayed. * Change API name from `SetAllocatorHandle` to `SetAllocator` for entity command buffer allocator. * RectTransform components are no longer skipped when baking transform hierarchies. @@ -264,7 +342,7 @@ uid: changelog * NetCodeClientSetting should not add FRONTEND_PLAYER_BUILD scripting define. * EntityQueryBuilder will correctly emit error when used without constructing with an Allocator. * `EntityQuery.ResetFilter()` now resets order version filtering to its default value (disabled) as well. -* BlobArray.ToArray() throws if the element type T contains nested BlobArray or BlobPtr fields. +* `BlobArray.ToArray()` throws if the element type T contains nested BlobArray or BlobPtr fields. * IJobEntity scheduling calls no longer ignores calls to `JobHandle.CombineDependencies()` passed as its `dependsOn` parameter. * IJobEntity scheduling calls now can contain calls to SystemAPI calls and other JobEntity scheduling * IJobEntity scheduling calls now ensure they only add automatic dependency assignment, when jobhandle is explicitly passed to scheduling functions. @@ -276,7 +354,7 @@ uid: changelog * Fixed a performance issue in the Entities Hierarchy when it's docked behind another window while GameObject events happen. * blob asset references would infrequently become invalid after a live baking pass. * Entities Hierarchy: Dynamically loaded subscenes are now correctly displayed in the hierarchy -* An exception that occurs when opening properties window from the Entities Hierarchy "Properties ..." context menu item. +* An exception that occurs when opening properties window from the Entities Hierarchy Properties context menu item. * `SystemAPI.GetBuffer` to get BufferLookup as ReadWrite (as to be consistent with rest of GetBuffer methods.) * `SystemAPI.Query>` where MyTag is a zero-size component, will now return `default(MyTag)` instead of throwing. * Drag n drop issue that can cause the Editor to enter an infinite loop @@ -308,18 +386,17 @@ uid: changelog * Under Relationships tab of Entity Inspector, systems with same name will display with added namespace for distinguishment. * Entities Hierarchy now properly retrieves subscenes' asset names. - ## [1.0.0-exp.12] - 2022-10-19 ### Changed -* updates to package dependencies +* Updates to package dependencies ## [1.0.0-exp.8] - 2022-09-21 ### Added -* new `GetSingletonBuffer(bool isReadOnly)` method on `ComponentSystemBase` and `EntityQuery`, for use with singleton `DynamicBuffer`s. No `SetSingletonBuffer()` is needed; once you have a copy of the buffer, its contents can be modified directly. +* `GetSingletonBuffer(bool isReadOnly)` method on `ComponentSystemBase` and `EntityQuery`, for use with singleton `DynamicBuffer`s. No `SetSingletonBuffer()` is needed; once you have a copy of the buffer, its contents can be modified directly. * `IJobEntityBatch` and `IJobEntityBatchWithIndex` now have `RunWithoutJobs()` and `RunByRefWithoutJobs()` extension methods. * Documentation on EntityCommandBuffer public functions including ParallelWriter and EntityCommandBufferManagedComponentExtensions. * GetOrCreateUnamangedSystemsAndLogException that allow to create unmanaged systems in batches like the equivalent GetOrCreateSystemsAndLogException. @@ -330,7 +407,6 @@ uid: changelog * `EntityManager.MoveComponent` is available as a way for managed components to properly transfer to other entities * `public bool HasBuffer(Entity entity) where T : struct, IBufferElementData` to `EntityManager`, which can be used to check whether an entity has a dynamic buffer of a given `IBufferElementData` type * `protected internal bool HasBuffer(Entity entity) where T : struct, IBufferElementData` to `SystemBase`, which can be used to check whether an entity has a dynamic buffer of a given `IBufferElementData` type -* Hybrid assemblies will not be included in DOTS Runtime builds. * DynamicBuffer.Resize(int length, NativeArrayOptions options); * DynamicBufferHandle.Update(ref SystemState) matching the same methods on ComponentTypeHandle to improve main thread performance * EntityManager.EntityManagerDebug.GetLastWriterSystemName. This is useful for debugging out which system last touched component data in a chunk @@ -338,8 +414,6 @@ uid: changelog * EntityArchetype.ToString reports all types on the archetype * `EntityManager.AddComponent(NativeArray, ComponentTypes)` and `EntityManager.RemoveComponent(NativeArray, ComponentTypes)` in order to perform batch component operations on a specific set of entities * New command in the EntityCommandBuffer to SetEnabled(Entity e, bool value). -* If a component implements `IEnableableComponent`, viewing an Entity within a debugger will now display whether a component is enabled or disabled. -* When viewing an ArchetypeChunk's ComponentTypes within a debugger, a `ComponentType` that implements `IEnableableComponent` will now display how many disabled components there are within a chunk. * Added property `UpdateAllocatorEnableBlockFree` in `World` to enable or disable world update allocator to free individual block when no memory is allocated in that block. * More detailed error descriptions for Job.WithCode * More detailed error descriptions for .this capture inside Entities.ForEach and LambdaJobs, now specifies whether it's Entities.ForEach or Job.WithCode additionally it specifies that you might be using a system's field, property or method as you're capturing 'this' system. @@ -348,9 +422,8 @@ uid: changelog * Support for GetStorageInfoFromEntity inside E.FE * New commands in EntityCommandBuffer to modify components (add/set/replace) of an entity's LinkedEntityGroup based on an EntityQueryMask. * `ComponentType` now provides a `ToFixedString` method to allow for a BurstCompatible way of generating a component's name and accessmode. -* ISystem Entities.ForEach lambdas may access system state through Systems proxy type -* Interface Unity.Entities.IAspect used for declaring aspects. -* Unity.Entities.ComponentDataRef. Used inside an aspect struct declaration as a proxy to the component data. It is also used during the generation of aspect code to identify the composition of the aspect. +* `Interface Unity.Entities.IAspect` used for declaring aspects. +* `Unity.Entities.ComponentDataRef`. Used inside an aspect struct declaration as a proxy to the component data. It is also used during the generation of aspect code to identify the composition of the aspect. * Class Unity.Entities.OptionalAttribute used for declaring optional component inside the aspect declaration. * Class Unity.Entities.DisableGenerationAttribute used to disable the source generation of aspect declarations. * Methods Unity.Entities.ComponentDataFromEntity.GetDataRef and GetDataRefOptional used to create ComponentDataRef from entity. @@ -371,7 +444,7 @@ uid: changelog * BlobAssetStore now checks if the blob was allocated with the correct allocator and throws if it wasn't * `BufferFromEntity.Update`, allowing users to update a reference within a system instead of constructing a new buffer every frame. * relaxed entity creation structural safety checks. -* `[CreateBefore]` and `[CreateAfter]` attributes to control the explict ordering for when systems `OnCreate` method is invoked relative to other systems. +* `[CreateBefore]` and `[CreateAfter]` attributes to control the explicit ordering for when systems `OnCreate` method is invoked relative to other systems. * `static AspectQueryEnumerable Query() where T : struct` in the `SystemAPI` class, allowing users to perform `foreach` iteration through a query without having to manually set up any arguments beforehand. This method may only be used inside methods in `ISystem` types. * IJobEntity supports Aspects in `Execute` parameters. * EntityManagerDebug.GetSystemProfilerMarkerName is a method useful for extracting the name of a systems profiler marker. This is used in our own test rigs for extracting performance data from systems. @@ -380,22 +453,16 @@ uid: changelog * `Update(SystemBase)` and `Update(SystemState)` to `DynamicComponentTypeHandle`, `SharedComponentHandle`, `DynamicSharedCompoentHandle`, and `EntityTypeHandle`, in order to allow for incremental updates. * `SetComponentEnabled()` to allow setting a component enabled by `DynamicComponentTypeHandle` * `GetComponentEnabledRO` to allow the retrieval of the enabledbits bitarray on a `Chunk` -* new menu item for exporting entities journaling data to CSV. +* New menu item for exporting entities journaling data to CSV. * Importance scaling: Custom entry for per chunk tile data. * TypeManager.HasDescendants(), TypeManager.IsDescendantOf(), TypeManager.GetDescendantCount() for checking the inheritance relationship between types. * `ComponentSystemBaseManagedComponentExtensions.GetSingletonRW` and `ComponentSystemBase.GetSingletonRW()` to access singletons by reference in systems, with read/write access to the data. * `EntityQuery.GetSingletonRW()` to access singletons by reference from an EntityQuery, with read/write access to the data. * `EntityQuery.TryGetSingleton(out T)`, `EntityQuery.HasSingleton()`, `EntityQuery.TryGetSingletonBuffer(out DynamicBuffer)`, and `EntityQuery.TryGetSingletonEntity(out Entity)` * SystemAPI for: `HasSingleton()`, `GetSingleton()`, `GetSingletonBuffer(bool)`, `TryGetSingleton(out T)`, `TryGetSingletonBuffer(out DynamicBuffer)`, `SetSingleton(T value)`, `GetSingletonEntity()`, `TryGetSingletonEntity(out Entity)`, and `GetSingletonRW()`. All of which are now supported inside Systems. -* Baker's now support declaring TransformUsageFlags which specifies how the transform component will be used at runtime -* optimized performance of `IJobEntityBatch` and `Entities.ForEach` -* Some extra `EntityCommandBuffer` checks can now be enabled during playback, by setting `EntityCommandBuffer.ENABLE_PRE_PLAYBACK_VALIDATION` to true. -* `QueryEnumerable SystemAPI.Query()` can now accept up to 8 type arguments, i.e. `QueryEnumerable<(T1, T2)> Query()`, `QueryEnumerable<(T1, T2, T3)> Query()`, and so forth. The maximum number of type arguments is set to 8, and correspondingly the maximum number of elements in the returned tuple is 8. This is in accordance with [current C# convention](https://docs.microsoft.com/en-us/dotnet/api/system.tuple-8?view=net-6.0). -* SystemBase.GetBuffer takes an optional isReadOnly parameter. * `.WithFilter(NativeArray entities)` to the `QueryEnumerablee` class. This allows users to supply an array of entities to a query over aspects/components in a `foreach` iteration. Entities without the specified aspects/components will be ignored. * Idiomatic `foreach` iteration through aspects/components is now supported inside `SystemBase` types. * `GetEntityDataPtrRO()`, `GetRequiredComponentDataPtrRO()`, and `GetRequiredComponentDataPtrRW()` methods to `ArchetypeChunk` (mostly for internal use, to provide efficient access to a Chunk's `Entity` array for generated job code). -* The DOTS Hierarchy content is now filtered based on the currently selected DataMode * `RefRO` is added as a read-only counterpart to `RefRW`. * Added an indicator to the items in the DOTS Hierarchy when they are in the Runtime DataMode to differentiate them from items in the Authoring DataMode. * Added netcode aware WorldSystemFilterFlags and WorldFlags. @@ -442,8 +509,8 @@ uid: changelog * Implicit syncing in SystemAPI for Systems. So calls to, GetComponent, SetComponent, HasComponent, GetBuffer, GetBufferFromEntity, HasBuffer and GetComponentDataFromEntity will complete other systems if they have that component. * `Aspect.CompleteDependencyBefore[RO|RW](ref SystemState)` for explicit Aspect syncing so that when on MainThread you can use GetAspect and GetAspectRO and it will complete that dependency. * SystemAPI now provides `GetAspectRW` and `GetAspectRO` methods in SystemBase and ISystem. -* EntityQueryBuilder WithAll, WithAny, WithNone fluent APIs that accept up to seven type arguments and can be chained together to create an EntityQueryBuilder. -* EntityQueryBuilder.WithAllRW and WithAnyRW that accept up to two type arguments. +* `EntityQueryBuilder WithAll`, `WithAny`, `WithNone `fluent APIs that accept up to seven type arguments and can be chained together to create an EntityQueryBuilder. +* `EntityQueryBuilder.WithAllRW` and `WithAnyRW `that accept up to two type arguments. * EntityQueryBuilder WithAll, WithAny, WithNone methods that accept an INativeList for bulk changes, or ComponentTypes that can only be known at runtime. These are compatible with NativeList and FixedList32Bytes et. al. * EntityQueryBuilder Build(SystemBase), Build(SystemState) and Build(EntityManager) each return a new EntityQuery, owned by the argument passed. * `SystemAPI.QueryBuilder()` to support building a query easily using fluent syntax inside `ISystem` and `SystemBase` types. @@ -465,18 +532,17 @@ uid: changelog ### Changed * **API-Breaking Change:** `IJobChunk.Execute()` now takes additional parameters to support per-component enable bits. These extra parameters contain information about which entities in the chunk should be processed or skipped (based on whether the relevant components are enabled or disabled). As a temporary workaround when converting existing `IJobChunk` implementations, we recommend adding a call to `Assert.IsFalse(useEnabledMask)` to their `Execute()` methods. -* Removed `IJobForEach`, due to long notice of deprecation * Changed `LiveLinkPatcher` and `LiveLinkPlayerSystem` to use `IJobEntityBatch`, due to removal of `IJobForeach` * Changed docs from `IJobForeach` and `IJobChunk` to refer to `IJobEntity`, and `IJobEntityBatch` respectivly * Changed IJE out of `DOTS_EXPERIMENTAL` * Improve Entity, System and Component inspector relationship tabs with dedicated message when there is nothing to show. * `IJobEntityBatchExtensions.RunWithoutJobs()` and `IJobEntityBatchWithIndexExtensions.RunWithoutJobs()` now pass their `jobData` parameter by value, for consistency with existing Run/Schedule methods. To pass by reference, use `RunByRefWithoutJobs()` instead. -* DOTS Hierarchy now display SubScenes' state (opened, livelinked, closed or not loaded). +* DOTS Hierarchy now displays SubScenes' state (opened, livelinked, closed or not loaded). * Make `ScratchpadAllocator` inherit `IAllocator`. -* both managed and unmanged systems instances are created before their respective OnCreate method are called. +* Both managed and unmanged systems instances are created before their respective OnCreate method are called. * When using `EntityManager.SetName` with a managed `string` as a parameter, if a string longer than 61 characters is used, the string will be truncated to fit within an `EntityName`, * Subscene nodes in DOTS Hierarchy now have a similar style as in the GameObject Hierarchy. -* Unity.Transforms systems are fully burst compiled now +* Unity.Transforms systems are fully Burst compiled now * Updated Emscripten from version 1.38.28.2-unity to 2.0.19-unity. * `EntityQuery.ToComponentDataArray` can be used with managed component as a generic parameter * `TypeManager.GetTypeIndexFromStableTypeHash` is now Burst compatible and can be called from Bursted functions. @@ -484,13 +550,13 @@ uid: changelog * Added support for `EntityQuery q = Entities.WithAll().ToQuery();` * No longer write out to files in Unity 2021+ from entities source generators. * Improved the performance of the `EntityQuery` matching chunk cache in applications with many empty archetypes. -*EntityTypeHandle is now marked as a readonly container since the data is never writable. This means is no longer required (But still correct & possible) to mark the EntityTypeHandle as [ReadOnly] in jobs. -* World.AddSystem for ISystem has been renamed to World.CreateSystem. This matches the managed system API. +* EntityTypeHandle is now marked as a readonly container since the data is never writable. This means is no longer required (But still correct & possible) to mark the EntityTypeHandle as [ReadOnly] in jobs. +* World.AddSystem for ISystem has been renamed to World.CreateSystem. This matches the managed system API. * World.DestroyUnmanagedSystem is now World.DestroySystem. This matches the managed system API. * ComponentSystemGroup.AddUnmanagedSystemToUpdateList has been renamed to AddSystemToUpdateList. This matches the managed system API. -* ComponentSystemGroup.RemoveUnmanagedSystemFromUpdateList. This matches the managed system API. +* ComponentSystemGroup.RemoveUnmanagedSystemFromUpdateList. This matches the managed system API. * `EntityCommandBufferSystem.CreateCommandBuffer()` now uses the `World.UpdateAllocator` to allocate command buffers instead of `Allocator.TempJob`. Allocations from this allocator have a fixed lifetime of two full World Update cycles, rather than being tied to the display frame rate. -* World.Dispose and World.DestroySystem can no longer be called while any system is executing (OnCreate / OnDestroy / OnUpdate) on the same world. This prevents a variety of corner cases where incorrect API usage would lead to a corrupted internal state. +* World.Dispose and World.DestroySystem can no longer be called while any system is executing (OnCreate / OnDestroy / OnUpdate) on the same world. This prevents a variety of corner cases where incorrect API usage would lead to a corrupted internal state. * World.GetExistingUnmanagedSystem no longer throws if the system couldn't be found but returns a null SystemHandle. (This matches the managed system behaviour of GetExistingSystem, which returns null) * Throwing an exception in OnDestroy now throws the exception in user code as opposed to just logging the exception. This matches the behaviour of SystemBase * `TypeManager.TypeInfo.DebugTypeName` now returns a `NativeText.ReadOnly` type allowing for burst compatible way to get a type name, reduces garbage and avoids string copies via string interning. @@ -500,7 +566,7 @@ uid: changelog * EntityManager.Version has been renamed to EntityManager.OrderVersion * Improved debug visualizers for EntityManager, Entity, Archetype and Chunk * Static safety ID creation is burst compatible, moving towards more Entities.ForEach job schedule sites burst compatibility. -* synchronous EntityQuery methods (`.ToEntityArray()`, `ToComponentDataArray()`, and `CopyFromComponentDataArray()`) are no longer implemented in terms of scheduling jobs. For asynchronous job-based implementations (which may be more efficient with extremely large workloads), use the variants of these methods with the `Async()` suffix. +* Synchronous EntityQuery methods (`.ToEntityArray()`, `ToComponentDataArray()`, and `CopyFromComponentDataArray()`) are no longer implemented in terms of scheduling jobs. For asynchronous job-based implementations (which may be more efficient with extremely large workloads), use the variants of these methods with the `Async()` suffix. * Updated docs explaining how to use IJobEntity. * CheckDisposed method in EntityQueryEnumerator is now public * The Current property for a generated Aspect Enumerator now has a conditional CheckDisposed call to identify when the property is being accessed with a disposed enumerator instance @@ -522,7 +588,7 @@ uid: changelog * `ComponentDataRef` is renamed to `RefRW`, and its property `Value` is likewise renamed to `ValueRW` in the name of explicitness. * Make initial memory block size of scratchpad allocator configurable. * Improved performance of `EntityManager.GetallUniqueSharedComponents` in IL2CPP builds -* Significantly improved the performance of `EntityQuery.CalculateEntityCount() +* Significantly improved the performance of `EntityQuery.CalculateEntityCount()` * Significantly improved the performance of `EntityQuery.IsEmpty`. * Global system version is now also incremented after a system update in addition to before a system update, so that changes made outside of systems also have their own version number. * The meaning of WorldSystemFilterFlags.Default can be changed by parent groups. @@ -570,17 +636,29 @@ uid: changelog * `ComponentDataFromEntity` was renamed to `ComponentLookup`, and `GetComponentDataFromEntity()` was renamed to `GetComponentLookup()`. * `BufferFromEntity` was renamed to `BufferLookup`, and `GetBufferFromEntity()` was renamed to `GetBufferLookup()`. In addition, the `HasComponent()` method on this type was renamed to `HasBuffer()`. * `StorageInfoFromEntity` was renamed to `EntityStorageInfoLookup`, and `GetStorageInfoFromEntity()` was renamed to `GetEntityStorageInfoLookup()`. -* T XXXSystem renamed to T XXXSystemManaged for managed system types +* `T XXXSystem` renamed to `T XXXSystemManaged` for managed system types * SystemHandleUntyped renamed to SystemHandle * T XXXSystem(Type) renamed to T XXXSystemManaged(Type) * SystemHandle.Update(WorldUnmanaged) works for managed systems too * the LoadSceneAsync overload that uses weak scene reference is now static. +* Hybrid assemblies will not be included in DOTS Runtime builds. +* If a component implements `IEnableableComponent`, viewing an Entity within a debugger will now display whether a component is enabled or disabled. +* When viewing an ArchetypeChunk's ComponentTypes within a debugger, a `ComponentType` that implements `IEnableableComponent` will now display how many disabled components there are within a chunk. +* ISystem Entities.ForEach lambdas may access system state through Systems proxy type +* Bakers now support declaring TransformUsageFlags which specifies how the transform component will be used at runtime +* Optimized performance of `IJobEntityBatch` and `Entities.ForEach` +* Some extra `EntityCommandBuffer` checks can now be enabled during playback, by setting `EntityCommandBuffer.ENABLE_PRE_PLAYBACK_VALIDATION` to true. +* `QueryEnumerable SystemAPI.Query()` can now accept up to 8 type arguments, i.e. `QueryEnumerable<(T1, T2)> Query()`, `QueryEnumerable<(T1, T2, T3)> Query()`, and so forth. The maximum number of type arguments is set to 8, and correspondingly the maximum number of elements in the returned tuple is 8. This is in accordance with [current C# convention](https://docs.microsoft.com/en-us/dotnet/api/system.tuple-8?view=net-6.0). +* SystemBase.GetBuffer takes an optional isReadOnly parameter. +* The DOTS Hierarchy content is now filtered based on the currently selected DataMode +* When creating an authoring compoenent and the movedfromAttribute is used, now we make sure to add "Authoring" string to the MovedFrom Class parameter. +* Removed the the default JobHandle parameter (it now must be passed in explicitly to match EFE scheduling with the built-in Dependency property). ### Deprecated -* EntityManager.GetEntityQueryMask(EntityQuery) has been deprecated uses EntityQuery.GetEntityQueryMask() instead. +* EntityManager.GetEntityQueryMask(EntityQuery) has been deprecated. Use EntityQuery.GetEntityQueryMask() instead. * Use of `[ExecuteAlways]` on systems is now deprecated (it's still supported on MonoBehaviours). Please use `[WorldSystemFilter(WorldSystemFilterFlags.Editor)]` instead to ensure your system is added and runs in the Editor's default world. If you'd like to ensure your system always updates, please use the [AlwaysUpdateSystemAttribute]` instead -* RequireSingletonForUpdate() has been renamed to RequireForUpdate() and no longer requires only a single component to exist. +* `RequireSingletonForUpdate()` has been renamed to `RequireForUpdate()` and no longer requires only a single component to exist. * AlwaysUpdateSystem has been deprecated and systems will now always update by default. * The `EntityQuery.ToEntityArrayAsync()`, `.ToComponentDataArrayAsync()`, and `.CopyFromComponentDataArrayAsync()` methods have been deprecated, as they do not correctly support enableable components and are prone to safety errors. They should be replaced with calls to the new `.ToEntityListAsync()`, `.ToComponentDataListAsync()`, and `.CopyFromComponentDataListAsync()` methods. * `EntityQuery.CreateArchetypeChunkArray()` was renamed to `EntityQuery.ToArchetypeChunkArray()`. The new function is also significantly faster. @@ -591,17 +669,19 @@ uid: changelog * The `EntityManager.AddComponents(Entity, ComponentTypes)` method has been renamed `AddComponent`, for consistency with all other `AddComponent` and `RemoveComponent` variants. * EntityQueryDescBuilder AddAll, AddAny, AddNone, and FinalizeQuery are all Obsolete now. * The `IJobEntityBatch` and `IJobEntityBatchWithIndex` job types have been deprecated, and will be removed before the 1.0 package release. New and current implementations of these job types should be conversion to `IJobChunk`, which handles enableable components much more efficiently. Note that the interface to `IJobChunk` has changed since previous DOTS releases; see the upgrade guide for migration tips covering the most common use cases. +* SystemGenerator and LambdaJobs: handling aspect as parameters to lambda jobs in our System Entities.ForEach. +* Correctly cache BufferTypeHandle for any DynamicBuffer used in Entities.ForEach ### Removed -* Remove the LiveLink feature and its build component. +* Removed the LiveLink feature and its build component. * ComponentTypes.m_Masks & ComponentTypes.Masks are now internal. This implementation detail was accidentally made public. * Removed element `EnableBlockFree` in enum `WorldFlags` because `EnableBlockFree` does not align with the usage of `WorldFlags`. -* DOTS Compiler Inspector. Functionality is now available via viewing generated code directly from Temp/GeneratedCode in the project directory. +* DOTS Compiler Inspector. Functionality is now available via viewing generated code directly from Temp/GeneratedCode in the project directory. * Entity Debugger (replaced by Entity Inspector, Systems window, DOTS Hierarchy, and Entities Profiler Modules) * Remove slow singleton API's from SystemState, that do too much when called in OnUpdate. Instead we are going to put those API's into SystemAPI where they can be efficiently code-generated * Removed GameObjectEntity -* Dependency on com.unity.roslyn package. This is no longer needed as Unity 2022 has built-in support for source-generators. +* Dependency on com.unity.roslyn package. This is no longer needed as Unity 2022 has built-in support for source-generators. * The `ArchetypeChunkIterator` type has been removed. To iterate over the chunks that match a query, call `query.CreateArchetypeChunkArray()` and iterate over the output array. * Removed the following deprecated methods: `BlobAssetReference.TryRead()`, `EntityQuery.CompareQuery(EntityQueryDesc[] queryDesc)`, `ScriptBehaviourUpdateOrder.AddWorldToPlayerLoop()`, `ScriptBehaviourUpdateOrder.AddWorldToCurrentPlayerLoop()`, `ScriptBehaviourUpdateOrder.AppendSystemToPlayerLoopList()`, and `MemoryBinaryReader.MemoryBinaryReader(byte* content)`. * ComponentSystem, EntityQueryBuilder, EntityQueryCache and the old ForEach methods, along with their tests. @@ -614,7 +694,8 @@ uid: changelog * The ability to schedule managed components in IJobEntity was removed due to safety concerns, instead use .Run. * Removed Entities.ForEach.WithFilter as this does not work correctly with enabled components. This feature will need to be implement in user code going forward. * The following `EntityQuery` methods have been removed: `ToEntityArray(NativeArray, Allocator)`, `ToComponentDataArray(NativeArray, Allocator)`, `CalculateEntityCount(NativeArray)`, `CalculateEntityCountWithoutFiltering(NativeArray)`, `MatchesAny(NativeArray)`, and `MatchesAnyIgnoreFilter(NativeArray)`. They are significantly slower than other overloads that do not limit processing to an array of entities, do not work with enableable components, and are prone to false positives. If an application requires these features, it's possible to implement them as wrappers around the remaining overloads, using a `NativeHashSet` of the desired entities as a post-processing step. -* SystemHandle and SystemRef +* `SystemHandle` and `SystemRef` +* Removed `IJobForEach`, due to long notice of deprecation ### Fixed @@ -625,7 +706,7 @@ uid: changelog * Bug with EntityCommandBuffer removing multiple components from multiple entities when the number of entities was more than 10. * Defining `UNITY_DOTS_DEBUG` in standalone builds no longer triggers false positives from `AssertValidArchetype()`. * OnCreateForCompiler is called before the OnCreate for unmanaged systems. -* unloading subscenes could sometimes result in an error about a query not including a member of LinkedEntityGroup +* Unloading subscenes could sometimes result in an error about a query not including a member of LinkedEntityGroup * When setting many long strings using `EntityManager.SetName`, the editor will properly handle the storage of these names. * `EntityQuery.ToComponentDataArray()` and `EntityQuery.CopyFromComponentDataArray()` now detect potential race conditions against running jobs which access the component `T`. These jobs must be completed before the `EntityQuery` methods are called. * `EntityQuery.CalculateEntityCountWithoutFiltering()` now gives correct results when the query includes enableable types. @@ -648,7 +729,7 @@ uid: changelog * Component inspector relationship tab being refreshed too often. * Dangling files left after a build using buildconfigs under certain circumstances * Freeing Chunks now occurs in constant time, and not linear time. -* codegen for `WithEntityQueryOptions` now works with multiple `EntityQueryOptions`. +* Codegen for `WithEntityQueryOptions` now works with multiple `EntityQueryOptions`. * DOTS Runtime now correctly sorts systems by creation order when calling TypeManager.GetSystems * Fixed Bug where GetSingleton and friends with a query that has more than 1 required component was returning the wrong component data * Certain usage patterns of `ComponentDataFromEntity` and `BufferFromEntity` no longer give incorrect results due to inconsistent `LookupCache` state. @@ -657,17 +738,17 @@ uid: changelog * `Entities.ForEach` calls that make use of an `Entity` parameter should no longer cause a warning to be logged (due to generated code) * `ExclusiveEntityTransaction.AddComponent` and `ExclusiveEntityTransaction.RemoveComponent` will no longer throw with the error message of `Must be called from the main thread` * Improved performance of Source Generators when run in IDEs. -* Fix Create -> ECS -> System template. +* Fix Create > ECS > System template. * `IJobEntity` inside nested struct now works. * `IJobEntity` now works inside namespaces that have `using` statements. * "System" in namespace causing issues with Entities.ForEach and other codegen. -* use of WithDisposeOnCompletion with Job.WithCode if a `using System.Collections` is missing. +* Use of WithDisposeOnCompletion with Job.WithCode if a `using System.Collections` is missing. * `EntityQuery.CopyFromComponentDataArray()` and `EntityQuery.CopyFromComponentDataArrayAsync()` now correctly set the change version of any chunks they write to. * System in System code. * Nested replacements, like GetComponent(SetComponent); * `SystemAPI.GetComponentDataFromEntity`, `SystemAPI.GetBufferFromEntity`, `SystemAPI.GetBuffer`, `SystemAPI.TryGetBuffer`, `SystemAPI.TryGetComponent`. * `SystemAPI.Time` now stores a copy of TimeData, making it deterministic in `Entities.ForEach` again. -* Fixes Issue where UnityEngine.Component, UnityEngine.GameObject, and UnityEngine.ScriptableObject didn't work as ManagedComponents for IJobEntity. +* Fixed an issue where UnityEngine.Component, UnityEngine.GameObject, and UnityEngine.ScriptableObject didn't work as ManagedComponents for IJobEntity. * Many additional debug checks are now run in standalone builds when `UNITY_DOTS_DEBUG` is defined. * IJobEntity now has support for Managed and Unmanaged SharedComponents in both ISystem and SystemBase * Entities profiler modules will no longer waste time recording data if they are not active in the profiler window. @@ -698,29 +779,269 @@ uid: changelog * Entities.ForEach in method with nullable parameter types. * SetComponent in Entities.ForEach with argument that has an element accessor. -### Security +## [0.51.1] - 2022-06-27 +### Changed -### Change +* Package Dependencies + * `com.unity.jobs` to version `0.51.1` + * `com.unity.platforms` to version `0.51.1` + * `com.unity.collections` to version `1.4.0` + * `com.unity.jobs` to version `0.70.0` -* SystemGenerator and LambdaJobs: handling aspect as parameters to lambda jobs in our System Entities.ForEach. +### Fixed -### Removed/Deprecated/Changed +* An issue with source generator that was causing a compilation error when the generator was unable to create the temporary output folder. +* An issue with netcode source generator that was trying to run on assembly that did not have the right references and also when the project path was not assigned, making impossible to load the templates files. +* Entities.ForEach in method with nullable parameter types. +* SetComponent in Entities.ForEach with argument that has an element accessor. -* Each bullet should be prefixed with Added, Fixed, Removed, Deprecated, or Changed to indicate where the entry should go. -* Correctly cache BufferTypeHandle for any DynamicBuffer used in Entities.ForEach -### Modified -* When creating an authoring compoenent and the movedfromAttribute is used, now we make sure to add "Authoring" string to the MovedFrom Class parameter. +## [0.51.0] - 2022-05-04 -### Removed/Changed +### Changed -* Removed the the default JobHandle parameter (it now must be passed in explicitly to match EFE scheduling with the built-in Dependency property). +* Package Dependencies + * `com.unity.jobs` to version `0.51.0` + * `com.unity.platforms` to version `0.51.0` + * `com.unity.mathematics` to version `1.2.6` + * `com.unity.collections` to version `1.3.1` + * `com.unity.burst` to version `1.6.6` +* Increased the maximum number of shared components per entity from 8 to 16. +* Updated dependency on version of com.unity.roslyn package that will work with both Unity 2020 and Unity 2021. + +### Fixed + +* DOTS Entities throws a compilation error when using named arguments. +* Fix Create > ECS > System template now adds partial keyword. +* Fixed a possible memory stomp triggered by specific sequences of `ComponentDataFromEntity` or `BufferFromEntity` calls. +* EntityQuery.CopyFromComponentDataArray() and EntityQuery.CopyFromComponentDataArrayAsync() now correctly set the change version of any chunks they write to. +* If the value of the Parent component of an entity is changed while the previous parent entity was destroyed at the same time, an exception could be thrown during the next update of the transform system. +* Changes to ComponentData made outside of Systems will be properly detected by EntityQueries with changed version filters. +* `EntityQuery` objects are consistently compared, regardless of which version of `GetEntityQuery` is called. + +### Added + +* New `BufferTypeHandle.Update()` method. Rather than creating new type handles every frame in `OnUpdate()`, it is more efficient to create the handle once in a system's `OnCreate()`, cache it as a member on the system, and call its `.Update()` method from `OnUpdate()` before using the handle. +* SystemBase.GetEntityQuery can now take an EntityQueryDescBuilder. -### Note -* All Systems inside other classes/structs, need to have their parents marked with partial as well. + +## [0.50.1-preview.3] - 2022-04-28 + +### Changed + +Release preparations, no functional changes. + +## [0.50.1-preview.2] - 2022-04-20 + +### Changed + +Release preparations, no functional changes. + + +## [0.50.1-preview.1] - 2022-04-07 + +### Added + +* Documentation on EntityCommandBuffer public functions including ParallelWriter and EntityCommandBufferManagedComponentExtensions. +* Hybrid assemblies will not be included in DOTS Runtime builds. +* `[WithAll]` Attribute that can be added to a struct that implements IJobEntity. Adding additional required components to the existing execute parameter required components. +* `[WithNone]` Attribute that can be added to a struct that implements IJobEntity. Specifying which components shouldn't be on the entity found by the query. +* `[WithAny]` Attribute that can be added to a struct that implements IJobEntity. Specifying that the entity found by this query should have at least one of these components. +* `[WithChangeFilter]` Attribute that can be added to a struct that implements IJobEntity, as well as on component parameters within the signature of the execute method. This makes it so that the query only runs on entities, which has marked a change on the component specified by the `[WithChangeFilter]`. +* `[WithEntityQueryOptions]` Attribute that can be added to a struct that implements IJobEntity. Enabling you to query on disabled entities, prefab entities, and use write groups on entities. +* Diagnostic suppressor to ignore specific generation of CS0282 warnings due to codegen. +* SystemBase.GetBuffer takes an optional isReadOnly parameter. + +### Changed + +* DOTS Hierarchy now display SubScenes' state (opened, livelinked, closed or not loaded). +* When using `EntityManager.SetName` with a managed `string` as a parameter, if a string longer than 61 characters is used, the string will be truncated to fit within an `EntityName`, +* Improved the performance of the `EntityQuery` matching chunk cache in applications with many empty archetypes. +* Removed `IJobForeach`, due to long notice of deprecation +* Changed `LiveLinkPatcher` and `LiveLinkPlayerSystem` to use `IJobEntityBatch`, due to removal of `IJobForeach` +* Changed docs from `IJobForeach` and `IJobChunk` to refer to `IJobEntity`, and `IJobEntityBatch` respectivly +* Changed IJE out of `DOTS_EXPERIMENTAL` +* Update dependency on com.unity.roslyn to 0.1.3-preview (no longer ignore CS0282 warnings globally). +* Updated docs explaining how to use IJobEntity. +* Updated com.unity.roslyn to `0.2.1-preview` +* CheckDisposed method in EntityQueryEnumerator is now public +* The Current property for a generated Aspect Enumerator now has a conditional CheckDisposed call to identify when the property is being accessed with a disposed enumerator instance +* SystemBase.GetBuffer registers a job dependency for the IBufferElementData type specified. + +### Removed + +* Remove the LiveLink feature and its build component. +* DOTS Compiler Inspector. Functionality is now available via viewing generated code directly from Temp/GeneratedCode in the project directory. + +### Fixed + +* Bug with EntityCommandBuffer removing multiple components from multiple entities when the number of entities was more than 10. +* Defining `UNITY_DOTS_DEBUG` in standalone builds no longer triggers false positives from `AssertValidArchetype()`. +* When setting many long strings using `EntityManager.SetName`, the editor will properly handle the storage of these names. +* `EntityQuery.ToComponentDataArray()` and `EntityQuery.CopyFromComponentDataArray()` now detect potential race conditions against running jobs which access the component `T`. These jobs must be completed before the `EntityQuery` methods are called. +* WorldSystemFilter, DisableAutoCreation, and AlwaysUpdateSystem attributes working with ISystem systems +* Interface implemented execute methods now work with IJobEntity. Before this point you couldn't make an interface of `interface ITranslationExecute { void Execute(ref Translation translation) }` and implement it in an IJobEntity: `partial struct TranslationJob : IJobEntity, ITranslationExecute { void Execute(ref Translation translation) {} }` +* `.Schedule` and `.ScheduleParallel` Invocations for IJobEntity without a jobhandle now matches Entities.ForEach automatic chain `SystemBase.Dependency` handling +* Dangling files left after a build using buildconfigs under certain circumstances +* Remove the double registers of world allocator when creating a world. +* Improved performance of Source Generators when run in IDEs. +* `ExclusiveEntityTransaction.AddComponent` and `ExclusiveEntityTransaction.RemoveComponent` will no longer throw with the error message of `Must be called from the main thread` +* SGICE002 Issue with nesting `SetComponent(GetComponent)` for replaced syntax in Entities.ForEach. +* "System" in namespace causing issues with Entities.ForEach and other codegen. +* use of WithDisposeOnCompletion with Job.WithCode if a `using System.Collections` is missing. +* `EntityQuery.Singleton` methods work correctly when the query has multiple required component data +* `EntityQuery.ToEntityArray()`, `EntityQuery.ToComponentDataArray()` and `EntityQuery.CopyFromComponentDataArray()` now complete any jobs running against the query's component types before performing the requested operation. This fixes a race condition introduced in Entities 0.17 (and present in Entities 0.50). +* `IJobEntity` inside nested struct now works. +* `IJobEntity` now works inside namespaces that have `using` statements. +* Fixes Issue where UnityEngine.Component didn't work as ManagedComponents for IJobEntity. + + +## [0.50.0] - 2021-09-17 + +### Added + +* **Window > DOTS > Entities** window to show all Entities in a world in real time, with ability to search, select each, and inspect it via the Inspector. +* **Window > DOTS > Components** window to show all Component types, with ability to search, select each, and inspect it via the Inspector. +* **Window > DOTS > Systems** window to show all Systems running in a world, categorized by System Group, with ability to search, select each, and inspect it via the Inspector. +* Introduced two new ECS specific **Window > Analysis > Profiler** modules: * **Entities Structural Changes** profiler module can record which world/system produced a structural change, and how much time it cost per frame. * **Entities Memory** profiler module can record which world/system allocates memory chunks, with additional details per archetype. +* `ArchetypeChunk.GetComponentDataPtrRO()` and `ArchetypeChunk.GetComponentDataPtrRW()` provide unsafe raw access to a chunk's component data, as a lower-overhead alternative to `ArchetypeChunk.GetNativeArray()` +* `ComponentTypeHandle.Update()` allows `ComponentTypeHandle`s to be created once at system creation time, and incrementally updated each frame before use. +* Adds clearer message when TypeManager hasn't been initialized yet, instead of only reporting a component type we don't know about has been requested. +* Disabled entities in Entity Window now have the same style as the disabled gameobjects in the gameobject hierarchy +* Go-to button to update Inspector content to reflect selected system and highlight the system in the Systems window if there is one open. +* It's now possible to specify an alignment when allocating an array with BlobBuilder +* Upgraded to burst 1.5.2 +* Added go-to buttons to update Inspector content to reflect selected component and highlight the component in the Components window if there is one open. +* Routines to create unmanaged systems on worlds were made available for public use +* It's now possible for a scene to contain weak asset references to other scenes and prefabs. All referenced scenes and prefabs will automatically be included in a player build. The sample in "EntitiesSamples/Assets/Advanced/WeakAssetReferences" shows how to use weak asset references to scenes and prefabs. +* Incremental conversion now tracks GameObject names to rename Entities when they change. +* New method `CanBeginExclusiveEntityTransaction` on `EntityManager` to check whether or not a new exclusive entity transaction can be made. +* Wrapper functions are added in CollectionHelper to create/allocate NativeArray from custom allocator +* Entities.ForEach() will now accept a lambda with no parameters. +* WithSharedComponentFilter now also works with two shared component parameters. +* `EntityCommandBuffer` has an `IsEmpty` property, which returns true if at least one command has been successfully recorded. +* TryGetComponent in ComponentDataFromEntity +* TryGetBuffer in BufferFromEntity +* Entities journaling, which can record ECS past events and inspected from the static class `EntitiesJournaling` properties. +* Allow for easier viewing of `EntityCommandBuffer` within an IDE through a new debug proxy. +* Within an `EntityCommandBufferDebugView`, each command will have a summary of the action performed before expanding the command. +* SystemRef.Update to allow updating unmanaged systems manually. +* Support WithScheduleGranularity with Entities.ForEach to allow per-entity scheduling +* `EntityCommandBuffer.Instantiate()` can now instantiate more than one `Entity` in a single command, writing the resulting entities to a `NativeArray`. +* Support for fully-bursted Entities.ForEach.Run in ISystemBase systems. +* RateUtils.VariableRateManager to facilitate update rate +* DefaultWorld.BeginVariableRateSimulationEntityCommandBufferSystem +* DefaultWorld.VariableRateSimulationSystemGroup +* DefaultWorld.EndVariableRateSimulationEntityCommandBufferSystem +* Element EnableBlockFree is added to enum WorldFlags to indicate whether World.UpdateAllocator is enabled to free individual memory block. +* `ComponentTypes` has a new constructor variant that takes a `FixedList128Bytes`, suitable for use in Burst-compiled code. +* `EntityCommandBuffer` has several new variants that target a `NativeArray`, which may be more efficient in many cases than recording individual commands for individual entities. +* New Archetypes window that can display current archetype memory usage. +* IJob* types use SharedStatic so they can be burst compiled eventually +* Add ability to add missing partials during generation if `DOTS_ADD_PARTIAL_KEYWORD` scripting define is set. + +### Changed + +* Added a fast path for `IJobEntityBatch.RunWithoutJobs()` and `IJobEntityBatchWithIndex.RunWithoutJobs()` where query filtering is disabled, resulting up to a 30% reduction in performance overhead. +* Merged `com.unity.dots.editor` package into `com.unity.entities` package, effectively deprecating the DOTS Editor as a standalone package. All the DOTS Editor package functionality is now included when referencing the Entities package. +* DOTS Runtime now uses source generators for codegen. +* Make parts of EntityPatcher burst compatible to prepare for burst compilation of EntityPatcher for its performance improvement. +* `Entity.Equals(object compare)` now returns false if the `compare` object is null, rather than throwing a `NullReferenceException`. +* Made `DynamicBuffer` an always blittable type (even in the Editor with safety checks on), so that it can be passed by reference to Burst function pointers. +* BlobAssetStore.ComputeKeyAndTypeHash hash calculation reduced chance of collision +* Capped the maximum number of previewable GameObjects to 100 in the Entity Conversion Preview. +* Capped the maximum number of additional entities shown to 250 in the Entity Conversion Preview. +* Improved overall performance of the Entity Conversion Preview. +* Source generators are now used as the default mode of codegen for Entities.ForEach and Generated Authoring Component. These can be disabled with `SYSTEM_SOURCEGEN_DISABLED` and `AUTHORINGCOMPONENT_SOURCEGEN_DISABLED` scripting defines if necessary. The largest change is that generated code can now be inspected and debugged (when not bursted). Generated code lives in Temp/GeneratedCode and can be stepped into with both Visual Studio and Rider. +* Documentation to highlight necessary prerequisites in the Build Configuration for making a profilable build. +* Entities window now shows prefab entities with a style similar to the one in the GameObject hierarchy +* Systems in the Entity inspector relationships tab are now sorted by scheduling order instead of creation order. +* Subscene headers are now loaded asynchronously and will no longer stall the main thread while loading. +* Performance of LiveTweaking has been improved. +* EntityDiffer capture entity changes when only entity's name is changed. +* With an IDE debugger, EntityQuery will present more information related to it. The raw view +* Debugging output for a `ComponentType` will present clearer info. +* The `batchesPerChunk` parameter to `IJobEntityBatch.ScheduleParallel()` has been replaced with a new `ScheduleGranularity` enum. Pass `ScheduleGranularity.Chunk` to distribute work to worker threads at the level of entire chunks (the default behavior). Pass `ScheduleGranularity.Entity` to distribute individual entities to each worker thread. This can improve load balancing in jobs that perform a large amount of work on a small number of entities. +*Make generate linker xml files deterministic in order. +* Within an IDE debugger, `ComponentSystemGroup` will present more relevant information. The raw view will be available for those who need the precise makeup of the class. +* `ComponentSystemGroup.RemoveSystemFromUpdateList` and `ComponentSystemGroup.RemoveUnmanagedSystemFromUpdateList` can now be used when `ComponentSystemGroup.EnableSystemSorting` is set to false +* Add debug checks to detect "placeholder" Entities created by one `EntityCommandBuffer` from being passed into a different `EntityCommandBuffer`. +* Clarified error message when calling `.Dispose()` on an `EntityQuery` created by `GetEntityQuery()`. This is always an error; these queries belong to the associated system, and should never be manually disposed. They will be cleaned up along with the system itself. +* Within an IDE debugger, `ArchetypeChunk` will present more relevant information. The raw view will be available for those who need the precise makeup of the struct. +* Within an IDE debugger, `EntityArchetype` will present more relevant information. The raw view will be available for those who need the precise makeup of the struct. +* IJobEntityBatch batchIndex parameter has been renamed to batchId. Documentation regarding what values to expect from this parameter have been updated accordingly. +* Changed: Within an IDE debugger, `EntityManager` will present more relevant information. The raw view will be available for those who need the precise makeup of the struct. +* Changed: Within an IDE debugger, an `ArchetypeChunk`'s OrderVersion and ChangeVersions per ComponentType will be easier to view. +* Changed: Within an IDE debugger, `SystemState` will present more relevant information. The raw view will be available for those who need the precise makeup of the struct. +* Changed: Within an IDE debugger, `World` will present more relevant information. The raw view will be available for those who need the precise makeup of the struct. +* `EntityCommandBufferSystem.CreateCommandBuffer()` now uses the `World.UpdateAllocator` to allocate command buffers instead of `Allocator.TempJob`. Allocations from this allocator have a fixed lifetime of two full World Update cycles, rather than being tied to the display frame rate. +* `EntityCommandBuffer.AddComponentForEntityQuery()` now asserts if the provided `T` value contains a reference to a temporary `Entity` created earlier in the same command buffer; these Entities are not yet correctly patched with the correct final Entity during playback. This patching will be implemented in a future change. +*Removed `ComponentSystemBaseManagedComponentExtensions.HasSingleton{T}` - `ComponentSystemBase.HasSingleton{T}` already handles managed components. +* ISystemBase to ISystem +* New Query Window design. +* FixedRateUtils renamed to RateUtils +* IFixedRateManager renamed to IRateManager +* Records in the EntitiesJournaling feature now have OriginSystem that will be populated for which system requested the change. This information is helpful to determine where a deferred EntityCommandBuffer was recorded from. +* Improved diagnostic when a SubScene section entity does not meet one of the constraints during GameObject conversion. + +### Deprecated + +* In a future release, `IJobEntityBatch.RunWithoutJobsInternal()` and `IJobEntityBatchWithIndex.RunWithoutJobsInternal()` will be removed from the public API; as the names indicate, they are for internal use only. User code should use the non-`Internal()` variants of these functions. +* Several public functions in the EntityDataAccess have been deprecated. The new functions follow this convention DuringStructuralChange(...) +* Entity Debugger has been marked as deprecated and will be removed in a future release. See new windows under **Window > DOTS**. + +### Removed + +* Deprecated functions in the EntityCommandBuffer for EntityQueries that were processed at Playback. +* GI Light baking in Closed SubScenes for now to remain consistent with Entity mesh renderers. +* `Unity.Entities.RegisterGenericJobTypeAttribute` has been moved to Unity.Jobs as `Unity.Jobs.RegisterGenericJobTypeAttribute`. +* StreamBinaryReader and StreamBinaryWriter are now internal +* Removed JobComponentSystem. It has been replaced by SystemBase, which it much better tested and supported. The Entities 0.5 upgrade guide explains how to upgrade from JobComponentSystem to SystemBase. +* IJobBurstSchedulable +* Job reflection data ILPP + +### Fixed + +* Fixed bug that caused compiler errors when users wrote multiple parts of the same partial type. +* Fixed a minor typo when generating the name of a conversion World. +* `[DisableAutoCreation]` is no longer inherited by subclasses, as documented. +* Improved the Entity inspector responsiveness. +* In Burst 1.5.0, fixed some player-build warnings that were caused by some entities code that contained `throw` statements not within `[Conditional("ENABLE_UNITY_COLLECTIONS_CHECKS")]` guarded functions. +* Performance of system safety checks greatly improved +* Systems window, Entities window and Components window all use the same minimum size. +* Systems window style issue in minimum size. +* Incremental conversion issue where children of disabled gameobjects are not properly re-enabled when parent is re-enabled. +* Fixed multiple inspectors issue with Entity Inspector where contents are duplicated in the existing inspectors. +* Fixed multiple inspectors issue with System Inspector where only the latest inspector has content while the rests are empty. +* We now use the TypeCache in TypeManager when initializing, which is about twice as fast as previously. +* Sometimes redundant error messages were logged, now fixed +* `EntityQuery` methods which limit their processing to a specific `NativeArray` now work correctly if the `EntityQuery` uses chunk filtering. +* Certain code paths of `IJobEntityBatchWithIndex` were not storing the per-batch base entity indices at the correct byte offset. +* `IJobEntityBatchWithIndex.ScheduleInternal()` did not always work correctly with `EntityQuery` chunk filtering and `limitToEntityArray` both enabled. +* The variant of `IJobEntityBatchWithIndex.Run()` that took a `limitToEntityArray` parameter no longer asserts. +* `IJobEntityBatch` was redundantly applying chunk filtering at both schedule-time and execute-time. +* `EntityCommandBuffer` no longer leaks embedded entity arrays whose commands are never played back. +* Methods that take an `EntityQuery` now validate the query's validity. +* Add missing bounds checks for `EntityManager` methods. +* A `ComponentSystemGroup` that disables automatic system sorting no longer sets its "sort order is dirty" flag on every update. +* `EntityQuery.SetSingleton()` will now throw an exception if the query only requested read-only access to type `T`. +* `EntityQuery.GetSingleton()` and `EntityQuery.SetSingleton()` now assert if `T` is a zero-sized component, avoiding a potential out-of-bounds memory access. +* Creating an `EntityQuery` with a non-empty list of `None` types will now match return a reference to an existing query if possible, instead of always creating a new query. +* `EntityCommandBuffer` playback of `*ForEntityQuery()` commands no longer leaks `AtomicSafetyHandle` allocations when collections checks are enabled +* Memory leak in BlobAssets when a World was disposed that had BlobAssets. Primarily seen when entering and exiting Playmode in the Editor. +* XXHash3 could potentially throw exceptions if Burst compilation was disabled. This is no longer that case. +* variant checkbox in GhostAuthoringComponent inspector was disabled if no variants for that component were present, not letting the user select the DoNotSerialize variation. +* SendToOwner not handled correctly by the client. Now both server and client do not send/receive the component only if the ghost present a GhostOwnerComponent. +* Baked lightmaps for SubScenes will no longer appear black due to lack of compiled shader features +* Clamp compute shader support detection to disallow GL < 4.3 +* If you update multiple packages, create a new section with a new header for the other package. +* EntityDiffer no longer patches BlobAsset or Entity references from `UnityEngine.Object` types. +* Debugging of source-generated Entities.ForEach +* Some main-threads `EntityCommandBuffer` methods were missing the necessary safety checks. +* StructuralChangeProfiler should now have the proper scope when making changes through the EntityCommandBuffer and EntityManager. @@ -921,12 +1242,6 @@ uid: changelog * SDF fonts are now rendered with correct anti-aliasing on WASM -### Removed/Deprecated/Changed - -* Each bullet should be prefixed with Added, Fixed, Removed, Deprecated, or Changed to indicate where the entry should go. - - - ## [0.18.0] - 2021-01-26 ### Added @@ -1019,13 +1334,6 @@ uid: changelog * using `BlobBuilder` in generic methods no longer raises a safety error * Many methods that use `IJob` were marked as `[NotBurstCompatible]` to reflect their true Burst compatibility. - -### Removed/Deprecated/Changed - -* Each bullet should be prefixed with Added, Fixed, Removed, Deprecated, or Changed to indicate where the entry should go. - - - ## [0.17.0] - 2020-11-13 ### Added @@ -1199,11 +1507,6 @@ uid: changelog * `EntityManger.AddComponent(EntityQuery entityQuery)` and `EntityManger.AddComponentData(EntityQuery entityQuery, NativeArray componentArray)` is 2x faster. * Reduced overhead of `IJobEntityBatch` execution by 5-10% if `batchesPerChunk` is 1. -### Security - - - - ## [0.15.0] - 2020-08-26 ### Added @@ -1586,10 +1889,6 @@ NotSupportedException: To marshal a managed method, please add an attribute name * System groups do not currently apply to systems running as part of `EntitySceneOptimizations` -### Known Issues - -* System groups do not currently apply to systems running as part of `EntitySceneOptimizations` - ## [0.8.0] - 2020-03-12 @@ -1608,8 +1907,6 @@ NotSupportedException: To marshal a managed method, please add an attribute name * LiveLink: No longer includes every Asset from builtin_extra to depend on a single Asset, and sends only what is used. This massively speeds up the first-time LiveLink to a Player. * Upgraded Burst to fix multiple issues and introduced native debugging feature. -### Deprecated - ### Fixed * Fixed LiveLinking with SubScene Sections indices that were not contiguous (0, 1, 2..). Now works with whatever index you use. diff --git a/DocCodeSamples.Tests/AspectExamples.cs b/DocCodeSamples.Tests/AspectExamples.cs index b31e6533..b10d7845 100644 --- a/DocCodeSamples.Tests/AspectExamples.cs +++ b/DocCodeSamples.Tests/AspectExamples.cs @@ -19,18 +19,18 @@ struct CannonBall : IComponentData public readonly Entity Self; // Aspects can contain other aspects. - readonly TransformAspect Transform; // A RefRW field provides read write access to a component. If the aspect is taken as an "in" // parameter, the field behaves as if it was a RefRO and throws exceptions on write attempts. + readonly RefRW Transform; readonly RefRW CannonBall; // Properties like this aren't mandatory. The Transform field can be public instead. // But they improve readability by avoiding chains of "aspect.aspect.aspect.component.value.value". public float3 Position { - get => Transform.LocalPosition; - set => Transform.LocalPosition = value; + get => Transform.ValueRO.Position; + set => Transform.ValueRW.Position = value; } public float3 Speed @@ -46,11 +46,11 @@ public partial struct MySystem : ISystem { public void OnUpdate(ref SystemState state) { - foreach (var transform in SystemAPI.Query()) + foreach (var cannonball in SystemAPI.Query()) { - // use transform aspect here + // use cannonball aspect here } } } #endregion -} \ No newline at end of file +} diff --git a/DocCodeSamples.Tests/BlobAssetBakingExamples.cs b/DocCodeSamples.Tests/BlobAssetBakingExamples.cs index cabadde9..5da41801 100644 --- a/DocCodeSamples.Tests/BlobAssetBakingExamples.cs +++ b/DocCodeSamples.Tests/BlobAssetBakingExamples.cs @@ -56,7 +56,8 @@ public override void Bake(MarketDataAuthoring authoring) // Register the Blob Asset to the Baker for de-duplication and reverting. AddBlobAsset(ref blobReference, out var hash); - AddComponent(new MarketDataComponent() {Blob = blobReference}); + var entity = GetEntity(TransformUsageFlags.None); + AddComponent(entity, new MarketDataComponent() {Blob = blobReference}); } } #endregion @@ -96,111 +97,8 @@ public override void Bake(MarketDataAuthoring authoring) AddBlobAssetWithCustomHash(ref blobReference, customHash); } - AddComponent(new MarketDataComponent() {Blob = blobReference}); - } - } - #endregion - - - public struct BBBlobAsset - { - public float3 MinBoundingBox; - public float3 MaxBoundingBox; - } - - public struct MeshComponent : IComponentData - { - public float3 MinBoundingBox; - public float3 MaxBoundingBox; - public Unity.Entities.Hash128 Hash; - } - - public struct BoundingBoxComponent : IComponentData - { - public BlobAssetReference BlobData; - } - - #region BlobAssetBakingSystemSetup - public struct CleanupComponent : ICleanupComponentData - { - public Unity.Entities.Hash128 Hash; - } - #endregion - - #region BlobAssetBakingSystem - [BurstCompile] - [WorldSystemFilter(WorldSystemFilterFlags.BakingSystem)] - public partial struct ComputeBlobAssetSystem : ISystem - { - public void OnUpdate(ref SystemState state) - { - // Get the BlobAssetStore from the BakingSystem - var blobAssetStore = - state.World.GetExistingSystemManaged().BlobAssetStore; - - // Handles the cleanup of BlobAssets for when BakingSystems are reverted - HandleCleanup(ref state, blobAssetStore); - - foreach (var (mesh, bb, cleanup) in SystemAPI - .Query, RefRW, - RefRW>()) - { - var hash = mesh.ValueRO.Hash; - BlobAssetReference blobAssetReference; - - // If the BlobAsset doesn't exist yet - if (!blobAssetStore.TryGet(hash, out blobAssetReference)) - { - // Create a new BlobAsset - var builder = new BlobBuilder(Allocator.Temp); - ref var root = ref builder.ConstructRoot(); - - root.MinBoundingBox = mesh.ValueRO.MinBoundingBox; - root.MaxBoundingBox = mesh.ValueRO.MaxBoundingBox; - - blobAssetReference = - builder.CreateBlobAssetReference(Allocator.Persistent); - - // Make sure to dispose the builder itself so all internal memory is disposed. - builder.Dispose(); - } - - // Update the Entity and BlobAssetStore with the new BlobAsset if it has changed - // since last run or is newly created - if (cleanup.ValueRO.Hash != hash || !bb.ValueRO.BlobData.IsCreated) - { - // Add the new BlobAsset to the component and the BlobAssetStore - blobAssetStore.TryAdd(hash, ref blobAssetReference); - bb.ValueRW.BlobData = blobAssetReference; - - // Cleanup of the 'previous' BlobAsset if it existed. - blobAssetStore.TryRemove(cleanup.ValueRO.Hash, true); - - // Update the cleanup component with the current hash - cleanup.ValueRW.Hash = hash; - } - } - } - - public void HandleCleanup(ref SystemState state, BlobAssetStore blobAssetStore) - { - // Add the Cleanup Component to the newly created Entities, that do not have it yet - var addCleanupQuery = SystemAPI.QueryBuilder() - .WithAll().WithNone().Build(); - state.EntityManager.AddComponent(addCleanupQuery); - - // Cleanup the BlobAssets and Cleanup Components of newly destroyed Entities - // Cleanup of the BlobAssets through the BlobAssetStore - foreach (var cleanup in SystemAPI - .Query>().WithNone()) - { - blobAssetStore.TryRemove(cleanup.ValueRO.Hash, true); - } - - // Remove the Cleanup Component from the destroyed Entities - var removeCleanupQuery = SystemAPI.QueryBuilder() - .WithAll().WithNone().Build(); - state.EntityManager.RemoveComponent(removeCleanupQuery); + var entity = GetEntity(TransformUsageFlags.None); + AddComponent(entity, new MarketDataComponent() {Blob = blobReference}); } } #endregion diff --git a/DocCodeSamples.Tests/DynamicBufferExamples.cs b/DocCodeSamples.Tests/DynamicBufferExamples.cs index 48088ec9..f8159bdd 100644 --- a/DocCodeSamples.Tests/DynamicBufferExamples.cs +++ b/DocCodeSamples.Tests/DynamicBufferExamples.cs @@ -186,9 +186,9 @@ public partial struct AccessDynamicBufferJob : IJobEntity { [ReadOnly] public BufferLookup BufferLookup; public void Execute() - { + { // ... - } + } } #endregion @@ -203,7 +203,7 @@ protected override void OnUpdate() Entities.ForEach((DynamicBuffer buffer) => { - for(int i = 0; i < buffer.Length; i++) + for (int i = 0; i < buffer.Length; i++) { sum += buffer[i].Value; } @@ -230,7 +230,7 @@ public struct SumResult : IJob public void Execute() { int sum = 0; - for(int i = 0; i < sums.Length; i++) + for (int i = 0; i < sums.Length; i++) { sum += sums[i]; } @@ -252,7 +252,7 @@ NativeArray intermediateSums Entities .WithStoreEntityQueryInField(ref query) .ForEach((int entityInQueryIndex, Entity entity, in DynamicBuffer buffer) => { - for(int i = 0; i < buffer.Length; i++) + for (int i = 0; i < buffer.Length; i++) { intermediateSums[entityInQueryIndex] += buffer[i].Value; } @@ -267,7 +267,7 @@ NativeArray intermediateSums .WithDisposeOnCompletion(intermediateSums) .WithCode(() => { - for(int i = 0; i < intermediateSums.Length; i++) + for (int i = 0; i < intermediateSums.Length; i++) { sum[0] += intermediateSums[i]; } @@ -354,11 +354,11 @@ BufferAccessor buffers ChunkEntityEnumerator enumerator = new ChunkEntityEnumerator(useEnabledMask, chunkEnabledMask, chunk.Count); - while(enumerator.NextEntityIndex(out var e)) + while (enumerator.NextEntityIndex(out var e)) { //An individual dynamic buffer for a specific entity DynamicBuffer buffer = buffers[e]; - for(int i = 0; i < buffer.Length; i++) + for (int i = 0; i < buffer.Length; i++) { sums[e] += buffer[i].Value; } @@ -373,7 +373,7 @@ public struct SumResult : IJob public NativeArray result; public void Execute() { - for(int i = 0; i < sums.Length; i++) + for (int i = 0; i < sums.Length; i++) { result[0] += sums[i]; } @@ -424,7 +424,7 @@ public static implicit operator float(FloatBufferElement e) public static implicit operator FloatBufferElement(float e) { - return new FloatBufferElement {Value = e}; + return new FloatBufferElement { Value = e }; } } @@ -436,7 +436,7 @@ protected override void OnUpdate() Entities.ForEach((DynamicBuffer buffer) => { - for(int i = 0; i < buffer.Length; i++) + for (int i = 0; i < buffer.Length; i++) { sum += buffer[i].Value; } @@ -463,7 +463,7 @@ private void ShowAdd() #region dynamicbuffer.addrange - int[] source = {1, 2, 3, 4, 5}; + int[] source = { 1, 2, 3, 4, 5 }; NativeArray newElements = new NativeArray(source, Allocator.Persistent); buffer.AddRange(newElements); @@ -471,7 +471,7 @@ private void ShowAdd() #region dynamicbuffer.asnativearray - int[] intArray = {1, 2, 3, 4, 5}; + int[] intArray = { 1, 2, 3, 4, 5 }; NativeArray.Copy(intArray, buffer.AsNativeArray()); #endregion @@ -494,7 +494,7 @@ private void ShowAdd() #region dynamicbuffer.copyfrom.nativearray - int[] sourceArray = {1, 2, 3, 4, 5}; + int[] sourceArray = { 1, 2, 3, 4, 5 }; NativeArray nativeArray = new NativeArray(source, Allocator.Persistent); buffer.CopyFrom(nativeArray); @@ -509,7 +509,7 @@ private void ShowAdd() #region dynamicbuffer.copyfrom.array - int[] integerArray = {1, 2, 3, 4, 5}; + int[] integerArray = { 1, 2, 3, 4, 5 }; buffer.CopyFrom(integerArray); #endregion @@ -619,4 +619,51 @@ protected override void OnUpdate() #endregion } } + + /// Entity command buffer example code + public class DynamicBufferECB + { + [InternalBufferCapacity(16)] + public struct MyElement : IBufferElementData + { + public int Value; + } + + #region dynamicbuffer.ecb + + private void Example(Entity e, Entity otherEntity) + { + EntityCommandBuffer ecb = new(Allocator.TempJob); + + // Record a command to remove the MyElement dynamic buffer from an entity. + ecb.RemoveComponent(e); + + // Record a command to add a MyElement dynamic buffer to an existing entity. + // This doesn't fail if the target entity already contains the buffer component. + // The data of the returned DynamicBuffer is stored in the EntityCommandBuffer, + // so changes to the returned buffer are also recorded changes. + DynamicBuffer myBuff = ecb.AddBuffer(e); + + // After playback, the entity will have a MyElement buffer with + // Length 20 and these recorded values. + myBuff.Length = 20; + myBuff[0] = new MyElement { Value = 5 }; + myBuff[3] = new MyElement { Value = -9 }; + + // SetBuffer is like AddBuffer, but safety checks will throw an exception at playback if + // the entity doesn't already have a MyElement buffer. + DynamicBuffer otherBuf = ecb.SetBuffer(otherEntity); + + // Records a MyElement value to append to the buffer. Throws an exception at + // playback if the entity doesn't already have a MyElement buffer. + // ecb.AddComponent(otherEntity) is a safe way to ensure a buffer + // exists before appending to it. + ecb.AppendToBuffer(otherEntity, new MyElement { Value = 12 }); + } + + #endregion + + } + + } diff --git a/DocCodeSamples.Tests/EntityCommandBuffers.cs b/DocCodeSamples.Tests/EntityCommandBuffers.cs index 6716210b..1e98a059 100644 --- a/DocCodeSamples.Tests/EntityCommandBuffers.cs +++ b/DocCodeSamples.Tests/EntityCommandBuffers.cs @@ -1,13 +1,57 @@ +using Doc.CodeSamples.Tests; +using Unity.Burst; using Unity.Collections; using Unity.Collections.LowLevel.Unsafe; using Unity.Entities; -using Unity.Jobs; using UnityEngine; // The files in this namespace are used to compile/test the code samples in the documentation. // Snippets used in entity_command_buffer.md namespace Doc.CodeSamples.Tests { + partial struct EcbParallel : ISystem + { + public void OnUpdate(ref SystemState state) + { + #region ecb_parallel + + EntityCommandBuffer ecb = new EntityCommandBuffer(Allocator.TempJob); + + // Methods of this writer record commands to + // the EntityCommandBuffer in a thread-safe way. + EntityCommandBuffer.ParallelWriter parallelEcb = ecb.AsParallelWriter(); + + #endregion + } + } + + #region ecb_parallel_for + + public struct HealthLevel : IComponentData + { + public int Value; + } + + partial class EcbParallelFor : SystemBase + { + protected override void OnUpdate() + { + Entities + .WithDeferredPlaybackSystem() + .ForEach( + (Entity entity, EntityCommandBuffer ecb, in HealthLevel health) => + { + if (health.Value == 0) + { + ecb.DestroyEntity(entity); + } + } + ).ScheduleParallel(); + } + } + + #endregion + #region ecb_concurrent struct Lifetime : IComponentData @@ -19,9 +63,11 @@ struct Lifetime : IComponentData partial class LifetimeSystem : SystemBase { EndSimulationEntityCommandBufferSystem m_EndSimulationEcbSystem; + protected override void OnCreate() { base.OnCreate(); + // Find the ECB system once and store it for later usage m_EndSimulationEcbSystem = World .GetOrCreateSystemManaged(); @@ -34,26 +80,27 @@ protected override void OnUpdate() var ecb = m_EndSimulationEcbSystem.CreateCommandBuffer().AsParallelWriter(); Entities .ForEach((Entity entity, int entityInQueryIndex, ref Lifetime lifetime) => - { - // Track the lifetime of an entity and destroy it once - // the lifetime reaches zero - if (lifetime.Value == 0) { - // pass the entityInQueryIndex to the operation so - // the ECB can play back the commands in the right - // order - ecb.DestroyEntity(entityInQueryIndex, entity); - } - else - { - lifetime.Value -= 1; - } - }).ScheduleParallel(); + // Track the lifetime of an entity and destroy it once + // the lifetime reaches zero + if (lifetime.Value == 0) + { + // pass the entityInQueryIndex to the operation so + // the ECB can play back the commands in the right + // order + ecb.DestroyEntity(entityInQueryIndex, entity); + } + else + { + lifetime.Value -= 1; + } + }).ScheduleParallel(); // Make sure that the ECB system knows about our job m_EndSimulationEcbSystem.AddJobHandleForProducer(this.Dependency); } } + #endregion public struct FooComp : IComponentData @@ -69,253 +116,327 @@ public struct BarComp : IComponentData [RequireMatchingQueriesForUpdate] partial class SingleThreadedSchedule_ECB : SystemBase { + #region ecb_single_threaded + protected override void OnUpdate() { -#region ecb_single_threaded -// ... in a system update - -// You don't specify a size because the buffer will grow as needed. -EntityCommandBuffer ecb = new EntityCommandBuffer(Allocator.TempJob); + // You don't specify a size because the buffer will grow as needed. + EntityCommandBuffer ecb = new EntityCommandBuffer(Allocator.TempJob); -// The ECB is captured by the ForEach job. -// Until completed, the job owns the ECB's job safety handle. -Entities - .ForEach((Entity e, in FooComp foo) => - { - if (foo.Value > 0) - { - // Record a command that will later add - // BarComp to the entity. - ecb.AddComponent(e); - } - }).Schedule(); + // The ECB is captured by the ForEach job. + // Until completed, the job owns the ECB's job safety handle. + Entities + .ForEach((Entity e, in FooComp foo) => + { + if (foo.Value > 0) + { + // Record a command that will later add BarComp to the entity. + ecb.AddComponent(e); + } + }).Schedule(); -this.Dependency.Complete(); + Dependency.Complete(); -// Now that the job is completed, you can enact the changes. -// Note that Playback can only be called on the main thread. -ecb.Playback(this.EntityManager); + // Now that the job is completed, you can enact the changes. + // Note that Playback can only be called on the main thread. + ecb.Playback(EntityManager); -// You are responsible for disposing of any ECB you create. -ecb.Dispose(); -#endregion + // You are responsible for disposing of any ECB you create. + ecb.Dispose(); } + + #endregion } + #region ecb_multi_threaded + [RequireMatchingQueriesForUpdate] - partial class MutliThreadedSchedule_ECB : SystemBase + partial struct MultiThreadedSchedule_ECB : ISystem { - protected override void OnUpdate() + partial struct ParallelRecordingJob : IJobEntity { + internal EntityCommandBuffer.ParallelWriter ecbParallel; + + // The ChunkIndexInQuery is unique for each chunk in the query and will be + // consistent regardless of scheduling. This will result in deterministic + // playback of the ECB. + void Execute(Entity e, [ChunkIndexInQuery] int sortKey, in FooComp foo) { -#region ecb_multi_threaded -// ... in a system update + if (foo.Value > 0) + { + // The first arg is the 'sort key' recorded with the command. + ecbParallel.AddComponent(sortKey, e); + } + } + } -EntityCommandBuffer ecb = new EntityCommandBuffer(Allocator.TempJob); + public void OnUpdate(ref SystemState state) + { + EntityCommandBuffer ecb = new EntityCommandBuffer(Allocator.TempJob); -// We need to write to the ECB concurrently across threads. -EntityCommandBuffer.ParallelWriter ecbParallel = ecb.AsParallelWriter(); + // We need to write to the ECB concurrently across threads. + new ParallelRecordingJob { ecbParallel = ecb.AsParallelWriter() }.Schedule(); -// The entityInQueryIndex is unique for each entity and will be -// consistent for each particular entity regardless of scheduling. -Entities - .ForEach((Entity e, int entityInQueryIndex, in FooComp foo) => { - if (foo.Value > 0) - { - // The first arg is the 'sort key' recorded with the command. - ecbParallel.AddComponent(entityInQueryIndex, e); + // Playback is single-threaded as normal. Note that explicitly completing + // adds a sync point, and is only done here for demonstration purposes. + // (Having an existing EntityCommandBufferSystem do the playback would not + // introduce an extra sync point.) + state.Dependency.Complete(); + + // To ensure deterministic playback order, + // the commands are first sorted by their sort keys. + ecb.Playback(state.EntityManager); + + ecb.Dispose(); } - }).Schedule(); + } -// Playback is single-threaded as normal. -this.Dependency.Complete(); + #endregion -// To ensure deterministic playback order, -// the commands are first sorted by their sort keys. -ecb.Playback(this.EntityManager); + partial struct SystemContainingAllTheOtherCodeSnippets : ISystem + { + partial struct MyParallelRecordingJob : IJobEntity + { + internal EntityCommandBuffer.ParallelWriter ecbParallel; -ecb.Dispose(); -#endregion + // The ChunkIndexInQuery is unique for each chunk in the query and will be + // consistent regardless of scheduling. This will result in deterministic + // playback of the ECB. + void Execute(Entity e, [ChunkIndexInQuery] int sortKey, in FooComp foo) + { + if (foo.Value > 0) + { + // The first arg is the 'sort key' recorded with the command. + ecbParallel.AddComponent(sortKey, e); + } } + } + public void OnUpdate(ref SystemState state) + { { -#region ecb_multi_playback -// ... in a system update + #region ecb_multi_playback + + // ... in a system update -EntityCommandBuffer ecb = - new EntityCommandBuffer(Allocator.TempJob, PlaybackPolicy.MultiPlayback); + EntityCommandBuffer ecb = + new EntityCommandBuffer(Allocator.TempJob, PlaybackPolicy.MultiPlayback); -// ... record commands + // ... record commands -ecb.Playback(this.EntityManager); + ecb.Playback(state.EntityManager); -// Additional playbacks are OK because this ECB is MultiPlayback. -ecb.Playback(this.EntityManager); + // Additional playbacks are OK because this ECB is MultiPlayback. + ecb.Playback(state.EntityManager); -ecb.Dispose(); -#endregion + ecb.Dispose(); + + #endregion } { -#region ecb_from_ecbsystem -// ... in a system - -// Assume an EntityCommandBufferSystem exists named FooECBSystem. -EntityCommandBufferSystem sys = - this.World.GetExistingSystemManaged(); - -// Create a command buffer that will be played back -// and disposed by MyECBSystem. -EntityCommandBuffer ecb = sys.CreateCommandBuffer(); - -// A ForEach with no argument to Schedule implicitly -// assigns its returned JobHandle to this.Dependency -Entities - .ForEach((Entity e, in FooComp foo) => { - // ... record to the ECB - }).Schedule(); - -// Register the job so that it gets completed by the ECB system. -sys.AddJobHandleForProducer(this.Dependency); -#endregion + #region ecb_from_ecbsystem + + // ... in a system + + // Assume an EntityCommandBufferSystem exists named FooECBSystem. + // This call to GetSingleton automatically registers the job so that + // it gets completed by the ECB system. + var singleton = SystemAPI.GetSingleton(); + + // Create a command buffer that will be played back + // and disposed by MyECBSystem. + EntityCommandBuffer ecb = singleton.CreateCommandBuffer(state.WorldUnmanaged); + + // An IJobEntity with no argument to Schedule implicitly + // assigns its returned JobHandle to this.Dependency + new MyParallelRecordingJob() { ecbParallel = ecb.AsParallelWriter() }.Schedule(); + + #endregion } { -#region ecb_deferred_entities -// ... in a system + #region ecb_deferred_entities + + // ... in a system + + EntityCommandBuffer ecb = new EntityCommandBuffer(Allocator.TempJob); -EntityCommandBuffer ecb = new EntityCommandBuffer(Allocator.TempJob); + Entity placeholderEntity = ecb.CreateEntity(); -Entity placeholderEntity = ecb.CreateEntity(); + // Valid to use placeholderEntity in later commands of same ECB. + ecb.AddComponent(placeholderEntity); -// Valid to use placeholderEntity in later commands of same ECB. -ecb.AddComponent(placeholderEntity); + // The real entity is created, and + // FooComp is added to the real entity. + ecb.Playback(state.EntityManager); -// The real entity is created, and -// FooComp is added to the real entity. -ecb.Playback(this.EntityManager); + // Exception! The placeholderEntity has no meaning outside + // the ECB which created it, even after playback. + state.EntityManager.AddComponent(placeholderEntity); -// Exception! The placeholderEntity has no meaning outside -// the ECB which created it, even after playback. -this.EntityManager.AddComponent(placeholderEntity); + ecb.Dispose(); -ecb.Dispose(); -#endregion + #endregion } { -#region ecb_deferred_remapping -// ... in a system + #region ecb_deferred_remapping -EntityCommandBuffer ecb = new EntityCommandBuffer(Allocator.TempJob); + // ... in a system -// For all entities with a FooComp component... -Entities - .WithAll() - .ForEach((Entity e) => - { - // In playback, an actual entity will be created - // that corresponds to this placeholder entity. - Entity placeholderEntity = ecb.CreateEntity(); + EntityCommandBuffer ecb = new EntityCommandBuffer(Allocator.TempJob); - // (Assume BarComp has an Entity field called TargetEnt.) - BarComp bar = new BarComp { TargetEnt = placeholderEntity }; + // For all entities with a FooComp component... + foreach (var (f, e) in SystemAPI.Query().WithEntityAccess()) + { + // In playback, an actual entity will be created + // that corresponds to this placeholder entity. + Entity placeholderEntity = ecb.CreateEntity(); - // In playback, TargetEnt will be assigned the - // actual Entity that corresponds to placeholderEntity. - ecb.AddComponent(e, bar); - }).Run(); + // (Assume BarComp has an Entity field called TargetEnt.) + BarComp bar = new BarComp { TargetEnt = placeholderEntity }; -// After playback, each entity with FooComp now has a -// BarComp component whose TargetEnt references a new entity. -ecb.Playback(this.EntityManager); + // In playback, TargetEnt will be assigned the + // actual Entity that corresponds to placeholderEntity. + ecb.AddComponent(e, bar); + } -ecb.Dispose(); -#endregion - } + // After playback, each entity with FooComp now has a + // BarComp component whose TargetEnt references a new entity. + ecb.Playback(state.EntityManager); + + ecb.Dispose(); + #endregion + } } } public partial class FooSystem : SystemBase { - protected override void OnUpdate() - { - } + protected override void OnUpdate() { } } - public class FooECBSystem : EntityCommandBufferSystem {} - -#region ecb_define_ecbsystem -// You should specify where exactly in the frame -// that the ECB system should update. -[UpdateInGroup(typeof(SimulationSystemGroup))] -[UpdateAfter(typeof(FooSystem))] -public class MyECBSystem : EntityCommandBufferSystem { - // The singleton component data access pattern should be used to safely access - // the command buffer system. This data will be stored in the derived ECB System's - // system entity. - - /// - /// Call to get this component for this system, and then call - /// on this singleton to create an ECB to be played back by this system. - /// - /// - /// Useful if you want to record entity commands now, but play them back at a later point in - /// the frame, or early in the next frame. - /// - public unsafe struct Singleton : IComponentData, IECBSingleton + public partial class FooECBSystem : EntityCommandBufferSystem { - internal UnsafeList* pendingBuffers; - internal Allocator allocator; - - /// - /// Create a command buffer for the parent system to play back. - /// - /// The command buffers created by this method are automatically added to the system's list of - /// pending buffers. - /// The world in which to play it back. - /// The command buffer to record to. - public EntityCommandBuffer CreateCommandBuffer(WorldUnmanaged world) + public unsafe struct Singleton : IComponentData, IECBSingleton { - return EntityCommandBufferSystem.CreateCommandBuffer(ref *pendingBuffers, allocator, world); + internal UnsafeList* pendingBuffers; + internal AllocatorManager.AllocatorHandle allocator; + + public EntityCommandBuffer CreateCommandBuffer(WorldUnmanaged world) + { + return EntityCommandBufferSystem.CreateCommandBuffer(ref *pendingBuffers, allocator, world); + } + + // Required by IECBSingleton + public void SetPendingBufferList(ref UnsafeList buffers) + { + pendingBuffers = (UnsafeList*)UnsafeUtility.AddressOf(ref buffers); + } + + // Required by IECBSingleton + public void SetAllocator(Allocator allocatorIn) + { + allocator = allocatorIn; + } + + // Required by IECBSingleton + public void SetAllocator(AllocatorManager.AllocatorHandle allocatorIn) + { + allocator = allocatorIn; + } } - /// - /// Sets the list of command buffers to play back when this system updates. - /// - /// This method is only intended for internal use, but must be in the public API due to language - /// restrictions. Command buffers created with are automatically added to - /// the system's list of pending buffers to play back. - /// The list of buffers to play back. This list replaces any existing pending command buffers on this system. - public void SetPendingBufferList(ref UnsafeList buffers) + + protected override void OnCreate() { - pendingBuffers = (UnsafeList*)UnsafeUtility.AddressOf(ref buffers); + base.OnCreate(); + + this.RegisterSingleton(ref PendingBuffers, World.Unmanaged); } - /// - /// Set the allocator that command buffers created with this singleton should be allocated with. - /// - /// The allocator to use - public void SetAllocator(Allocator allocatorIn) + } + + + #region ecb_define_ecbsystem + + // You should specify where exactly in the frame this ECB system should update. + [UpdateInGroup(typeof(SimulationSystemGroup))] + [UpdateAfter(typeof(FooSystem))] + public partial class MyECBSystem : EntityCommandBufferSystem + { + // The singleton component data access pattern should be used to safely access + // the command buffer system. This data will be stored in the derived ECB System's + // system entity. + + public unsafe struct Singleton : IComponentData, IECBSingleton { - allocator = allocatorIn; + internal UnsafeList* pendingBuffers; + internal AllocatorManager.AllocatorHandle allocator; + + public EntityCommandBuffer CreateCommandBuffer(WorldUnmanaged world) + { + return EntityCommandBufferSystem + .CreateCommandBuffer(ref *pendingBuffers, allocator, world); + } + + // Required by IECBSingleton + public void SetPendingBufferList(ref UnsafeList buffers) + { + var ptr = UnsafeUtility.AddressOf(ref buffers); + pendingBuffers = (UnsafeList*)ptr; + } + + // Required by IECBSingleton + public void SetAllocator(Allocator allocatorIn) + { + allocator = allocatorIn; + } + + // Required by IECBSingleton + public void SetAllocator(AllocatorManager.AllocatorHandle allocatorIn) + { + allocator = allocatorIn; + } + } + + protected override void OnCreate() + { + base.OnCreate(); + + this.RegisterSingleton(ref PendingBuffers, World.Unmanaged); } } - /// - protected override void OnCreate() - { - base.OnCreate(); - this.RegisterSingleton(ref PendingBuffers, World.Unmanaged); + #endregion + + [UpdateInGroup(typeof(SimulationSystemGroup))] + [UpdateAfter(typeof(FooSystem))] + public partial class NonSingletonECBSystem : EntityCommandBufferSystem { + // This class is intentionally empty. There is generally no + // reason to put any code in an EntityCommandBufferSystem. } -} -#endregion -#region ecb_addjobhandleforproducer -public struct ProcessInfo: IComponentData { public float Value; } -public struct ProcessCompleteTag : IComponentData {} + // 888 888 d8888 8888888b. 888b 888 8888888 888b 888 .d8888b. + // 888 o 888 d88888 888 Y88b 8888b 888 888 8888b 888 d88P Y88b + // 888 d8b 888 d88P888 888 888 88888b 888 888 88888b 888 888 888 + // 888 d888b 888 d88P 888 888 d88P 888Y88b 888 888 888Y88b 888 888 + // 888d88888b888 d88P 888 8888888P" 888 Y88b888 888 888 Y88b888 888 88888 + // 88888P Y88888 d88P 888 888 T88b 888 Y88888 888 888 Y88888 888 888 + // 8888P Y8888 d8888888888 888 T88b 888 Y8888 888 888 Y8888 Y88b d88P + // 888P Y888 d88P 888 888 T88b 888 Y888 8888888 888 Y888 "Y8888P88 -public partial class AsyncProcessJobSystem : SystemBase -{ + // ########################################################### + // ## DO NOT CHANGE THE CODE BELOW TO USE A SINGLETON, ## + // ## IT'S USED IN THE API DOCS FOR AddJobHandleForProducer ## + // ########################################################### + + #region ecb_addjobhandleforproducer + + public struct ProcessInfo : IComponentData { public float Value; } + public struct ProcessCompleteTag : IComponentData { } + + [BurstCompile] public partial struct ProcessInBackgroundJob : IJobEntity { public EntityCommandBuffer.ParallelWriter ConcurrentCommands; @@ -326,23 +447,30 @@ public void Execute(Entity entity, [ChunkIndexInQuery] int chunkIndex, in Proces // Remove ProcessInfo and add a ProcessCompleteTag... ConcurrentCommands.RemoveComponent(chunkIndex, entity); - ConcurrentCommands.AddComponent(chunkIndex, entity, new ProcessCompleteTag()); + ConcurrentCommands.AddComponent(chunkIndex, entity); } } - protected override void OnUpdate() + public partial class AsyncProcessJobSystem : SystemBase { - // Get a reference to the system that will play back - var ecbSingleton = SystemAPI.GetSingleton(); - var ecb = ecbSingleton.CreateCommandBuffer(World.Unmanaged); - - // Pass the command buffer to the writer job, using its ParallelWriter interface - var job = new ProcessInBackgroundJob + protected override void OnUpdate() { - ConcurrentCommands = ecb.AsParallelWriter(), - }; - Dependency = job.ScheduleParallel(Dependency); + // Get a reference to the system that will play back + var ecbSystem = World.GetOrCreateSystemManaged(); + var ecb = ecbSystem.CreateCommandBuffer(); + + // Pass the command buffer to the writer job, using its ParallelWriter interface + var job = new ProcessInBackgroundJob + { + ConcurrentCommands = ecb.AsParallelWriter(), + }; + Dependency = job.ScheduleParallel(Dependency); + + // Register the writer job with the playback system as an input dependency + ecbSystem.AddJobHandleForProducer(Dependency); + } } -} -#endregion -} + + #endregion + +} \ No newline at end of file diff --git a/DocCodeSamples.Tests/LambdaJobExamples.cs b/DocCodeSamples.Tests/LambdaJobExamples.cs index 9e2e49e6..56baaf3d 100644 --- a/DocCodeSamples.Tests/LambdaJobExamples.cs +++ b/DocCodeSamples.Tests/LambdaJobExamples.cs @@ -326,14 +326,14 @@ public partial class EFESystem : SystemBase protected override void OnUpdate() { #region lambda-params - Entities.ForEach( (Entity entity, int entityInQueryIndex, ref ObjectPosition translation, in Movement move) => { /* .. */}) - #endregion + #endregion .Run(); + } } } diff --git a/DocCodeSamples.Tests/LookupDataExamples.cs b/DocCodeSamples.Tests/LookupDataExamples.cs index dc49094f..d44c139a 100644 --- a/DocCodeSamples.Tests/LookupDataExamples.cs +++ b/DocCodeSamples.Tests/LookupDataExamples.cs @@ -106,11 +106,7 @@ private partial struct MoveTowardsJob : IJobEntity // Non-entity data public float deltaTime; -#if !ENABLE_TRANSFORM_V1 public void Execute(ref LocalTransform transform, in Target target, in LocalToWorld entityPosition) -#else - public void Execute(Translation position, in Target target, in LocalToWorld entityPosition) -#endif { // Get the target Entity object Entity targetEntity = target.entity; @@ -121,20 +117,10 @@ public void Execute(Translation position, in Target target, in LocalToWorld enti // Update translation to move the chasing entity toward the target float3 targetPosition = entityPosition.Position; -#if !ENABLE_TRANSFORM_V1 float3 chaserPosition = transform.Position; float3 displacement = targetPosition - chaserPosition; transform.Position = chaserPosition + displacement * deltaTime; -#else - float3 chaserPosition = position.Value; - - float3 displacement = targetPosition - chaserPosition; - position = new Translation - { - Value = chaserPosition + displacement * deltaTime - }; -#endif } } @@ -143,11 +129,7 @@ protected override void OnCreate() // Select all entities that have Translation and Target Component query = this.GetEntityQuery ( -#if !ENABLE_TRANSFORM_V1 typeof(LocalTransform), -#else - typeof(Translation), -#endif ComponentType.ReadOnly() ); } @@ -176,11 +158,7 @@ public partial class Snippets : SystemBase protected override void OnCreate() { // Select all entities that have LocalTransform and Target Component -#if !ENABLE_TRANSFORM_V1 query = this.GetEntityQuery(typeof(LocalTransform), ComponentType.ReadOnly()); -#else - query = this.GetEntityQuery(typeof(Translation), ComponentType.ReadOnly()); -#endif } [BurstCompile] @@ -192,11 +170,7 @@ private partial struct ChaserSystemJob : IJobEntity [ReadOnly] public ComponentLookup EntityPositions; -#if !ENABLE_TRANSFORM_V1 public void Execute(ref LocalTransform transform, in Target target, in LocalToWorld entityPosition) -#else - public void Execute(ref Translation position, in Target target, in LocalToWorld entityPosition) -#endif { var targetEntity = target.entity; @@ -207,18 +181,10 @@ public void Execute(ref Translation position, in Target target, in LocalToWorld // Update translation to move the chasing enitity toward the target #region lookup-ijobchunk-read float3 targetPosition = entityPosition.Position; -#if !ENABLE_TRANSFORM_V1 float3 chaserPosition = transform.Position; -#else - float3 chaserPosition = position.Value; -#endif float3 displacement = targetPosition - chaserPosition; float3 newPosition = chaserPosition + displacement * deltaTime; -#if !ENABLE_TRANSFORM_V1 transform.Position = newPosition; -#else - position = new Translation { Value = newPosition }; -#endif #endregion } diff --git a/DocCodeSamples.Tests/SpawnerBakerExample.cs b/DocCodeSamples.Tests/SpawnerBakerExample.cs index fae8469d..eccc43f0 100644 --- a/DocCodeSamples.Tests/SpawnerBakerExample.cs +++ b/DocCodeSamples.Tests/SpawnerBakerExample.cs @@ -14,11 +14,12 @@ class SpawnerBaker : Baker { public override void Bake(SpawnerAuthoring authoring) { - AddComponent(new Spawner + var entity = GetEntity(TransformUsageFlags.None); + AddComponent(entity, new Spawner { // By default, each authoring GameObject turns into an Entity. // Given a GameObject (or authoring component), GetEntity looks up the resulting Entity. - Prefab = GetEntity(authoring.Prefab), + Prefab = GetEntity(authoring.Prefab, TransformUsageFlags.Dynamic), SpawnPosition = authoring.transform.position, NextSpawnTime = 0.0f, SpawnRate = authoring.SpawnRate diff --git a/DocCodeSamples.Tests/SpawnerSystemExample.cs b/DocCodeSamples.Tests/SpawnerSystemExample.cs index e91f8e4d..f8a2dfcf 100644 --- a/DocCodeSamples.Tests/SpawnerSystemExample.cs +++ b/DocCodeSamples.Tests/SpawnerSystemExample.cs @@ -1,6 +1,5 @@ namespace Doc.CodeSamples.Tests { -#if !ENABLE_TRANSFORM_V1 #region example using Unity.Entities; using Unity.Transforms; @@ -41,5 +40,4 @@ private void ProcessSpawner(ref SystemState state, RefRW spawner) } } #endregion -#endif } diff --git a/DocCodeSamples.Tests/SpawnerSystemOptimizedExample.cs b/DocCodeSamples.Tests/SpawnerSystemOptimizedExample.cs index 3b9ff354..d416d55f 100644 --- a/DocCodeSamples.Tests/SpawnerSystemOptimizedExample.cs +++ b/DocCodeSamples.Tests/SpawnerSystemOptimizedExample.cs @@ -1,6 +1,5 @@ namespace Doc.CodeSamples.Tests { -#if !ENABLE_TRANSFORM_V1 #region example using Unity.Collections; using Unity.Entities; @@ -18,7 +17,7 @@ public void OnDestroy(ref SystemState state) { } public void OnUpdate(ref SystemState state) { EntityCommandBuffer.ParallelWriter ecb = GetEntityCommandBuffer(ref state); - + // Creates a new instance of the job, assigns the necessary data, and schedules the job in parallel. new ProcessSpawnerJob { @@ -43,7 +42,7 @@ public partial struct ProcessSpawnerJob : IJobEntity // IJobEntity generates a component data query based on the parameters of its `Execute` method. // This example queries for all Spawner components and uses `ref` to specify that the operation - // requires read and write access. Unity processes `Execute` for each entity that matches the + // requires read and write access. Unity processes `Execute` for each entity that matches the // component data query. private void Execute([ChunkIndexInQuery] int chunkIndex, ref Spawner spawner) { @@ -60,5 +59,4 @@ private void Execute([ChunkIndexInQuery] int chunkIndex, ref Spawner spawner) } } #endregion -#endif } diff --git a/DocCodeSamples.Tests/StreamingExamples.cs b/DocCodeSamples.Tests/StreamingExamples.cs index 352d2b02..eedebf02 100644 --- a/DocCodeSamples.Tests/StreamingExamples.cs +++ b/DocCodeSamples.Tests/StreamingExamples.cs @@ -8,9 +8,7 @@ using UnityEngine; using Hash128 = Unity.Entities.Hash128; -#if !ENABLE_TRANSFORM_V1 using LocalTransform = Unity.Transforms.LocalTransform; -#endif namespace Doc.CodeSamples.Tests { @@ -46,7 +44,8 @@ class Baker : Baker public override void Bake(SceneLoaderAuthoring authoring) { var reference = new EntitySceneReference(authoring.Scene); - AddComponent(new SceneLoader + var entity = GetEntity(TransformUsageFlags.None); + AddComponent(entity, new SceneLoader { SceneReference = reference }); @@ -225,9 +224,8 @@ public void OnUpdate(ref SystemState state) } #endif -#if !ENABLE_TRANSFORM_V1 - #region sceneloading_instancing3 + [UpdateInGroup(typeof(ProcessAfterLoadGroup))] public partial struct PostprocessSystem : ISystem { @@ -258,5 +256,4 @@ public void OnUpdate(ref SystemState state) } #endregion -#endif } diff --git a/DocCodeSamples.Tests/SystemAPIExamples.cs b/DocCodeSamples.Tests/SystemAPIExamples.cs new file mode 100644 index 00000000..a3d33427 --- /dev/null +++ b/DocCodeSamples.Tests/SystemAPIExamples.cs @@ -0,0 +1,84 @@ +using Unity.Entities; +using Unity.Transforms; +using Unity.Burst; +using Unity.Collections; + +namespace Doc.CodeSamples.Tests +{ + #region query-data + public partial struct MyRotationSpeedSystem : ISystem + { + + [BurstCompile] + public void OnUpdate(ref SystemState state) + { + float deltaTime = SystemAPI.Time.DeltaTime; + + foreach (var (transform, speed) in SystemAPI.Query, RefRO>()) + transform.ValueRW = transform.ValueRO.RotateY(speed.ValueRO.RadiansPerSecond * deltaTime); + } + } + #endregion + + + public partial struct RotationSpeedSystemAgain : ISystem + { + [BurstCompile] + public void OnUpdate(ref SystemState state) + { + #region query-data-alt + float deltaTime = SystemAPI.Time.DeltaTime; + + foreach (var (transform, speed) in SystemAPI.Query, RotationSpeed>()) + transform.ValueRW = transform.ValueRO.RotateY(speed.RadiansPerSecond * deltaTime); + #endregion + } + } + + public partial struct AnotherRotationSpeedSystem : ISystem + { + + [BurstCompile] + public void OnUpdate(ref SystemState state) + { + float deltaTime = SystemAPI.Time.DeltaTime; + + #region entity-access + foreach (var (transform, speed, entity) in SystemAPI.Query, RefRO>().WithEntityAccess()) + { + // Do stuff; + } + #endregion + } + } + + + public partial struct MyCoolSystem : ISystem + { + [BurstCompile] + public void OnUpdate(ref SystemState state) + { + #region dynamic-buffer + var bufferHandle = state.GetBufferTypeHandle(isReadOnly: true); + var myBufferElementQuery = SystemAPI.QueryBuilder().WithAll().Build(); + var chunks = myBufferElementQuery.ToArchetypeChunkArray(Allocator.Temp); + + foreach (var chunk in chunks) + { + var numEntities = chunk.Count; + var bufferAccessor = chunk.GetBufferAccessor(ref bufferHandle); + + for (int j = 0; j < numEntities; j++) + { + var dynamicBuffer = bufferAccessor[j]; + // Read from dynamicBuffer and perform various operations + } + } + #endregion + + } + + } + + +} \ No newline at end of file diff --git a/Unity.Entities.Editor/Inspector/Aspects/TransformAspectInspector.cs.meta b/DocCodeSamples.Tests/SystemAPIExamples.cs.meta similarity index 83% rename from Unity.Entities.Editor/Inspector/Aspects/TransformAspectInspector.cs.meta rename to DocCodeSamples.Tests/SystemAPIExamples.cs.meta index c7a99792..7dae0ecf 100644 --- a/Unity.Entities.Editor/Inspector/Aspects/TransformAspectInspector.cs.meta +++ b/DocCodeSamples.Tests/SystemAPIExamples.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 35590364b15814845ad3dcb9192a0361 +guid: 4d4ccac0bfffd4f11b331205c9b4f522 MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/DocCodeSamples.Tests/SystemGroupAllocatorExample.cs b/DocCodeSamples.Tests/SystemGroupAllocatorExample.cs index b46a2a5e..1aa8469e 100644 --- a/DocCodeSamples.Tests/SystemGroupAllocatorExample.cs +++ b/DocCodeSamples.Tests/SystemGroupAllocatorExample.cs @@ -8,7 +8,7 @@ namespace Doc.CodeSamples.Tests #region create-allocator [WorldSystemFilter(WorldSystemFilterFlags.Default | WorldSystemFilterFlags.Editor | WorldSystemFilterFlags.ThinClientSimulation)] [UpdateInGroup(typeof(FixedStepSimulationSystemGroup), OrderFirst = true)] - public class FixedStepTestSimulationSystemGroup : ComponentSystemGroup + public partial class FixedStepTestSimulationSystemGroup : ComponentSystemGroup { // Set the timestep use by this group, in seconds. The default value is 1/60 seconds. // This value will be clamped to the range [0.0001f ... 10.0f]. diff --git a/Unity.Entities.CodeGen.Tests/Unity.Entities.CodeGen.Tests.TestTypes.meta b/DocCodeSamples.Tests/content-management.meta similarity index 77% rename from Unity.Entities.CodeGen.Tests/Unity.Entities.CodeGen.Tests.TestTypes.meta rename to DocCodeSamples.Tests/content-management.meta index 6d2f3993..708ded05 100644 --- a/Unity.Entities.CodeGen.Tests/Unity.Entities.CodeGen.Tests.TestTypes.meta +++ b/DocCodeSamples.Tests/content-management.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 38a827c0e28a2465e862180edb7177ab +guid: 137561984df69514f9fd0d449b69e03d folderAsset: yes DefaultImporter: externalObjects: {} diff --git a/DocCodeSamples.Tests/content-management/DeliverContent.cs b/DocCodeSamples.Tests/content-management/DeliverContent.cs new file mode 100644 index 00000000..04c7d8c7 --- /dev/null +++ b/DocCodeSamples.Tests/content-management/DeliverContent.cs @@ -0,0 +1,32 @@ +namespace Doc.CodeSamples.Tests +{ + #region example + using System; + using Unity.Entities.Content; + using UnityEngine; + + public class GameStarter : MonoBehaviour + { + public string remoteUrlRoot; + public string initialContentSet; + + void Start() + { +#if ENABLE_CONTENT_DELIVERY + ContentDeliverySystem.Instance.UpdateContent(remoteUrlRoot, initialContentSet); + ContentDeliverySystem.Instance.RegisterForContentUpdateCompletion(s => + { + LoadMainScene(); + }); +#else + LoadMainScene(); +#endif + } + + void LoadMainScene() + { + //content is ready here... + } + } + #endregion +} diff --git a/Unity.Transforms.PerformanceTests/TransformAspectPerformanceTests.cs.meta b/DocCodeSamples.Tests/content-management/DeliverContent.cs.meta similarity index 83% rename from Unity.Transforms.PerformanceTests/TransformAspectPerformanceTests.cs.meta rename to DocCodeSamples.Tests/content-management/DeliverContent.cs.meta index c3e40387..d9d1af5f 100644 --- a/Unity.Transforms.PerformanceTests/TransformAspectPerformanceTests.cs.meta +++ b/DocCodeSamples.Tests/content-management/DeliverContent.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: ac8912a7f9039c64ca14cf1ce0f0d0d0 +guid: e73005c53776e674388b8496c51f81e2 MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/DocCodeSamples.Tests/content-management/LoadObjectUntypedWeakReferenceId.cs b/DocCodeSamples.Tests/content-management/LoadObjectUntypedWeakReferenceId.cs new file mode 100644 index 00000000..ab1ec07d --- /dev/null +++ b/DocCodeSamples.Tests/content-management/LoadObjectUntypedWeakReferenceId.cs @@ -0,0 +1,44 @@ +namespace Doc.CodeSamples.Tests +{ + #region example + using Unity.Entities; + using Unity.Entities.Content; + using Unity.Transforms; + using UnityEngine; + using Unity.Entities.Serialization; + + public struct ObjectUntypedWeakReferenceIdData : IComponentData + { + public bool startedLoad; + public UntypedWeakReferenceId mesh; + public UntypedWeakReferenceId material; + } + + [WorldSystemFilter(WorldSystemFilterFlags.Default | WorldSystemFilterFlags.Editor)] + [UpdateInGroup(typeof(PresentationSystemGroup))] + public partial struct RenderFromUntypedWeakReferenceIdSystem : ISystem + { + public void OnCreate(ref SystemState state) { } + public void OnDestroy(ref SystemState state) { } + public void OnUpdate(ref SystemState state) + { + foreach (var (transform, dec) in SystemAPI.Query, RefRW>()) + { + if (!dec.ValueRO.startedLoad) + { + RuntimeContentManager.LoadObjectAsync(dec.ValueRO.mesh); + RuntimeContentManager.LoadObjectAsync(dec.ValueRO.material); + dec.ValueRW.startedLoad = true; + } + if (RuntimeContentManager.GetObjectLoadingStatus(dec.ValueRO.mesh) == ObjectLoadingStatus.Completed && + RuntimeContentManager.GetObjectLoadingStatus(dec.ValueRO.material) == ObjectLoadingStatus.Completed) + { + Mesh mesh = RuntimeContentManager.GetObjectValue(dec.ValueRO.mesh); + Material material = RuntimeContentManager.GetObjectValue(dec.ValueRO.material); + Graphics.DrawMesh(mesh, transform.ValueRO.Value, material, 0); + } + } + } + } + #endregion +} diff --git a/Unity.Transforms.Tests/TransformAspectTests.cs.meta b/DocCodeSamples.Tests/content-management/LoadObjectUntypedWeakReferenceId.cs.meta similarity index 83% rename from Unity.Transforms.Tests/TransformAspectTests.cs.meta rename to DocCodeSamples.Tests/content-management/LoadObjectUntypedWeakReferenceId.cs.meta index 6655d548..b88cc043 100644 --- a/Unity.Transforms.Tests/TransformAspectTests.cs.meta +++ b/DocCodeSamples.Tests/content-management/LoadObjectUntypedWeakReferenceId.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: aa6d0d382a70e66408c08c71c6eae525 +guid: 15fa5d0325b43474a89351c1938eda14 MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/DocCodeSamples.Tests/content-management/LoadObjectWeakObjectReference.cs b/DocCodeSamples.Tests/content-management/LoadObjectWeakObjectReference.cs new file mode 100644 index 00000000..4800a3b5 --- /dev/null +++ b/DocCodeSamples.Tests/content-management/LoadObjectWeakObjectReference.cs @@ -0,0 +1,42 @@ +namespace Doc.CodeSamples.Tests +{ + #region example + using Unity.Entities; + using Unity.Entities.Content; + using Unity.Transforms; + using UnityEngine; + + public struct WeakObjectReferenceData : IComponentData + { + public bool startedLoad; + public WeakObjectReference mesh; + public WeakObjectReference material; + } + + [WorldSystemFilter(WorldSystemFilterFlags.Default | WorldSystemFilterFlags.Editor)] + [UpdateInGroup(typeof(PresentationSystemGroup))] + public partial struct RenderFromWeakObjectReferenceSystem : ISystem + { + public void OnCreate(ref SystemState state) { } + public void OnDestroy(ref SystemState state) { } + public void OnUpdate(ref SystemState state) + { + foreach (var (transform, dec) in SystemAPI.Query, RefRW>()) + { + if (!dec.ValueRW.startedLoad) + { + dec.ValueRW.mesh.LoadAsync(); + dec.ValueRW.material.LoadAsync(); + dec.ValueRW.startedLoad = true; + } + if (dec.ValueRW.mesh.LoadingStatus == ObjectLoadingStatus.Completed && + dec.ValueRW.material.LoadingStatus == ObjectLoadingStatus.Completed) + { + Graphics.DrawMesh(dec.ValueRO.mesh.Result, + transform.ValueRO.Value, dec.ValueRO.material.Result, 0); + } + } + } + } + #endregion +} diff --git a/Unity.Transforms/CompositeRotation.cs.meta b/DocCodeSamples.Tests/content-management/LoadObjectWeakObjectReference.cs.meta similarity index 83% rename from Unity.Transforms/CompositeRotation.cs.meta rename to DocCodeSamples.Tests/content-management/LoadObjectWeakObjectReference.cs.meta index d7bf5569..88e67b48 100644 --- a/Unity.Transforms/CompositeRotation.cs.meta +++ b/DocCodeSamples.Tests/content-management/LoadObjectWeakObjectReference.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 08a4bcc444ad94a01aa895023dba0352 +guid: 2df56df8ec22ddf4ab4a5f1fd11a524e MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/DocCodeSamples.Tests/content-management/LoadSceneUntypedWeakReferenceId.cs b/DocCodeSamples.Tests/content-management/LoadSceneUntypedWeakReferenceId.cs new file mode 100644 index 00000000..375dcf0a --- /dev/null +++ b/DocCodeSamples.Tests/content-management/LoadSceneUntypedWeakReferenceId.cs @@ -0,0 +1,37 @@ +namespace Doc.CodeSamples.Tests +{ + #region example + using Unity.Entities; + using Unity.Entities.Content; + using Unity.Entities.Serialization; + + public struct SceneUntypedWeakReferenceIdData : IComponentData + { + public bool startedLoad; + public UntypedWeakReferenceId scene; + } + + [WorldSystemFilter(WorldSystemFilterFlags.Default | WorldSystemFilterFlags.Editor)] + [UpdateInGroup(typeof(PresentationSystemGroup))] + public partial struct LoadSceneFromUntypedWeakReferenceIdSystem : ISystem + { + public void OnCreate(ref SystemState state) { } + public void OnDestroy(ref SystemState state) { } + public void OnUpdate(ref SystemState state) + { + foreach (var sceneData in SystemAPI.Query>()) + { + if (!sceneData.ValueRO.startedLoad) + { + RuntimeContentManager.LoadSceneAsync(sceneData.ValueRO.scene, + new Unity.Loading.ContentSceneParameters() + { + loadSceneMode = UnityEngine.SceneManagement.LoadSceneMode.Additive + }); + sceneData.ValueRW.startedLoad = true; + } + } + } + } + #endregion +} diff --git a/DocCodeSamples.Tests/content-management/LoadSceneUntypedWeakReferenceId.cs.meta b/DocCodeSamples.Tests/content-management/LoadSceneUntypedWeakReferenceId.cs.meta new file mode 100644 index 00000000..70062b92 --- /dev/null +++ b/DocCodeSamples.Tests/content-management/LoadSceneUntypedWeakReferenceId.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 16b825f703e55e948b72c2b8a96bb11b +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/DocCodeSamples.Tests/content-management/LoadSceneWeakObjectReference.cs b/DocCodeSamples.Tests/content-management/LoadSceneWeakObjectReference.cs new file mode 100644 index 00000000..d0a7cf71 --- /dev/null +++ b/DocCodeSamples.Tests/content-management/LoadSceneWeakObjectReference.cs @@ -0,0 +1,35 @@ +namespace Doc.CodeSamples.Tests +{ + #region example + using Unity.Entities; + using Unity.Entities.Content; + + public struct WeakObjectSceneReferenceData : IComponentData + { + public bool startedLoad; + public WeakObjectSceneReference scene; + } + + [WorldSystemFilter(WorldSystemFilterFlags.Default | WorldSystemFilterFlags.Editor)] + [UpdateInGroup(typeof(PresentationSystemGroup))] + public partial struct LoadSceneFromWeakObjectReferenceSystem : ISystem + { + public void OnCreate(ref SystemState state) { } + public void OnDestroy(ref SystemState state) { } + public void OnUpdate(ref SystemState state) + { + foreach (var sceneData in SystemAPI.Query>()) + { + if (!sceneData.ValueRO.startedLoad) + { + sceneData.ValueRW.scene.LoadAsync(new Unity.Loading.ContentSceneParameters() + { + loadSceneMode = UnityEngine.SceneManagement.LoadSceneMode.Additive + }); + sceneData.ValueRW.startedLoad = true; + } + } + } + } + #endregion +} diff --git a/DocCodeSamples.Tests/content-management/LoadSceneWeakObjectReference.cs.meta b/DocCodeSamples.Tests/content-management/LoadSceneWeakObjectReference.cs.meta new file mode 100644 index 00000000..e0b9f739 --- /dev/null +++ b/DocCodeSamples.Tests/content-management/LoadSceneWeakObjectReference.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 813bac517c4e3eb4cb54eb9123a8a48a +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/DocCodeSamples.Tests/content-management/WeaklyReferenceFromInspector.cs b/DocCodeSamples.Tests/content-management/WeaklyReferenceFromInspector.cs new file mode 100644 index 00000000..a5284e02 --- /dev/null +++ b/DocCodeSamples.Tests/content-management/WeaklyReferenceFromInspector.cs @@ -0,0 +1,26 @@ +namespace Doc.CodeSamples.Tests +{ + #region example + using Unity.Entities; + using Unity.Entities.Content; + using UnityEngine; + + public class MeshRefSample : MonoBehaviour + { + public WeakObjectReference mesh; + class MeshRefSampleBaker : Baker + { + public override void Bake(MeshRefSample authoring) + { + var entity = GetEntity(TransformUsageFlags.Dynamic); + AddComponent(entity, new MeshComponentData { mesh = authoring.mesh }); + } + } + } + + public struct MeshComponentData : IComponentData + { + public WeakObjectReference mesh; + } + #endregion +} diff --git a/DocCodeSamples.Tests/content-management/WeaklyReferenceFromInspector.cs.meta b/DocCodeSamples.Tests/content-management/WeaklyReferenceFromInspector.cs.meta new file mode 100644 index 00000000..691a9ae6 --- /dev/null +++ b/DocCodeSamples.Tests/content-management/WeaklyReferenceFromInspector.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 4549113f3a033ac498aa6d8e08bf08bf +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/DocCodeSamples.Tests/content-management/WeaklyReferenceFromScript.cs b/DocCodeSamples.Tests/content-management/WeaklyReferenceFromScript.cs new file mode 100644 index 00000000..1ef57390 --- /dev/null +++ b/DocCodeSamples.Tests/content-management/WeaklyReferenceFromScript.cs @@ -0,0 +1,21 @@ +namespace Doc.CodeSamples.Tests +{ + #region example + using UnityEngine; + using UnityEditor; + using Unity.Entities.Serialization; + + public static class ContentManagementEditorUtility + { + [MenuItem("Content Management/Log UntypedWeakReferenceId of Selected")] + private static void LogWeakReferenceIDs() + { + Object[] selectedObjects = Selection.GetFiltered(typeof(Object), SelectionMode.Assets); + for (int i = 0; i < selectedObjects.Length; i++) + { + Debug.Log($"{selectedObjects[i].name}: {UntypedWeakReferenceId.CreateFromObjectInstance(selectedObjects[i])}"); + } + } + } + #endregion +} diff --git a/DocCodeSamples.Tests/content-management/WeaklyReferenceFromScript.cs.meta b/DocCodeSamples.Tests/content-management/WeaklyReferenceFromScript.cs.meta new file mode 100644 index 00000000..b7d8e40b --- /dev/null +++ b/DocCodeSamples.Tests/content-management/WeaklyReferenceFromScript.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 71956ab028160114787fca5c801f0369 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/DocCodeSamples.Tests/content-management/WeaklyReferenceSceneFromInpsector.cs b/DocCodeSamples.Tests/content-management/WeaklyReferenceSceneFromInpsector.cs new file mode 100644 index 00000000..96619d5b --- /dev/null +++ b/DocCodeSamples.Tests/content-management/WeaklyReferenceSceneFromInpsector.cs @@ -0,0 +1,26 @@ +namespace Doc.CodeSamples.Tests +{ + #region example + using Unity.Entities; + using Unity.Entities.Content; + using UnityEngine; + + public class SceneRefSample : MonoBehaviour + { + public WeakObjectSceneReference scene; + class SceneRefSampleBaker : Baker + { + public override void Bake(SceneRefSample authoring) + { + var entity = GetEntity(TransformUsageFlags.Dynamic); + AddComponent(entity, new SceneComponentData { scene = authoring.scene }); + } + } + } + + public struct SceneComponentData : IComponentData + { + public WeakObjectSceneReference scene; + } + #endregion +} diff --git a/DocCodeSamples.Tests/content-management/WeaklyReferenceSceneFromInpsector.cs.meta b/DocCodeSamples.Tests/content-management/WeaklyReferenceSceneFromInpsector.cs.meta new file mode 100644 index 00000000..783c9444 --- /dev/null +++ b/DocCodeSamples.Tests/content-management/WeaklyReferenceSceneFromInpsector.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 4a2943b17354a5947a893ed7443ca997 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Documentation~/TableOfContents.md b/Documentation~/TableOfContents.md index ff239901..a619ed26 100644 --- a/Documentation~/TableOfContents.md +++ b/Documentation~/TableOfContents.md @@ -1,6 +1,6 @@ -* [Entities overview](index.md) - * [Upgrade guide](upgrade-guide.md) +* [Entities package](index.md) * [What's new](whats-new.md) + * [Upgrade guide](upgrade-guide.md) * [Get started](getting-started.md) * [Installation](getting-started-installation.md) * [ECS packages](ecs-packages.md) @@ -46,7 +46,7 @@ * [Access dynamic buffers in a chunk](components-buffer-get-all-in-chunk.md) * [Reuse a dynamic buffer for multiple entities](components-buffer-reuse.md) * [Access dynamic buffers from jobs](components-buffer-jobs.md) - * [Modify dynamic buffers with an EntityCommandBuffer](components-buffer-command-buffer.md) + * [Modify dynamic buffers with an entity command buffer](components-buffer-command-buffer.md) * [Reinterpret a dynamic buffer](components-buffer-reinterpret.md) * [Chunk components](components-chunk.md) * [Introducing chunk components](components-chunk-introducing.md) @@ -56,14 +56,14 @@ * [Enableable components overview](components-enableable-intro.md) * [Use enableable components](components-enableable-use.md) * [Singleton components](components-singleton.md) - * [Aspect overview](aspects-intro.md) - * [Aspect concepts](aspects-concepts.md) - * [Create an aspect](aspects-create.md) - * [Aspect source generation](aspects-source-generation.md) * [Add components to an entity](components-add-to-entity.md) * [Remove components from an entity](components-remove-from-entity.md) * [Read and write component values](components-read-and-write.md) * [Native container component support](components-nativecontainers.md) + * [Aspect overview](aspects-intro.md) + * [Aspect concepts](aspects-concepts.md) + * [Create an aspect](aspects-create.md) + * [Aspect source generation](aspects-source-generation.md) * [Systems overview](systems-overview.md) * [Introduction to systems](systems-intro.md) * [Systems comparison](systems-comparison.md) @@ -71,9 +71,14 @@ * [SystemBase overview](systems-systembase.md) * [Defining and managing system data](systems-data.md) * [System groups](systems-update-order.md) + * [Manage systems in multiple worlds](systems-icustombootstrap.md) * [Working with systems](systems-working.md) * [SystemAPI](systems-systemapi.md) - * [Scheduling data changes with an EntityCommandBuffer](systems-entity-command-buffers.md) + * [Schedule data changes](systems-schedule-changes.md) + * [Entity command buffer overview](systems-entity-command-buffers.md) + * [Use an entity command buffer](systems-entity-command-buffer-use.md) + * [Entity command buffer playback](systems-entity-command-buffer-playback.md) + * [Automatic playback and disposal of entity command buffers](systems-entity-command-buffer-automatic-playback.md) * [Iterate over component data](systems-iterating-data-intro.md) * [Iterate over component data with SystemAPI.Query](systems-systemapi-query.md) * [Iterate over component data with IJobEntity](iterating-data-ijobentity.md) @@ -81,6 +86,10 @@ * [Implement IJobChunk](iterating-data-ijobchunk-implement.md) * [Iterate manually over data](iterating-manually.md) * [Iterate over component data with Entities.ForEach](iterating-data-entities-foreach.md) + * [Define and execute an Entites.ForEach lambda expression](iterating-entities-foreach-define.md) + * [Select and access data](iterating-entities-foreach-select-data.md) + * [Filter data](iterating-entities-foreach-filtering.md) + * [Use entity command buffers](iterating-entities-foreach-ecb.md) * [Query data with an entity query](systems-entityquery.md) * [EntityQuery overview](systems-entityquery-intro.md) * [Create an EntityQuery](systems-entityquery-create.md) @@ -106,7 +115,6 @@ * [Transforms in Entities](transforms-intro.md) * [Transforms concepts](transforms-concepts.md) * [Using transforms](transforms-using.md) - * [Transform aspect](transform-aspect.md) * [Blob assets](blob-assets-intro.md) * [Blob assets concepts](blob-assets-concept.md) * [Create a blob asset](blob-assets-create.md) @@ -117,6 +125,7 @@ * [System group allocators](allocators-system-group.md) * [Working in the Editor](editor-workflows.md) * [Entities Preferences reference](editor-preferences.md) + * [Entities Project Settings reference](editor-project-settings.md) * [Working with authoring and runtime data](editor-authoring-runtime.md) * [Entities windows](editor-entities-windows.md) * [Archetypes window](editor-archetypes-window.md) @@ -129,6 +138,13 @@ * [Component Inspector reference](editor-component-inspector.md) * [Query window reference](editor-query-window.md) * [Content management](content-management.md) + * [Introduction to content management](content-management-intro.md) + * [Weakly reference an object](content-management-get-a-weak-reference.md) + * [Weakly reference a scene](content-management-get-a-weak-reference-scene.md) + * [Load a weakly-referenced object at runtime](content-management-load-an-object.md) + * [Load a weakly-referenced scene at runtime](content-management-load-a-scene.md) + * [Create custom content archives](content-management-create-content-archives.md) + * [Deliver content to an application](content-management-delivery.md) * [Performance and debugging](performance-debugging.md) * [Entities Profiler modules](profiler-modules-entities.md) * [Entities Structural Changes Profiler module](profiler-module-structural-changes.md) diff --git a/Documentation~/aspects-concepts.md b/Documentation~/aspects-concepts.md index 646bf055..2d7dc108 100644 --- a/Documentation~/aspects-concepts.md +++ b/Documentation~/aspects-concepts.md @@ -2,8 +2,6 @@ An aspect is an object-like wrapper that you can use to group together a subset of an entity's components into a single C# struct. Aspects are useful for organizing component code and simplifying queries in your systems. Unity provides predefined aspects for groups of related components or you can define your own with the [`IAspect`](xref:Unity.Entities.IAspect) interface. -For example, [`TransformAspect`](xref:Unity.Transforms.TransformAspect) groups together the `LocalTransform`, `WorldTransform`, and `ParentTransform` components. It also provides several utility methods for acting on these components. The utility methods are accessible from any query or `IJobEntity` that includes `TransformAspect`. - Aspects can include items such as the following: * A single `Entity` field to store the entity's ID @@ -17,4 +15,3 @@ Aspects can include items such as the following: * [Create an aspect](aspects-create.md) * [`IAspect` API documentation](xref:Unity.Entities.IAspect) -* [`TransformAspect` API documentation](xref:Unity.Transforms.TransformAspect) \ No newline at end of file diff --git a/Documentation~/aspects-intro.md b/Documentation~/aspects-intro.md index 805c0d28..fb640eb4 100644 --- a/Documentation~/aspects-intro.md +++ b/Documentation~/aspects-intro.md @@ -6,8 +6,9 @@ This section explains how to use aspects within your project. |**Topic**|**Description**| |---|---| -|[Aspect overview](aspects-concepts.md)|Overview of aspects.| -|[Create an aspect](aspects-create.md)|How to create an aspect with the `IAspect` interface.| +|[Aspect overview](aspects-concepts.md)|Understand how aspects work.| +|[Create an aspect](aspects-create.md)|Create an aspect with the `IAspect` interface.| +|[Aspect source generation](aspects-source-generation.md)|Use aspect source generators.| ## Further information diff --git a/Documentation~/blob-assets-create.md b/Documentation~/blob-assets-create.md index c9d22beb..43cd9fda 100644 --- a/Documentation~/blob-assets-create.md +++ b/Documentation~/blob-assets-create.md @@ -78,25 +78,3 @@ The previous example let the baker handle all de-duplication, but that means you To do this, you can use a custom hash instead of letting the baker generate one. If multiple bakers either have access to, or generate the same hash for the same blob assets, you can use this hash to de-duplicate before generating a blob asset. Use [TryGetBlobAssetReference](xref:Unity.Entities.IBaker.TryGetBlobAssetReference*) to check if the custom hash is already registered to the baker: [!code-cs[blobs](../DocCodeSamples.Tests/BlobAssetBakingExamples.cs#CustomHashBlobAssetBaker)] - -## Blob assets in baking systems - -Baking systems are outside the control of a baker, so they aren't automatically incrementally correct. To make it correct, you need to manually ensure that components and blob assets are reverted and disposed correctly when the baking system is rerun. - -To do this, use the [ICleanupComponent](xref:Unity.Entities.ICleanupComponentData) component. Because this component is not automatically removed when an entity is removed, you can use it to cleanup after recently destroyed entities. This means that the baking system can manually revert when it's re-run. This allows the baking system to dispose the blob assets from the previous run: - -[!code-cs[blobs](../DocCodeSamples.Tests/BlobAssetBakingExamples.cs#BlobAssetBakingSystemSetup)] - -The hash is stored in the `ICleanupComponent` when the blob asset is created. The hash is used to dispose the blob asset when the baking system is reverted. - -The baking system needs to handle the following to be incrementally correct: - -* Newly created entities. These need to receive the `ICleanupComponent` and the new blob asset needs to be added. -* Newly removed entities. The `ICleanupComponent` needs to be removed and the old blob asset needs to be released. -* Entities with changed blob assets. The old blob asset needs to be released and the new one added. - -> [!NOTE] -> If the blob asset is also referenced somewhere else, it won't be added or released respectively. Instead the refcounting is updated accordingly. - - -[!code-cs[blobs](../DocCodeSamples.Tests/BlobAssetBakingExamples.cs#BlobAssetBakingSystem)] diff --git a/Documentation~/components-buffer-command-buffer.md b/Documentation~/components-buffer-command-buffer.md index db844fd2..fb6ba19a 100644 --- a/Documentation~/components-buffer-command-buffer.md +++ b/Documentation~/components-buffer-command-buffer.md @@ -1,40 +1,20 @@ -# Modify dynamic buffers with an `EntityCommandBuffer` +# Modify dynamic buffers with an entity command buffer -An [`EntityCommandBuffer`](xref:Unity.Entities.EntityCommandBuffer) records commands to add, remove, or set buffer components for entities. There are dynamic buffer-specific APIs that are different than the regular component APIs. +An [`EntityCommandBuffer`](xref:Unity.Entities.EntityCommandBuffer) (ECB) records commands to add, remove, or set buffer components for entities. There are dynamic buffer-specific APIs that are different than the regular component APIs. -The following code sample walks through some general dynamic buffer-specific `EntityCommandBuffer` APIs. It assumes a dynamic buffer called `MyElement` exists. +An ECB can only record commands to happen in the future, so it can only manipulate dynamic buffer components in the following ways: -```csharp -private void Example(Entity e, Entity otherEntity) -{ - EntityCommandBuffer ecb = new EntityCommandBuffer(Allocator.TempJob); +* [`SetBuffer`](xref:Unity.Entities.EntityCommandBuffer.SetBuffer*): Returns a `DynamicBuffer` that the recording thread can populate with data. At playback, these buffer contents overwrite any existing buffer contents. `SetBuffer` doesn’t fail if the target entity already contains the buffer component. If more than one thread records a `SetBuffer` command on the same entity, after playback only the contents added by the last command according to `sortKey` order are visible. `SetBuffer` has the same functionality as [`AddBuffer`](xref:Unity.Entities.EntityCommandBuffer.AddBuffer*), except `AddBuffer` adds the buffer to the component first, if it doesn't exist. +* [`AppendToBuffer`](xref:Unity.Entities.EntityCommandBuffer.AppendToBuffer*): Appends a single buffer element to an existing buffer component on an entity, and preserves any existing buffer contents. Multiple threads can safely append to the same buffer component on the same entity and the `sortKey` of the recorded commands determines the order of the resulting elements. `AppendToBuffer` fails at playback if the target entity doesn’t contain a buffer component of type `T`. Therefore, it's best practice to precede every `AppendToBuffer` command with `AddComponent`, to ensure that the target buffer component is present. +* You can safely use the [`AddComponent`](xref:Unity.Entities.EntityCommandBuffer.AddComponent*) and [`RemoveComponent`](xref:Unity.Entities.EntityCommandBuffer.RemoveComponent*) methods if `T` is an `IBufferElementData` to add an empty buffer or remove an existing buffer. These methods are safe to use from multiple threads and adding an existing component or removing a non-existent component doesn't cause an error. - // Record a command to remove the MyElement dynamic buffer from an entity. - ecb.RemoveComponent(e); +The following code example walks through some general dynamic buffer-specific `EntityCommandBuffer` APIs. It assumes a dynamic buffer called `MyElement` exists. - // Record a command to add a MyElement dynamic buffer to an existing entity. - // The data of the returned DynamicBuffer is stored in the EntityCommandBuffer, - // so changes to the returned buffer are also recorded changes. - DynamicBuffer myBuff = ecb.AddBuffer(e); - - // After playback, the entity will have a MyElement buffer with - // Length 20 and these recorded values. - myBuff.Length = 20; - myBuff[0] = new MyElement { Value = 5 }; - myBuff[3] = new MyElement { Value = -9 }; - - // SetBuffer is like AddBuffer, but safety checks will throw an exception at playback if - // the entity doesn't already have a MyElement buffer. - DynamicBuffer otherBuf = ecb.SetBuffer(otherEntity); - - // Records a MyElement value to append to the buffer. Throws an exception at - // playback if the entity doesn't already have a MyElement buffer. - ecb.AppendToBuffer(otherEntity, new MyElement { Value = 12 }); -} -``` +[!code-cs[Dynamic buffer in ECB](../DocCodeSamples.Tests/CreateComponentExamples.cs#dynamicbuffer.ecb)] When you set the `Length`, `Capacity`, and content of the `DynamicBuffer`, ECS records those changes into the `EntityCommandBuffer`. When you play back the `EntityCommandBuffer`, ECS makes the changes to the dynamic buffer. ## Additional resources -* [Access dynamic buffers from jobs](components-buffer-jobs.md) \ No newline at end of file +* [Access dynamic buffers from jobs](components-buffer-jobs.md) +* [Entity command buffer overview](systems-entity-command-buffers.md) \ No newline at end of file diff --git a/Documentation~/content-management-create-content-archives.md b/Documentation~/content-management-create-content-archives.md new file mode 100644 index 00000000..00ea7450 --- /dev/null +++ b/Documentation~/content-management-create-content-archives.md @@ -0,0 +1,121 @@ +# Create custom content archives + +Unity automatically creates content archives to store every object referenced from the [subscenes](conversion-subscenes.md) included in the build. For more information, refer to [How Unity generates content archives](content-management-intro.md#how-unity-generates-content-archives). This covers the content management requirements of many applications, but you can also create additional content archives that you can load independently at runtime. This can be useful to structure downloadable content, reduce the initial install size of your application, or load assets optimized for the end user’s platform. + +The process to create a custom content archive that you can load into Unity at runtime has the following steps: + +* Build: Unity finds all the objects that should go into the content archive and copies the object files into a target directory. +* Publish: Unity takes all the files in the directory and organizes them into a structure that allows Unity to directly install the files onto the target platform. To do this, Unity renames the files to be a hash of the contents of the file. It then builds a catalog alongside the content archive to map the files to their original object. + +The results of this process are a content archive to store the files, and a catalog to enable Unity to find the correct file for an object. + +The Unity Editor contains menu items that you can use to build and publish your own content archives. They take a set of subscenes, extract the objects that the subscenes reference, and build the objects into a content archive. You can use the menu items on a specific list of subscenes to add additional content to your application, or on the subscenes specified in [Build Settings](xref:UnityEditor.EditorBuildSettings.scenes) to rebuild all the content for the application's Standalone Player without needing to rebuild the Player itself. + +## Build content archives for a specific set of subscenes + +To build content archives for the objects in a specific set of scenes, use a [Build Configuration](https://docs.unity3d.com/Packages/com.unity.platforms@latest?subfolder=/api/Unity.Build.BuildConfiguration.html) asset. The Build Configuration asset can specify a list of scenes and a target platform to build content archives for. To use it to build content archives for a set of subscenes: + +1. Create an empty build configuration (menu: **Assets** > **Create** > **Build Configuration** > **Empty Build Configuration**). +2. In the [Project window](xref:ProjectView), select the new build configuration and view it in the [Inspector window](xref:UsingTheInspector). +3. Select **Add Component** > **Unity.Build.Common** > **Scene List**. +4. In the new **Scene List** section, add the subscene assets you want to build content archives for to the **Scene Infos** list. +5. Select **Add Component** > **Unity.Build.Classic** > **Classic Build Profile**. +6. In the new **Classic Build Profile** section, set **Platform** to the platform that you want to build content archives for. +7. With the build configuration still selected, select **Assets** > **Publish** > **Content Update**. This begins the content archive build and publish process. After this finishes, the result content archive and catalog will be in your [Streaming Assets](xref:StreamingAssets) folder. + +## Rebuild the content for your application's Player + +During the Player build process, Unity automatically generates content archives to store all the objects referenced from the [subscenes](conversion-subscenes.md) included in the build. For more information, refer to [How Unity generates content archives](content-management-intro.md#how-unity-generates-content-archives). To improve iteration time, Unity can generate this same set of content archives without building a new Player. You can then update the content in your application with the new content archives. To do this: + +1. Deselect any Build Configuration assets. If you have one selected, Unity instead builds content archives for the subscenes referenced from the Build Configuration. +2. Select **Assets** > **Publish** > **Content Update**. This begins the content archive build and publish process. After this finishes, the result content archive and catalog will be in your [Streaming Assets](xref:StreamingAssets) folder. + +## Build content archives from a C# script + +If the menu items don't provide enough control over which objects to include in custom content archives, you can use the content archive build and publish APIs to create content archives from a C# script. + +The following code example shows how to build and publish a content update. + +```C# +using System; +using System.Collections.Generic; +using System.IO; +using Unity.Build.Classic; +using Unity.Collections; +using Unity.Scenes.Editor; +using UnityEditor.Experimental; +using UnityEngine; +using UnityEditor; +using System.Linq; +using Unity.Build; +using Unity.Build.Common; +using Unity.Entities.Build; +using Unity.Entities.Content; + +static class BuildUtilities +{ + //prepares the content files for publish. The original files can be deleted or retained during this process by changing the last parameter of the PublishContent call. + static void PublishExistingBuild() + { + var buildFolder = EditorUtility.OpenFolderPanel("Select Build To Publish", + Path.GetDirectoryName(Application.dataPath), "Builds"); + if (!string.IsNullOrEmpty(buildFolder)) + { + var streamingAssetsPath = $"{buildFolder}/{PlayerSettings.productName}_Data/StreamingAssets"; + //the content sets are defined by the functor passed in here. + RemoteContentCatalogBuildUtility.PublishContent(streamingAssetsPath, + $"{buildFolder}-RemoteContent", + f => new string[] { "all" }, true); + } +} + //This method is somewhat complicated because it will build the scenes from a player build but without fully building the player. + static void CreateContentUpdate() + { + var buildFolder = EditorUtility.OpenFolderPanel("Select Build To Publish", + Path.GetDirectoryName(Application.dataPath), "Builds"); + if (!string.IsNullOrEmpty(buildFolder)) + { + var buildTarget = EditorUserBuildSettings.activeBuildTarget; + var tmpBuildFolder = Path.Combine(Path.GetDirectoryName(Application.dataPath), + $"/Library/ContentUpdateBuildDir/{PlayerSettings.productName}"); + + var instance = DotsGlobalSettings.Instance; + var playerGuid = instance.GetPlayerType() == DotsGlobalSettings.PlayerType.Client ? instance.GetClientGUID() : instance.GetServerGUID(); + if (!playerGuid.IsValid) + throw new Exception("Invalid Player GUID"); + + var subSceneGuids = new HashSet(); + for (int i = 0; i < EditorBuildSettings.scenes.Length; i++) + { + var ssGuids = EditorEntityScenes.GetSubScenes(EditorBuildSettings.scenes[i].guid); + foreach (var ss in ssGuids) + subSceneGuids.Add(ss); + } + var artifactKeys = new Dictionary(); + var binaryFiles = new EntitySectionBundlesInBuild(); + + EntitySceneBuildUtility.PrepareEntityBinaryArtifacts(playerGuid, subSceneGuids, artifactKeys); + binaryFiles.Add(artifactKeys.Keys, artifactKeys.Values); + var entitySceneGUIDs = binaryFiles.SceneGUIDs.ToArray(); + + EntitySceneBuildUtility.PrepareAdditionalFiles(default, artifactKeys.Keys.ToArray(), + artifactKeys.Values.ToArray(), buildTarget, (s, d) => DoCopy(s, Path.Combine(tmpBuildFolder, d))); + + var publishFolder = Path.Combine(Path.GetDirectoryName(Application.dataPath), "Builds", $"{buildFolder}-RemoteContent"); + PublishContent(tmpBuildFolder, publishFolder, f => new string[] { "all" }); + } + } +} +``` + + + +## Deliver the content + +After you create custom content archives, you can deliver them to the application at runtime. The content archive build and publish process structures the content so that the content mirrors the structure of the local device cache. This makes the content delivery process simpler because you can load content into your application directly from content archives on the local device, or from an online content delivery service. For information on how to do this, refer to [Deliver content to an application](content-management-delivery.md). + +## Additional resources + +* [Deliver content to an application](content-management-delivery.md) + + \ No newline at end of file diff --git a/Documentation~/content-management-delivery.md b/Documentation~/content-management-delivery.md new file mode 100644 index 00000000..fd51bb04 --- /dev/null +++ b/Documentation~/content-management-delivery.md @@ -0,0 +1,19 @@ +# Deliver content to an application + +To deliver content updates for your application, you create custom content archives and load them into the application from a content delivery service, or from the local device. The [ContentDeliveryService](xref:Unity.Entities.Content.ContentDeliveryService) APIs provide a unified workflow for loading both local and online content on demand. You can use it to load content, check the delivery status, and conditionally run code depending on the content available. + +## Set up content delivery + +To enable content delivery for your project, set the `ENABLE_CONTENT_DELIVERY` scripting symbol. For information on how to do this, refer to [Custom scripting symbols](xref:CustomScriptingSymbols). + +## Load content + +The `ContentDeliveryService` APIs load content by URL. You can pass the URL of content stored on an online content delivery service or the local URL of a content archive on the device. After you begin to download the content, you must wait until Unity finishes installing and caching the content. Then, you can load objects from the installed content in the same way you access local content, by weak reference ID. For more information, refer to [Load a weakly-referenced object at runtime](content-management-load-an-object.md). + +The following code sample shows how to load content by URL, wait for content delivery to complete, then continue with the application logic. It can use a MonoBehaviour rather than a [system](concepts-systems.md) because the example code performs a full content update once before the application starts. You can also use a system's update method to do this, but the content delivery system APIs aren't Burst-compiled, and you can't use the APIs from jobs, so there is no performance benefit. + +[!code-cs[](../DocCodeSamples.Tests/content-management/DeliverContent.cs#example)] + +## Additional resources + +* [Create custom content archives](content-management-create-content-archives.md) \ No newline at end of file diff --git a/Documentation~/content-management-get-a-weak-reference-scene.md b/Documentation~/content-management-get-a-weak-reference-scene.md new file mode 100644 index 00000000..67e4856f --- /dev/null +++ b/Documentation~/content-management-get-a-weak-reference-scene.md @@ -0,0 +1,17 @@ +# Weakly reference a scene + +Weak references to scenes work in the same way as weak references to objects. For more information, refer to [Weakly reference an object](content-management-get-a-weak-reference.md). + +Unity uses an `UntypedWeakReferenceId` to weakly reference [scenes](xref:CreatingScenes) so, to weakly reference a scene from a C# script, you can use the same workflow as described in [Weakly reference an object from a C# script](content-management-get-a-weak-reference.md#weakly-reference-an-object-from-a-c-script). + +## Weakly reference a scene from the Inspector + +The [RuntimeContentManager](xref:Unity.Entities.Content.RuntimeContentManager) has a specific set of APIs to manage weakly-referenced scenes at runtime. This means that the[WeakObjectReference](xref:Unity.Entities.Content.WeakObjectReference`1) wrapper doesn't work for scenes. The scene equivalent to this wrapper is [WeakObjectSceneReference](xref:Unity.Entities.Content.WeakObjectSceneReference) which provides the same runtime and editor workflow benefits as `WeakObjectReference`, but for scenes. + +To weakly reference a scene from the Inspector, substitute `WeakObjectSceneReference` for `WeakObjectReference` in the [Weakly reference an object from the Inspector](content-management-get-a-weak-reference.md#weakly-reference-an-object-from-the-inspector) workflow. The following code sample shows how to do this. + +[!code-cs[](../DocCodeSamples.Tests/content-management/WeaklyReferenceSceneFromInpsector.cs#example)] + +## Additional resources + +* [Weakly reference an object](content-management-get-a-weak-reference.md) \ No newline at end of file diff --git a/Documentation~/content-management-get-a-weak-reference.md b/Documentation~/content-management-get-a-weak-reference.md new file mode 100644 index 00000000..d38fe93e --- /dev/null +++ b/Documentation~/content-management-get-a-weak-reference.md @@ -0,0 +1,27 @@ +# Weakly reference an object + +A [weak object reference](content-management-intro.md#weakly-referenced-objects) is a handle to an object that is valid regardless of whether the object is loaded or unloaded. If you create a weak reference to an object, Unity includes that object in a content archive which makes the object available to you at runtime. You can then use the [RuntimeContentManager](xref:Unity.Entities.Content.RuntimeContentManager) API to load, use, and release the weakly-referenced objects. + +The content management system provides a way to weakly reference an object via the Inspector and via a C# script. + +## Weakly reference an object from the Inspector + +The [WeakObjectReference](xref:Unity.Entities.Content.WeakObjectReference`1) struct provides a wrapper around the `RuntimeContentManager` APIs that are responsible for managing weakly-referenced objects. It also makes it possible to create a weak reference to an object via the Inspector. The Inspector property drawer for a `WeakObjectReference` is an object field that you can drag objects onto. Internally, Unity generates a weak reference to the object you assign, which you can then pass to [ECS components](concepts-components.md) during the [baking process](baking.md). + +The `WeakObjectReference` wrapper also makes it easier to manage individual weakly-referenced objects at runtime. It provides methods and properties to load, use, and release the object that it weakly references. + +The following code sample shows how to create a [Baker](xref:Unity.Entities.Baker`1) that passes a `WeakObjectReference` of a [mesh asset](xref:class-Mesh) to an [unmanaged component](components-unmanaged.md). The `mesh` property appears in the Inspector of the MeshRefSample component as an object field that you can assign a mesh asset to. + +[!code-cs[](../DocCodeSamples.Tests/content-management/WeaklyReferenceFromInspector.cs#example)] + +## Weakly reference an object from a C# script + +The `RuntimeContentManager` APIs manage [weakly-referenced objects](content-management-intro.md#weakly-referenced-objects) by an [UntypedWeakReferenceId](xref:Unity.Entities.Serialization.UntypedWeakReferenceId). + +The following code sample shows how to get the `UntypedWeakReferenceId` of the objects currently selected in the [Project window](xref:ProjectView). To make Unity include these objects in content archives, you must bake the weak reference IDs into an ECS component. To pass weak reference IDs created in Editor scripts to a baker, you can use a [Scriptable Object](xref:class-ScriptableObject). An Editor script can write weak reference IDs to a ScriptableObject then later, during the baking process, a baker can read the IDs from the ScriptableObject and write them into an ECS component. + +[!code-cs[](../DocCodeSamples.Tests/content-management/WeaklyReferenceFromScript.cs#example)] + +## Additional resources + +* [Load a weakly-referenced object at runtime](content-management-load-an-object.md) \ No newline at end of file diff --git a/Documentation~/content-management-intro.md b/Documentation~/content-management-intro.md new file mode 100644 index 00000000..de86e232 --- /dev/null +++ b/Documentation~/content-management-intro.md @@ -0,0 +1,40 @@ +# Introduction to content management + +For applications built with the Entities package, Unity stores assets, scenes, and other objects in content archives. Unity builds these content archives automatically during the Player build process and loads, uses, and releases the objects within them at runtime when the objects are required. This all happens internally, but Unity also provides APIs that you can use to interface with the content management system yourself. + +This content archive-based content management system was designed to provide optimal performance in data-oriented Entities-based applications without much setup. It provides [Burst-compiled](https://docs.unity3d.com/Packages/com.unity.burst@latest) and thread-safe APIs that you can use within ECS [systems](concepts-systems.md), multithreaded job code, and, in some cases, MonoBehaviour components. The other content management solutions in Unity, such as [Resources](xref:UnityEngine.Resources), [AssetBundles](xref:AssetBundlesIntro), and [Addressables](https://docs.unity3d.com/Packages/com.unity.addressables@latest), either don't work with Entities or are suboptimal solutions for data-oriented applications. This means that it's best practice to use this content archive-based content management system for applications that use Entities. + +## How Unity generates content archives + +During the Player build process, Unity generates content archives to store every object referenced in the [subscenes](conversion-subscenes.md) included in the build. Unity generates at least one content archive per subscene that references objects. If multiple subscenes reference the same object, Unity moves the object into a shared content archive. You can reference objects in the following ways: + +* With a [strong reference](#strongly-referenced-objects): When you directly assign an object to a Monobehaviour component property in a subscene. +* With a [weak reference](#weakly-referenced-objects): When you pass an object's [UntypedWeakReferenceId](xref:Unity.Entities.Serialization.UntypedWeakReferenceId) to an ECS component during the [baking process](baking.md). + +Unity bundles both strongly-referenced and weakly-referenced objects into the same content archives, but handles them differently at runtime. + +### Strongly-referenced objects + +Strongly-referenced objects are objects that you directly assign to a property of a MonoBehaviour component in the subscene. For example, if you assign a [mesh object](xref:class-Mesh) to a [Mesh Filter](xref:class-MeshFilter) component, Unity considers the mesh object to be strongly referenced. + +At runtime, Unity automatically loads strongly-referenced objects when they're required, keeps track of where they're used, and unloads them when they're no longer needed. You don't need to worry about the asset lifecycle of any objects that you reference this way. + +### Weakly-referenced objects + +Weakly-referenced objects are objects that Unity detects an [UntypedWeakReferenceId](xref:Unity.Entities.Serialization.UntypedWeakReferenceId) to during the baking process. To weakly-reference an object, you pass an `UntypedWeakReferenceId`, or a [WeakObjectReference](xref:Unity.Entities.Content.WeakObjectReference`1) which is a wrapper for an `UntypedWeakReferenceId`, to an [ECS component](concepts-components.md) from a [Baker](xref:Unity.Entities.Baker`1). For more information, refer to [Weakly-reference an object](content-management-get-a-weak-reference.md) + +At runtime, you are responsible for the lifecycle of weakly-referenced objects. You must use the content management APIs to load them before you need to use them, and then release them when you don't need them anymore. Unity counts the number of references to each weakly-referenced object and when you release the last instance, Unity unloads the object. + +## The content management workflow + +In the simplest content management workflow, you: + +1. Weakly reference an object and bake the reference into a component. For information on how to do this, refer to [Weakly reference an object](content-management-get-a-weak-reference.md). +2. Use the weak reference to load, use, and release the object at runtime. For more information, refer to [Load a weakly-referenced object at runtime](content-management-load-an-object.md) and [Load a weakly-referenced scene at runtime](content-management-load-a-scene.md) + +Unity automatically adds any object that you weakly reference from a subscene to a content archive, so you don't need to do any additional work to make the object available to load at runtime. However, to support more advanced use cases, you can also create custom content archives and load them either from the local device or from a remote content delivery service. This can help you structure additional downloadable content, reduce the initial install size of the application, or load assets optimized for the end user's platform. For more information, refer to [Create custom content archives](content-management-create-content-archives.md). + +## Additional resources + +* [Weakly reference an object](content-management-get-a-weak-reference.md) +* [Load a weakly-referenced object at runtime](content-management-load-an-object.md) \ No newline at end of file diff --git a/Documentation~/content-management-load-a-scene.md b/Documentation~/content-management-load-a-scene.md new file mode 100644 index 00000000..bf5940d7 --- /dev/null +++ b/Documentation~/content-management-load-a-scene.md @@ -0,0 +1,21 @@ +# Load a weakly-referenced scene at runtime + +Unity doesn't automatically load weakly-referenced scenes. This means you can't use [SceneManager](xref:UnityEngine.SceneManagement.SceneManager) APIs such as [LoadScene](xref:UnityEngine.SceneManagement.SceneManager.LoadScene(System.Int32,UnityEngine.SceneManagement.LoadSceneMode)) to open the scene. Instead, you must use [RuntimeContentManager](xref:Unity.Entities.Content.RuntimeContentManager) APIs to load the scene from the content archive. To load a scene at runtime, you must have a weak reference to the scene stored in an [ECS component](concepts-components.md). The weak reference can be either a [WeakObjectSceneReference](xref:Unity.Entities.Content.WeakObjectSceneReference) or an [UntypedWeakReferenceId](xref:Unity.Entities.Serialization.UntypedWeakReferenceId). For information on how to store a weak reference to an object, refer to [Weakly reference a scene](content-management-get-a-weak-reference-scene.md). + +## Load a scene at runtime with a WeakObjectSceneReference + +The following code example shows how to use `WeakObjectSceneReferences` to load scenes from an [`ISystem`](systems-isystem.md). For information on how to pass a `WeakObjectSceneReference` to a component, refer to [Weakly reference a scene](content-management-get-a-weak-reference-scene.md). + +[!code-cs[](../DocCodeSamples.Tests/content-management/LoadSceneWeakObjectReference.cs#example)] + + +## Load a scene at runtime with an UntypedWeakReferenceId + +The following code example shows how to use the [RuntimeContentManager](xref:Unity.Entities.Content.RuntimeContentManager) APIs and an `UntypedWeakReferenceId` to load a scene from an [`ISystem`](systems-isystem.md). For information on how to pass a `UntypedWeakReferenceId` to a component, refer to [Weakly reference an object from a C# script](content-management-get-a-weak-reference.md#weakly-reference-an-object-from-a-c-script). + + +[!code-cs[](../DocCodeSamples.Tests/content-management/LoadSceneUntypedWeakReferenceId.cs#example)] + +## Additional resource + +* [Load a weakly-referenced object at runtime](content-management-load-an-object.md) \ No newline at end of file diff --git a/Documentation~/content-management-load-an-object.md b/Documentation~/content-management-load-an-object.md new file mode 100644 index 00000000..cc48a882 --- /dev/null +++ b/Documentation~/content-management-load-an-object.md @@ -0,0 +1,19 @@ +# Load a weakly-referenced object at runtime + +Unity doesn't automatically load weakly-referenced objects, so it's your responsibility to load them before you need them. To load an object at runtime, you must have a weak reference to the object stored in an [ECS component](concepts-components.md). The weak reference can be either a [WeakObjectReference](xref:Unity.Entities.Content.WeakObjectReference`1) or an [UntypedWeakReferenceId](xref:Unity.Entities.Serialization.UntypedWeakReferenceId). For information on how to store a weak reference to an object, refer to [Weakly reference an object](content-management-get-a-weak-reference.md). + +## Load an object at runtime with a WeakObjectReference + +The following code example shows how to use `WeakObjectReferences` to load and render a mesh with a material from an [ISystem](systems-isystem.md). For information on how to pass a `WeakObjectRefrence` to a component, refer to [Weakly reference an object from the Inspector](content-management-get-a-weak-reference.md#weakly-reference-an-object-from-the-inspector). + +[!code-cs[](../DocCodeSamples.Tests/content-management/LoadObjectWeakObjectReference.cs#example)] + +## Load an object at runtime with an UntypedWeakReferenceId + +The following code example shows how to use the [RuntimeContentManager](xref:Unity.Entities.Content.RuntimeContentManager) APIs and `UntypedWeakReferenceIds` to load and render a mesh with a material from an [ISystem](systems-isystem.md). For information on how to pass a `UntypedWeakReferenceId` to a component, refer to [Weakly reference an object from a C# script](content-management-get-a-weak-reference.md#weakly-reference-an-object-from-a-c-script). + +[!code-cs[](../DocCodeSamples.Tests/content-management/LoadObjectUntypedWeakReferenceId.cs#example)] + +## Additional resources + +* [Load a weakly-referenced scene at runtime](content-management-load-a-scene.md) \ No newline at end of file diff --git a/Documentation~/content-management.md b/Documentation~/content-management.md index c0022e61..c9b9065d 100644 --- a/Documentation~/content-management.md +++ b/Documentation~/content-management.md @@ -1,194 +1,20 @@ # Content management -You can use the [`RuntimeContentManger`](xref:Unity.Entities.Content.RuntimeContentManager) API to load and release Unity engine objects, and GameObject scene from [systems](systems-intro.md) or MonoBehaviour code. +Unity's entity component system (ECS) includes its own content management and delivery system. This system provides a performant interface to load and release Unity objects and scenes in a data-oriented application. The API is available to both ECS [systems](concepts-systems.md) and MonoBehaviour code which means you can use it in [Bakers](baking.md). -## Main content APIs +> [!NOTE] +> This system is built on top of Unity's [ContentLoadModule](https://docs.unity3d.com/2023.1/Documentation/ScriptReference/UnityEngine.ContentLoadModule.html) assembly. -The primary APIs for objects are: -* [`LoadObjectAsync`](xref:Unity.Entities.Content.RuntimeContentManager.LoadObjectAsync*) -* [`GetObjectLoadingStatus`](xref:Unity.Entities.Content.RuntimeContentManager.GetObjectLoadingStatus*) -* [`GetObjectValue`](Unity.Entities.Content.RuntimeContentManager.GetObjectValue*) -* [`ReleaseObjectAsync`](xref:Unity.Entities.Content.RuntimeContentManager.ReleaseObjectAsync*) +| **Topic** | **Description** | +| ------------------------------------------------------------ | ------------------------------------------------------------ | +| [Introduction to content management](content-management-intro.md) | Understand how content management works for entities-based applications. | +| [Weakly reference an object](content-management-get-a-weak-reference.md) | Get a weak reference to an object so you can load and use the object at runtime. | +| [Weakly reference a scene](content-management-get-a-weak-reference-scene.md) | Get a weak reference to a scene so you can load and use the scene at runtime. | +| [Load a weakly-referenced object at runtime](content-management-load-an-object.md) | Use a [system](concepts-systems.md) to load an object from a content archive. | +| [Load a weakly-referenced scene at runtime](content-management-load-a-scene.md) | Use a system to load a scene, and everything it contains, from a content archive. | +| [Create custom content archives](content-management-create-content-archives.md) | Create your own content archives to store objects ready for delivery to your application. | +| [Deliver content to an application](content-management-delivery.md) | Load content archives into your application at runtime. | -The primary APIs for scenes are: -* [`LoadSceneAsync`](xref:Unity.Entities.Content.RuntimeContentManager.LoadSceneAsync*) -* [`GetSceneLoadingStatus`](xref:Unity.Entities.Content.RuntimeContentManager.GetSceneLoadingStatus*) -* [`GetSceneValue`](xref:Unity.Entities.Content.RuntimeContentManager.GetSceneValue*) -* [`ReleaseScene`](xref:Unity.Entities.Content.RuntimeContentManager.ReleaseScene*) +## Additional resources -Both scenes and objects are reference counted and released once there are no longer any references to them. - -This example illustrates using `RuntimeContentManger` to load and render a mesh with a material from an ECS system: - -```c# -using Unity.Entities; -using Unity.Entities.Content; -using Unity.Transforms; -using UnityEngine; - -public struct DecorationVisualComponentData : IComponentData -{ - public bool startedLoad; - public WeakObjectReference mesh; - public WeakObjectReference material; -} - -[WorldSystemFilter(WorldSystemFilterFlags.Default | WorldSystemFilterFlags.Editor)] -[UpdateInGroup(typeof(PresentationSystemGroup))] -public partial struct DecorationRenderSystem : ISystem -{ - public void OnCreate(ref SystemState state) { } - public void OnDestroy(ref SystemState state) { } - public void OnUpdate(ref SystemState state) - { - foreach (var (transform, dec) in SystemAPI.Query, RefRW>()) - { - if (!dec.ValueRW.startedLoad) - { - dec.ValueRW.mesh.LoadAsync(); - dec.ValueRW.material.LoadAsync(); - } - - if (dec.ValueRW.mesh.LoadingStatus == ObjectLoadingStatus.Completed && - dec.ValueRW.material.LoadingStatus == ObjectLoadingStatus.Completed) - { - Graphics.DrawMesh(dec.ValueRO.mesh.Result, - transform.ValueRO.Value, dec.ValueRO.material.Result, 0); - } - } - } -} -``` - -## Content building - -Referenced engine objects are automatically collected for each [subscene](conversion-subscenes.md) during the import process. During a build, Unity combines these object references from the scenes included in the build and builds them into [`ContentArchives`](xref:Unity.Entities.Content.ContentArchivesBuildUtility). Any `UntypedWeakReferenceId` that's serialized into entity data is included in the build. - -For convenience and better Editor workflows, you can use the [`WeakObjectReference`](xref:Unity.Entities.Content.WeakObjectReference`1) and `WeakSceneReference` types in a `MonoBehaviour` and copy them to the `ComponentData` from the [`Baker`](xref:Unity.Entities.Baker`1). - -Here is an example of [baking](baking.md) the reference from a `MonoBehavior` to the `ComponentData`: - -```c# -using Unity.Entities; -using Unity.Entities.Content; -using UnityEngine; - -public class MeshRefSample : MonoBehaviour -{ - public WeakObjectReference mesh; - class MeshRefSampleBaker : Baker - { - public override void Bake(MeshRefSample authoring) - { - AddComponent(new MeshComponentData { mesh = authoring.mesh }); - } - } -} -public struct MeshComponentData : IComponentData -{ - public WeakObjectReference mesh; -} -``` - -### Content delivery -The `RuntimeContentManger` APIs rely on content files to be on the device. [`ContentDeliveryService`](xref:Unity.Entities.Content.ContentDeliveryService) can be used to download these files on demand. Any file in the streaming assets folder can be delivered by the `ContentDeliveryService` but code that relies on these files must wait until they have been delivered. - - -The primary APIs are: - -* [`DeliverContent`](xref:Unity.Entities.Content.ContentDeliveryService.DeliverContent*) -* [`GetDeliveryStatus`](xref:Unity.Entities.Content.ContentDeliveryService.GetDeliveryStatus*) -* [`CancelDelivery`](xref:Unity.Entities.Content.ContentDeliveryService.CancelDelivery*) - -Here is an example of how to wait for content delivery to complete before continuing with the game logic: -```C# -using System; -using Unity.Entities.Content; -using UnityEngine; -public class GameStarter : MonoBehaviour -{ - public string remoteUrlRoot; - public string initialContentSet; - void Start() - { -#if ENABLE_CONTENT_DELIVERY - RuntimeContentSystem.LoadContentCatalog(remoteUrlRoot, null, initialContentSet, false); - ContentDeliveryGlobalState.RegisterForContentUpdateCompletion(s => - { - if (s >= ContentDeliveryGlobalState.ContentUpdateState.ContentReady) - LoadMainScene() - }); -#else - LoadMainScene(); -#endif - } - - void LoadMainScene() - { - //content is ready here... - } -} -``` - -To prepare content for remote delivery, you must "publish" a build using the APIs found in [`RemoteContentCatalogBuildUtility`](xref:Unity.Entities.Content.RemoteContentCatalogBuildUtility). Any files in the streaming assets folder of the build can be published. These files will be renamed with the content hash and put into a folder structure that mirrors the structure of the local device cache. This data can then be put on a server for the players to download. - -Here is an example of how to create a content build and update: -```C# -using System; -using System.Collections.Generic; -using System.IO; -using UnityEngine; -using UnityEditor; -using Unity.Entities.Build; -using Unity.Entities.Content; -using Unity.Scenes.Editor; - -static class BuildUtilities -{ - //prepares the content files for publish. The original files can be deleted or retained during this process by changing the last parameter of the PublishContent call. - static void PublishExistingBuild() - { - var buildFolder = EditorUtility.OpenFolderPanel("Select Build To Publish", - Path.GetDirectoryName(Application.dataPath), "Builds"); - if (!string.IsNullOrEmpty(buildFolder)) - { - var streamingAssetsPath = $"{buildFolder}/{PlayerSettings.productName}_Data/StreamingAssets"; - //the content sets are defined by the functor passed in here. - RemoteContentCatalogBuildUtility.PublishContent(streamingAssetsPath, - $"{buildFolder}-RemoteContent", - f => new string[] { "all" }, true); - } - } - - //This method is somewhat complicated because it will build the scenes from a player build but without fully building the player. - static void CreateContentUpdate() - { - var buildFolder = EditorUtility.OpenFolderPanel("Select Build To Publish", - Path.GetDirectoryName(Application.dataPath), "Builds"); - if (!string.IsNullOrEmpty(buildFolder)) - { - var buildTarget = EditorUserBuildSettings.activeBuildTarget; - var tmpBuildFolder = Path.Combine(Path.GetDirectoryName(Application.dataPath), - $"/Library/ContentUpdateBuildDir/{PlayerSettings.productName}"); - - var instance = DotsGlobalSettings.Instance; - var playerGuid = instance.GetPlayerType() == DotsGlobalSettings.PlayerType.Client ? instance.GetClientGUID() : instance.GetServerGUID(); - if (!playerGuid.IsValid) - throw new Exception("Invalid Player GUID"); - - var subSceneGuids = new HashSet(); - for (int i = 0; i < EditorBuildSettings.scenes.Length; i++) - { - var ssGuids = EditorEntityScenes.GetSubScenes(EditorBuildSettings.scenes[i].guid); - foreach (var ss in ssGuids) - subSceneGuids.Add(ss); - } - - RemoteContentCatalogBuildUtility.BuildContent(subSceneGuids, playerGuid, buildTarget, tmpBuildFolder); - - var publishFolder = Path.Combine(Path.GetDirectoryName(Application.dataPath), "Builds", $"{buildFolder}-RemoteContent"); - RemoteContentCatalogBuildUtility.PublishContent(tmpBuildFolder, publishFolder, f => new string[] { "all" }); - } - } -} -``` +* [ContentLoadModule](https://docs.unity3d.com/2023.1/Documentation/ScriptReference/UnityEngine.ContentLoadModule.html) \ No newline at end of file diff --git a/Documentation~/ecs-workflow-scene.md b/Documentation~/ecs-workflow-scene.md index be075eab..e6c6f3e1 100644 --- a/Documentation~/ecs-workflow-scene.md +++ b/Documentation~/ecs-workflow-scene.md @@ -31,4 +31,4 @@ To continue to create and optimize the spawner system, follow the next task in t ## Additional resources - [Understand the ECS workflow](ecs-workflow-intro.md) -- [Subscene](scripting-loading-scenes.md) +- [Subscene overview](conversion-subscenes.md) diff --git a/Documentation~/editor-project-settings.md b/Documentation~/editor-project-settings.md new file mode 100644 index 00000000..4dea6999 --- /dev/null +++ b/Documentation~/editor-project-settings.md @@ -0,0 +1,11 @@ +# Entities Project Settings reference + +Use the Entities Project Settings to define Entities-specific settings for your project. To open the Entities Project Settings, go to **Edit** > **Project Settings** > **Entities**. + +|**Setting**|**Description**| +|---|---| +|**Excluded Baking System Assemblies**| Add assembly definition assets to exclude from the baking system. You can use this property to prevent specific [bakers](baking.md) from running when building your project.| + +## Additional resources + +* [Entities preferences](editor-preferences.md) diff --git a/Documentation~/editor-workflows.md b/Documentation~/editor-workflows.md index 020c5010..61f5210e 100644 --- a/Documentation~/editor-workflows.md +++ b/Documentation~/editor-workflows.md @@ -1,10 +1,11 @@ # Working in the Editor -You can view information about the ECS elements in your project in Entities-specific Editor windows. The documentation in this section of the manual outlines the windows, Inspectors, and workflows available in the Editor. This section of the user manual contains information about the following: +You can view information about the ECS elements in your project in Entities-specific Editor windows. The documentation in this section of the manual outlines the windows, Inspectors, and workflows available in the Editor. |**Topic**|**Description**| |---|---| |[Entities Preferences reference](editor-preferences.md)|Reference for the Entities specific properties in the **Preferences** window.| +|[Entities Project Settings reference](editor-project-settings.md)|Reference for the Entities specific properties in the **Player Settings** window.| |[Working with authoring and runtime data](editor-authoring-runtime.md)|Information about the different data modes in the Editor.| |[Entities windows](editor-entities-windows.md)|Reference for the various windows in the Editor, including the Entities Hierarchy window.| -|[Entities Inspectors](editor-inspectors.md)|Information about the Entities-specific [Inspectors](https://docs.unity3d.com/Manual/UsingTheInspector.html).| +|[Entities Inspectors](editor-inspectors.md)|Information about the Entities-specific Inspectors.| diff --git a/Documentation~/filter.yml b/Documentation~/filter.yml index 56ac342d..eb3f3fc1 100644 --- a/Documentation~/filter.yml +++ b/Documentation~/filter.yml @@ -83,3 +83,12 @@ apiRules: - exclude: uidRegex: ^Unity\.Entities\.FastEquality type: Type + - exclude: + uidRegex: ^Unity\.Entities\.FilteredArchetype + type: Type + - exclude: + uidRegex: ^Unity\.Entities\.ComponentSystemBase\.OnCreateForCompiler + type: Method + - exclude: + uidRegex: ^Unity\.Entities\.ISystemCompilerGenerated + type: Interface diff --git a/Documentation~/getting-started-installation.md b/Documentation~/getting-started-installation.md index 0cc2d2c9..18961c6d 100644 --- a/Documentation~/getting-started-installation.md +++ b/Documentation~/getting-started-installation.md @@ -4,7 +4,7 @@ When you set up an Entities project, there are additional steps you must follow. ## Unity version -Entities 1.0 is compatible with Unity version 2022.2.0b8 and later. +Entities 1.0 is compatible with Unity version 2022.2.6f1 and later. ## Recommended packages @@ -16,7 +16,7 @@ You should add the following recommended set of core packages to your project: * [com.unity.entities.graphics](https://docs.unity3d.com/Packages/com.unity.entities.graphics@latest) ## IDE support -Entities 0.51 uses the [Microsoft Source Generator](https://docs.microsoft.com/en-us/dotnet/csharp/roslyn-sdk/source-generators-overview) feature for its code generation. Because of this, you should use an IDE that's compatible with source generators. Previous IDE versions might experience slow-downs or mark valid code as errors. The following IDEs are compatible with source generators: +The Entities package uses [Roslyn Source Generators](https://docs.microsoft.com/en-us/dotnet/csharp/roslyn-sdk/source-generators-overview). Because of this, you should use an IDE that's compatible with source generators. Previous IDE versions might experience slow-downs or mark valid code as errors. The following IDEs are compatible with source generators: * Visual Studio 2022+ * Rider 2021.3.3+ diff --git a/Documentation~/iterating-data-entities-foreach.md b/Documentation~/iterating-data-entities-foreach.md index 552391b1..6e717c3d 100644 --- a/Documentation~/iterating-data-entities-foreach.md +++ b/Documentation~/iterating-data-entities-foreach.md @@ -4,183 +4,42 @@ uid: iterating-data-entities-foreach # Iterate over data with Entities.ForEach -If you use the [`SystemBase`](xref:Unity.Entities.SystemBase) class to create your systems, you can use the [`Entities.ForEach`](xref:Unity.Entities.SystemBase.Entities) construction to define and execute algorithms over entities and their components. At compile time, the Unity translates each `ForEach()` call into a generated job. +If you use the [`SystemBase`](xref:Unity.Entities.SystemBase) class to create your systems, you can use the [`Entities.ForEach`](xref:Unity.Entities.SystemBase.Entities) construction to define and execute algorithms over entities and their components. At compile time, Unity translates each `ForEach` call into a generated job. -You pass `Entities.ForEach` a lambda expression, and Unity generates an [entity query](systems-entityquery.md) based on the lambda parameter types. When the generated job runs, Unity calls the lambda once for each entity that matches the query. `ForEachLambdaJobDescription` represents this generated job. +You pass `Entities.ForEach` a lambda expression, and Unity generates an [entity query](systems-entityquery.md) based on the lambda parameter types. When the generated job runs, Unity calls the lambda expression once for each entity that matches the query. `ForEachLambdaJobDescription` represents this generated job. -## Define a lambda expression - -When you define the [`Entities.ForEach`](xref:Unity.Entities.SystemBase.Entities) lambda expression, you can declare parameters that the [`SystemBase`](xref:Unity.Entities.SystemBase) class uses to pass in information about the current entity when it executes the method. - -A typical lambda expression looks like this: - -[!code-cs[lambda-params](../DocCodeSamples.Tests/LambdaJobExamples.cs#lambda-params)] - -You can pass up to eight parameters to an `Entities.ForEach` lambda expression. If you need to pass more parameters, you can define a custom delegate. For more information, see the section on [Custom delegates](#custom-delegates) in this document. - -When you use the standard delegates, you must group the parameters in the following order: - -1. Parameters passed-by-value (no parameter modifiers) -1. Writable parameters (`ref` parameter modifier) -1. Read-only parameters (`in` parameter modifier) - -You must use the `ref` or `in` parameter modify keyword on all components. If you don't, the component struct that Unity passes to your method is a copy instead of a reference. This means that it takes up extra memory for the read-ony parameters, and any changes you make to the components are silently thrown when the copied struct goes out of scope after the function returns. - -If the lambda expression doesn't follow this order, and you haven't created a suitable delegate, the compiler provides an error similar to: - -`error CS1593: Delegate 'Invalid_ForEach_Signature_See_ForEach_Documentation_For_Rules_And_Restrictions' does not take N arguments` - -This error message cites the number of arguments as the issue even when though the problem is the parameter order. - -### Custom delegates - -If you want to use more than eight arguments in a `ForEach` lambda expression, you must declare your own delegate type and `ForEach` overload. This allows you to use an unlimited amount of arguments and to put the `ref`, `in`, and `value` parameters in any order you want. - -You can also declare the three [named parameters](#named-parameters) `entity`, `entityInQueryIndex`, and `nativeThreadIndex` anywhere in your parameter list. Don't use `ref` or `in` modifiers for these parameters. - -The following example shows 12 arguments, and uses the `entity` parameter within the lambda expression: - -[!code-cs[lambda-params](../DocCodeSamples.Tests/LambdaJobExamples.cs#lambda-params-many)] - -### Component parameters - -To access a component associated with an entity, you must pass a parameter of that component type to the lambda expression. The compiler automatically adds all components passed to the lambda expression to the entity query as required components. - -To update a component value, you must use the `ref` keyword in the parameter list to pass it to the lambda expression. Without the `ref` keyword, Unity makes any modifications to a temporary copy of the component. - -To declare a read-only component passed to the lambda expression use the `in` keyword in the parameter list. - -When you use `ref`, Unity marks the components in the current chunk as changed, even if the lambda expression doesn't actually modify them. For efficiency, you should always use the `in` keyword to declare components that your lambda expression doesn't modify as read only. - -The following example passes a `Source` component parameter to a job as read-only, and a `Destination` component parameter as writable: - -[!code-cs[read-write-modifiers](../DocCodeSamples.Tests/LambdaJobExamples.cs#read-write-modifiers)] - -> [!IMPORTANT] -> You can't pass [chunk components](components-chunk.md) to the `Entities.ForEach` lambda expression. - -For dynamic buffers, use `DynamicBuffer` rather than the component type stored in the buffer: - -[!code-cs[dynamicbuffer](../DocCodeSamples.Tests/LambdaJobExamples.cs#dynamicbuffer)] - -### Named parameters - -You can also pass the following named parameters to the `Entities.ForEach` lambda expression, which Unity assigns values based on the entity the job is processing. - -|**Parameter**|**Function**| -|---|---| -|`Entity entity`| The entity instance of the current entity. You can name the parameter anything as long as the type is `Entity`.| -|`int entityInQueryIndex`| The index of the entity in the list of all entities the query selected. Use the entity index value when you have a native array that you need to fill with a unique value for each entity. You can use the `entityInQueryIndex` as the index in that array. You should use `entityInQueryIndex` as the `sortKey` to add commands to a concurrent [entity command buffer](systems-entity-command-buffers.md).| -|`int nativeThreadIndex`| A unique index of the thread executing the current iteration of the lambda expression. When you use `Run()` to execute the lambda expression, `nativeThreadIndex` is always zero. Don't use `nativeThreadIndex` as the `sortKey` of a concurrent [entity command buffer](systems-entity-command-buffers.md); use `entityInQueryIndex`instead.| -|`EntityCommands commands`| You can name this parameter anything as long as the type is `EntityCommands`. Use this parameter only in conjunction with either `WithDeferredPlaybackSystem()` or `WithImmediatePlayback()`. The `EntityCommands` type contains several methods that mirror their counterparts in the `EntityCommandBuffer` type. If you use an `EntityCommands` instance inside `Entities.ForEach()`, the compiler creates extra code where appropriate to handle the creation, scheduling, playback, and disposal of entity command buffers, on which counterparts to `EntityCommands` methods are invoked.| - -## Execute an Entities.ForEach lambda expression -You can execute a job lambda expression in the following ways: - -* Use `Schedule()` and `ScheduleParallel()` to schedule the job -* Use `Run()` to execute the job immediately on the main thread. - -The following example illustrates a `SystemBase` implementation that uses `Entities.ForEach` to read the `Velocity` component and write to the `ObjectPosition` component: - -[!code-cs[entities-foreach-example](../DocCodeSamples.Tests/LambdaJobExamples.cs#entities-foreach-example)] - - -## Select entities - -`Entities.ForEach` has its own mechanism to define the entity query that it uses to select the entities to process. The query automatically includes any components that you use as parameters of the lambda expression. - -You can also use the `WithAll`, `WithAny`, and `WithNone` clauses to further refine which entities `Entities.ForEach` selects. See [`SystemBase.Entities`](xref:Unity.Entities.SystemBase.Entities) for the complete list of query options. - -The following example uses these clauses to select entities based on these parameters: - -* The entity has the components, `Destination`, `Source`, and `LocalToWorld` -* The entity has at least one of the components, `ObjectRotation`, `ObjectPosition`, or `ObjectUniformScale` -* The entity doesn't have a `ObjectNonUniformScale` component. - -[!code-cs[entity-query](../DocCodeSamples.Tests/LambdaJobExamples.cs#entity-query)] - -In this example, only the `Destination` and `Source` components are accessed inside the lambda expression because they're the only components in the parameter list. - -## Access the EntityQuery object - -`Entities.ForEach` creates an [`EntityQuery`](xref:Unity.Entities.EntityQuery) in [`OnCreate`](xref:Unity.Entities.ComponentSystemBase.OnCreate), which you can use a copy of at any time, even before `Entities.ForEach` is invoked. - -To access this entity query, use [`WithStoreEntityQueryInField(ref query)`](xref:Unity.Entities.SystemBase.Entities) with the `ref` parameter modifier. This method assigns a reference to the query to the field you provide. However, this `EntityQuery` doesn't have any of the filters that the `Entities.ForEach` invocation sets up. - -The following example illustrates how to access the `EntityQuery` object implicitly created for an `Entities.ForEach` construction. It uses the `EntityQuery` object to invoke the [`CalculateEntityCount()`](xref:Unity.Entities.EntityQuery.CalculateEntityCount*) method and uses this count to create a native array with enough space to store one value per entity that the query selects: - -[!code-cs[store-query](../DocCodeSamples.Tests/LambdaJobExamples.cs#store-query)] - -## Access optional components - -The `Entities.ForEach` lambda expression doesn't support querying and accessing optional components with `WithAny`. - -If you want to read or write to an optional component, split the `Entities.ForEach` construction into multiple jobs for each combination of the optional components. For example, if you have two optional components, you would need three `ForEach` constructions: one including the first optional component, one including the second, and one including both components. Another alternative is to use `IJobChunk `iterate by chunk. For more information, see [Iterating over data by chunk](iterating-data-ijobchunk.md). - -## Change filtering - -You can use `WithChangeFilter` to enable change filtering, which processes components only if another component in the entity has changed since the last time the current `SystemBase` instance has run. The component type in the change filter must either be in the lambda expression parameter list, or part of a `WithAll` statement. For example: - -[!code-cs[with-change-filter](../DocCodeSamples.Tests/LambdaJobExamples.cs#with-change-filter)] - -An entity query supports change filtering on up to two component types. - -Unity applies change filtering at the archetype chunk level. If any code accesses a component in a chunk that has write access, then Unity marks that component type in that archetype chunk as changed, even if the code didn’t change any data. - -## Shared component filtering - -Unity groups entities with [shared components](components-shared.md) into chunks with other entities that have the same value for their shared components. To select groups of entities that have specific shared component values, use the [`WithSharedComponentFilter`](xref:Unity.Entities.LambdaJobQueryConstructionMethods.WithSharedComponentFilter*) method. - -The following example selects entities grouped by a `Cohort ISharedComponentData`. The lambda expression in this example sets a `DisplayColor IComponentData` component based on the entity’s cohort: - -[!code-cs[with-shared-component](../DocCodeSamples.Tests/LambdaJobExamples.cs#with-shared-component)] - -The example uses the `EntityManager` to get all the unique cohort values. It then schedules a lambda job for each cohort, and passes the new color to the lambda expression as a captured variable. - -## Capture variables - -You can capture local variables for the `Entities.ForEach` lambda expression. When you call one of the `Schedule` methods instead of `Run` to use a job to execute the lambda expression, there are some restrictions on the captured variables and how you use them: - -* You can only capture [native containers](https://docs.unity3d.com/Manual/JobSystemNativeContainer.html) and blittable types. -* A job can only write to captured variables that are native containers. To return a single value, create a native array with one element. - -If you read a native container, but don't write to it, always use `WithReadOnly(variable)` to specify read-only access. For more information about setting attributes for captured variables, see [`SystemBase.Entities`](xref:Unity.Entities.SystemBase.Entities). `Entities.ForEach` provides these as methods because the C# language doesn't allow attributes on local variables. - -To dispose of captured native containers or types that contain native containers after `Entities.ForEach` runs, use `WithDisposeOnCompletion(variable)`. If you call this in `Run()`, this disposes of the types immediately after the lambda expression runs. If you call this in `Schedule()` and`ScheduleParallel()`, it schedules them to be disposed of later with a job, and returns the JobHandle. - -> [!NOTE] -> When you execute the method with `Run()` you can write to captured variables that aren't native containers. However, you should still use blittable types where possible so that the method can be compiled with [Burst](https://docs.unity3d.com/Packages/com.unity.burst@latest/index.html). +If you use [`ISystem`](systems-isystem.md) to create your systems, use [`SystemAPI.Query`](systems-systemapi-query.md) to iterate over system data. `Entities.ForEach` has four times slower compilation time than `SystemAPI.Query` and [`IJobEntity`](iterating-data-ijobentity.md), so you should consider using those methods to iterate over data instead. ## Supported features -Use `Run()` to execute the lambda expression on the main thread. You can also use `Schedule()` to execute it as a single job, or `ScheduleParallel()` to execute it as a parallel job. These different execution methods have different constraints on how you access data. Also, the [Burst compiler](https://docs.unity3d.com/Packages/com.unity.burst@latest/index.html) uses a restricted subset of the C# language, so you need to specify `WithoutBurst()` if you want to use C# features outside this subset. This includes accessing managed types. +Use `Run()` to execute the lambda expression on the main thread. You can also use `Schedule` to execute it as a single job, or `ScheduleParallel` to execute it as a parallel job. These different execution methods have different constraints on how you access data. Also, the [Burst compiler](https://docs.unity3d.com/Packages/com.unity.burst@latest/index.html) uses a restricted subset of the C# language, so you need to specify `WithoutBurst` if you want to use C# features outside this subset. This includes accessing managed types. The following table shows which features are supported in `Entities.ForEach` for the different methods of scheduling available in `SystemBase`: -| Supported feature | `Run` | `Schedule` | `ScheduleParallel`| -|-------------------------------|---------|------------|-------------------| -| Capture local value type | ✓| ✓ |✓ | -| Capture local reference type | Only `WithoutBurst` and not in `ISystem`||| -| Writing to captured variables |✓ | | | -| Use field on the system class | Only `WithoutBurst`| | | -| Methods on reference types | Only `WithoutBurst` and not in `ISystem`||| -| Shared Components | Only `WithoutBurst` and not in `ISystem`||| -| Managed Components | Only `WithoutBurst` and not in `ISystem`||| -| Structural changes | Only `WithStructuralChanges` and not in `ISystem`||| -| `SystemBase.GetComponent` | ✓| ✓ |✓ | -| `SystemBase.SetComponent` | ✓| ✓ | | -| `GetComponentDataFromEntity` | ✓| ✓ | Only as `ReadOnly`| -| `HasComponent` | ✓| ✓ |✓ | -| `WithDisposeOnCompletion` | ✓| ✓ |✓ | -| `WithScheduleGranularity ` | | |✓ | -| `WithDeferredPlaybackSystem ` | ✓| ✓ |✓ | -| `WithImmediatePlayback` | ✓| | | -| `HasBuffer ` | ✓| ✓ |✓ | -| `SystemBase.GetStorageInfoFromEntity`| ✓| ✓|✓ | -| `SystemBase.Exists ` | ✓| ✓|✓ | +| **Supported feature**| **Run method**| **Schedule method** | **ScheduleParallel method**| +|---|---|---|---| +| Capture local value type | Supported| Supported |Supported| +| Capture local reference type | Supported only `WithoutBurst` and not in `ISystem`| Unsupported|Unsupported| +| Writing to captured variables |Supported|Unsupported|Unsupported| +| Use field on the system class | Supported only `WithoutBurst`|Unsupported|Unsupported| +| Methods on reference types | Supported only `WithoutBurst` and not in `ISystem`|Unsupported|Unsupported| +| Shared components | Supported only `WithoutBurst` and not in `ISystem`|Unsupported|Unsupported| +| Managed components | Supported only `WithoutBurst` and not in `ISystem`|Unsupported|Unsupported| +| Structural changes | Supported only `WithStructuralChanges` and not in `ISystem`|Unsupported|Unsupported| +| `SystemBase.GetComponent` | Supported| Supported |Supported| +| `SystemBase.SetComponent` | Supported| Supported |Unsupported| +| `GetComponentDataFromEntity` | Supported| Supported | Supported only as `ReadOnly`| +| `HasComponent` | Supported| Supported |Supported| +| `WithDisposeOnCompletion` | Supported| Supported |Supported| +| `WithScheduleGranularity ` | Unsupported|Unsupported|Supported| +| `WithDeferredPlaybackSystem ` | Supported| Supported |Supported| +| `WithImmediatePlayback` | Supported|Unsupported|Unsupported| +| `HasBuffer ` | Supported| Supported |Supported| +| `SystemBase.GetStorageInfoFromEntity`| Supported| Supported |Supported| +| `SystemBase.Exists ` | Supported| Supported |Supported| >[!IMPORTANT] -> `WithStructuralChanges()` disables Burst. Don't use this option if you want to achieve high levels of performance `Entities.ForEach`. If you want to use this option, use an [`EntityCommandBuffer`](xref:Unity.Entities.EntityCommandBuffer). +> `WithStructuralChanges` disables Burst. Don't use this option if you want to achieve high levels of performance with `Entities.ForEach`. If you want to use this option, use an [entity command buffer](systems-entity-command-buffers.md) instead. An `Entities.ForEach` construction uses Roslyn source generators to translate the code you write for the construction into correct ECS code. This translation means you can express the intent of your algorithm without having to include complex, boilerplate code. However, it means that some common ways of writing code aren't allowed. @@ -197,6 +56,6 @@ The following features aren't supported: ## Dependencies -By default, a system uses its [`Dependency`](xref:Unity.Entities.SystemBase.Dependency) property to manage its ECS-related dependencies. By default, the system adds each job created with `Entities.ForEach` and `Job.WithCode` to the `Dependency` job handle in the order that they appear in the [`OnUpdate()`](xref:Unity.Entities.SystemBase.OnUpdate*) function. You can also pass a `JobHandle` to your `Schedule` methods to manage job dependencies manually, which then return the resulting dependency. For more information, see the [`Dependency`](xref:Unity.Entities.SystemBase.Dependency) documentation. +By default, a system uses its [`Dependency`](xref:Unity.Entities.SystemBase.Dependency) property to manage its ECS-related dependencies. The system adds each job created with `Entities.ForEach` and `Job.WithCode` to the `Dependency` job handle in the order that they appear in the [`OnUpdate`](xref:Unity.Entities.SystemBase.OnUpdate*) method. You can also pass a `JobHandle` to your `Schedule` methods to manage job dependencies manually, which then returns the resulting dependency. For more information, refer to the [`Dependency`](xref:Unity.Entities.SystemBase.Dependency) documentation. -See [Job dependencies](scheduling-jobs-dependencies.md) for more general information about job dependencies. +Refer to [Job dependencies](scheduling-jobs-dependencies.md) for more general information about job dependencies. diff --git a/Documentation~/iterating-entities-foreach-define.md b/Documentation~/iterating-entities-foreach-define.md new file mode 100644 index 00000000..8ea69a03 --- /dev/null +++ b/Documentation~/iterating-entities-foreach-define.md @@ -0,0 +1,91 @@ +# Define and execute an Entities.ForEach lambda expression + +To use `Entities.ForEach`, you must pass it a lambda expression, which Unity uses to generate an [entity query](systems-entityquery.md) based on the lambda parameter types. When the generated job runs, Unity calls the lambda expression once for each entity that matches the query. `ForEachLambdaJobDescription` represents this generated job. + +When you define the [`Entities.ForEach`](xref:Unity.Entities.SystemBase.Entities) lambda expression, you can declare parameters that the [`SystemBase`](xref:Unity.Entities.SystemBase) class uses to pass in information about the current entity when it executes the method. + +A typical lambda expression looks like this: + +[!code-cs[lambda-params](../DocCodeSamples.Tests/LambdaJobExamples.cs#lambda-params)] + +You can pass up to eight parameters to an `Entities.ForEach` lambda expression. If you need to pass more parameters, you can define a custom delegate. For more information, refer to the section on [Custom delegates](#custom-delegates) in this document. + +When you use the standard delegates, you must group the parameters in the following order: + +1. Parameters passed-by-value (no parameter modifiers) +1. Writable parameters (`ref` parameter modifier) +1. Read-only parameters (`in` parameter modifier) + +You must use the `ref` or `in` parameter modify keyword on all components. If you don't, the component struct that Unity passes to your method is a copy instead of a reference. This means that it takes up extra memory for the read-ony parameters, and Unity silently throws any changes you make to the components when the copied struct goes out of scope after the function returns. + +If the lambda expression doesn't follow this order, and you haven't created a suitable delegate, the compiler provides an error similar to: + +`error CS1593: Delegate 'Invalid_ForEach_Signature_See_ForEach_Documentation_For_Rules_And_Restrictions' does not take N arguments` + +This error message cites the number of arguments as the issue even when though the problem is the parameter order. + +## Custom delegates + +If you want to use more than eight arguments in a `ForEach` lambda expression, you must declare your own delegate type and `ForEach` overload. Declaring your own type means you can use an unlimited amount of arguments and put the `ref`, `in`, and `value` parameters in any order you want. + +You can also declare the three [named parameters](#named-parameters) `entity`, `entityInQueryIndex`, and `nativeThreadIndex` anywhere in your parameter list. Don't use `ref` or `in` modifiers for these parameters. + +The following example shows 12 arguments, and uses the `entity` parameter within the lambda expression: + +[!code-cs[lambda-params](../DocCodeSamples.Tests/LambdaJobExamples.cs#lambda-params-many)] + +## Component parameters + +To access a component associated with an entity, you must pass a parameter of that component type to the lambda expression. The compiler automatically adds all components passed to the lambda expression to the entity query as required components. + +To update a component value, you must use the `ref` keyword in the parameter list to pass it to the lambda expression. Without the `ref` keyword, Unity makes any modifications to a temporary copy of the component. + +To declare a read-only component passed to the lambda expression use the `in` keyword in the parameter list. + +When you use `ref`, Unity marks the components in the current chunk as changed, even if the lambda expression doesn't actually change them. For efficiency, always use the `in` keyword to declare components that your lambda expression doesn't change as read only. + +The following example passes a `Source` component parameter to a job as read-only, and a `Destination` component parameter as writable: + +[!code-cs[read-write-modifiers](../DocCodeSamples.Tests/LambdaJobExamples.cs#read-write-modifiers)] + +> [!IMPORTANT] +> You can't pass [chunk components](components-chunk.md) to the `Entities.ForEach` lambda expression. + +For dynamic buffers, use `DynamicBuffer` rather than the component type stored in the buffer: + +[!code-cs[dynamicbuffer](../DocCodeSamples.Tests/LambdaJobExamples.cs#dynamicbuffer)] + +## Named parameters + +You can also pass the following named parameters to the `Entities.ForEach` lambda expression, which Unity assigns values based on the entity the job is processing. + +|**Parameter**|**Description**| +|---|---| +|`Entity entity`| The entity instance of the current entity. You can name the parameter anything as long as the type is `Entity`.| +|`int entityInQueryIndex`| The index of the entity in the list of all entities that the query selected. Use the entity index value when you have a native array that you need to fill with a unique value for each entity. You can use the `entityInQueryIndex` as the index in that array. Use `entityInQueryIndex` as the `sortKey` to add commands to a concurrent [entity command buffer](systems-entity-command-buffers.md).| +|`int nativeThreadIndex`| A unique index of the thread executing the current iteration of the lambda expression. When you use `Run` to execute the lambda expression, `nativeThreadIndex` is always zero. Don't use `nativeThreadIndex` as the `sortKey` of a concurrent [entity command buffer](systems-entity-command-buffers.md); use `entityInQueryIndex`instead.| +|`EntityCommands commands`| You can name this parameter anything as long as the type is `EntityCommands`. Use this parameter only in conjunction with either `WithDeferredPlaybackSystem` or `WithImmediatePlayback`. The `EntityCommands` type has several methods that mirror their counterparts in the `EntityCommandBuffer` type. If you use an `EntityCommands` instance inside `Entities.ForEach`, the compiler creates extra code where appropriate to handle the creation, scheduling, playback, and disposal of entity command buffers, on which counterparts to `EntityCommands` methods are invoked.| + +## Execute an Entities.ForEach lambda expression +You can execute a job lambda expression in the following ways: + +* Use `Schedule` and `ScheduleParallel` to schedule the job +* Use `Run` to execute the job immediately on the main thread. + +The following example illustrates a `SystemBase` implementation that uses `Entities.ForEach` to read the `Velocity` component and write to the `ObjectPosition` component: + +[!code-cs[entities-foreach-example](../DocCodeSamples.Tests/LambdaJobExamples.cs#entities-foreach-example)] + +## Capture variables + +You can capture local variables for the `Entities.ForEach` lambda expression. When you call one of the `Schedule` methods instead of `Run` to use a job to execute the lambda expression, there are some restrictions on the captured variables and how you use them: + +* You can only capture [native containers](https://docs.unity3d.com/Manual/JobSystemNativeContainer.html) and blittable types. +* A job can only write to captured variables that are native containers. To return a single value, create a native array with one element. + +If you read a native container, but don't write to it, always use `WithReadOnly(variable)` to specify read-only access. For more information about setting attributes for captured variables, see [`SystemBase.Entities`](xref:Unity.Entities.SystemBase.Entities). `Entities.ForEach` provides these as methods because the C# language doesn't allow attributes on local variables. + +To dispose of captured native containers or types that contain native containers after `Entities.ForEach` runs, use `WithDisposeOnCompletion(variable)`. If you call this in `Run`, this disposes of the types immediately after the lambda expression runs. If you call this in `Schedule` and`ScheduleParallel`, it schedules them to be disposed of later with a job, and returns the JobHandle. + +> [!NOTE] +> When you execute the method with `Run` you can write to captured variables that aren't native containers. However, you should still use blittable types where possible so that the method can be compiled with [Burst](https://docs.unity3d.com/Packages/com.unity.burst@latest/index.html). \ No newline at end of file diff --git a/Documentation~/iterating-entities-foreach-ecb.md b/Documentation~/iterating-entities-foreach-ecb.md new file mode 100644 index 00000000..fb3f2eb4 --- /dev/null +++ b/Documentation~/iterating-entities-foreach-ecb.md @@ -0,0 +1,34 @@ +# Use entity command buffers in Entities.ForEach + +To use an entity command buffer (ECB) in the `Entities.ForEach` method, pass an `EntityCommandBuffer` parameter to the lambda expression. Only a small subset of `EntityCommandBuffer` methods are supported, and they have the `[SupportedInEntitiesForEach]` attribute: + +* `Entity Instantiate(Entity entity)` +* `void DestroyEntity(Entity entity)` +* `void AddComponent<T>(Entity e, T component) where T : unmanaged, IComponentData` +* `void SetComponent<T>(Entity e, T component) where T : unmanaged, IComponentData` +* `void RemoveComponent<T>(Entity e)` + +For example, the following code does this: + +1. It checks each entity to find out whether its `HealthLevel` is 0. +2. If true, it records a command to destroy the entity. +3. It also specifies that the `EndSimulationEntityCommandBufferSystem` must play back the command. + +[!code-cs[conversion](../DocCodeSamples.Tests/EntityCommandBuffers.cs#ecb_parallel_for)] + +When you use any of these methods in a `ForEach` method, at runtime the compiler generates the code necessary to create, populate, play back, and dispose of an `EntityCommandBuffer` instance, or an `EntityCommandBuffer.ParallelWriter` instance, if `ScheduleParallel` is called. + +Invoking these methods outside of `ForEach()` results in an exception. + +## Play back an entity command buffer + +To pass an `EntityCommandBuffer` parameter to the `ForEach` method, you must also call one of the following methods to specify when you want to play back the commands: + +* Deferred playback: Call `WithDeferredPlaybackSystem`, where `T` identifies the ECB system that plays back the commands. It must be a type that derives from `EntityCommandBufferSystem`. +* Immediate playback: call `WithImmediatePlayback` to execute the commands instantly after the `ForEach` method has finished all iterations. You can only use `WithImmediatePlayback` with `Run`. + +The compiler automatically generates code to create and dispose of any `EntityCommandBuffer` instances. + +## Additional resources + +* [Entity command buffer overview](systems-entity-command-buffers.md) \ No newline at end of file diff --git a/Documentation~/iterating-entities-foreach-filtering.md b/Documentation~/iterating-entities-foreach-filtering.md new file mode 100644 index 00000000..8eb2f4b0 --- /dev/null +++ b/Documentation~/iterating-entities-foreach-filtering.md @@ -0,0 +1,23 @@ +# Filtering data + +You can filter the data in an `Entities.ForEach` expression, either by change, or by shared component. + +## Change filtering + +You can use `WithChangeFilter` to enable change filtering, which processes components only if another component in the entity has changed since the last time the current `SystemBase` instance has run. The component type in the change filter must either be in the lambda expression parameter list, or part of a `WithAll` statement. For example: + +[!code-cs[with-change-filter](../DocCodeSamples.Tests/LambdaJobExamples.cs#with-change-filter)] + +An entity query supports change filtering on up to two component types. + +Unity applies change filtering at the archetype chunk level. If any code accesses a component in a chunk that has write access, then Unity marks that component type in that archetype chunk as changed, even if the code didn’t change any data. + +## Shared component filtering + +Unity groups entities with [shared components](components-shared.md) into chunks with other entities that have the same value for their shared components. To select groups of entities that have specific shared component values, use the [`WithSharedComponentFilter`](xref:Unity.Entities.LambdaJobQueryConstructionMethods.WithSharedComponentFilter*) method. + +The following example selects entities grouped by a `Cohort ISharedComponentData`. The lambda expression in this example sets a `DisplayColor IComponentData` component based on the entity’s cohort: + +[!code-cs[with-shared-component](../DocCodeSamples.Tests/LambdaJobExamples.cs#with-shared-component)] + +The example uses the `EntityManager` to get all the unique cohort values. It then schedules a lambda job for each cohort, and passes the new color to the lambda expression as a captured variable. \ No newline at end of file diff --git a/Documentation~/iterating-entities-foreach-select-data.md b/Documentation~/iterating-entities-foreach-select-data.md new file mode 100644 index 00000000..f8409b7d --- /dev/null +++ b/Documentation~/iterating-entities-foreach-select-data.md @@ -0,0 +1,31 @@ +# Select and access data + +`Entities.ForEach` has its own mechanism to define the entity query that it uses to select the entities to process. The query automatically includes any components that you use as parameters of the lambda expression. + +You can use the `WithAll`, `WithAny`, and `WithNone` clauses to further refine which entities `Entities.ForEach` selects. Refer to the [`SystemBase.Entities`](xref:Unity.Entities.SystemBase.Entities) API documentation for the complete list of query options. + +The following example uses these clauses to select entities based on these parameters: + +* The entity has the components, `Destination`, `Source`, and `LocalToWorld` +* The entity has at least one of the components, `ObjectRotation`, `ObjectPosition`, or `ObjectUniformScale` +* The entity doesn't have a `ObjectNonUniformScale` component. + +[!code-cs[entity-query](../DocCodeSamples.Tests/LambdaJobExamples.cs#entity-query)] + +In this example, only the `Destination` and `Source` components are accessed inside the lambda expression because they're the only components in the parameter list. + +## Access the EntityQuery object + +`Entities.ForEach` creates an [`EntityQuery`](xref:Unity.Entities.EntityQuery) in [`OnCreate`](xref:Unity.Entities.ComponentSystemBase.OnCreate), which you can use a copy of at any time, even before `Entities.ForEach` is invoked. + +To access this entity query, use [`WithStoreEntityQueryInField(ref query)`](xref:Unity.Entities.SystemBase.Entities) with the `ref` parameter modifier. This method assigns a reference to the query to the field you provide. However, this `EntityQuery` doesn't have any of the filters that the `Entities.ForEach` invocation sets up. + +The following example illustrates how to access the `EntityQuery` object implicitly created for an `Entities.ForEach` construction. The example uses the `EntityQuery` object to invoke the [`CalculateEntityCount()`](xref:Unity.Entities.EntityQuery.CalculateEntityCount*) method and uses this count to create a native array with enough space to store one value per entity that the query selects: + +[!code-cs[store-query](../DocCodeSamples.Tests/LambdaJobExamples.cs#store-query)] + +## Access optional components + +The `Entities.ForEach` lambda expression doesn't support querying and accessing optional components with `WithAny`. + +If you want to read or write to an optional component, split the `Entities.ForEach` construction into multiple jobs for each combination of the optional components. For example, if you have two optional components, you need three `ForEach` constructions: one including the first optional component, one including the second, and one including both components. Another alternative is to use `IJobChunk `iterate by chunk. For more information, refer to [Iterating over data by chunk](iterating-data-ijobchunk.md). diff --git a/Documentation~/scheduling-jobs-extensions.md b/Documentation~/scheduling-jobs-extensions.md index 1ca3da4b..a04f3d44 100644 --- a/Documentation~/scheduling-jobs-extensions.md +++ b/Documentation~/scheduling-jobs-extensions.md @@ -10,9 +10,9 @@ These interfaces include: * [`IJobParallelForExtensions`](https://docs.unity3d.com/ScriptReference/Unity.Jobs.IJobParallelForExtensions.html): Provides extension methods to run `IJobParallelFor` jobs. * [`JobHandle`](https://docs.unity3d.com/ScriptReference/Unity.Jobs.JobHandle.html): A handle to access a scheduled job. You can also use `JobHandle` instances to specify dependencies between jobs. -For an overview of the jobs system see [C# Job System](https://docs.unity3d.com/Manual/JobSystemSafetySystem.html) in the Unity User Manual. +For an overview of the jobs system see the [Job system](xref:JobSystem) documentation in the Unity User Manual. -The [Jobs package](https://docs.unity3d.com/Packages/com.unity.jobs@latest) extends the job system to support ECS. It contains: +The [Collections package](https://docs.unity3d.com/Packages/com.unity.collections@latest) extends the job system to support ECS. It contains: * [`IJobParallelForDeferExtensions`](https://docs.unity3d.com/Packages/com.unity.collections@latest/index.html?subfolder=/api/Unity.Jobs.IJobParallelForDeferExtensions.html) * [`IJobFilter`](https://docs.unity3d.com/Packages/com.unity.collections@latest/index.html?subfolder=/api/Unity.Jobs.IJobFilter.html) diff --git a/Documentation~/systems-entity-command-buffer-automatic-playback.md b/Documentation~/systems-entity-command-buffer-automatic-playback.md new file mode 100644 index 00000000..79dcb623 --- /dev/null +++ b/Documentation~/systems-entity-command-buffer-automatic-playback.md @@ -0,0 +1,57 @@ +# Automatic play back and disposal of entity command buffers + +To play back and dispose of entity command buffers (ECBs), you can use [`EntityCommandBufferSystem`](xref:Unity.Entities.EntityCommandBufferSystem), rather than manually doing it yourself. To do this: + +1. Get the singleton instance of the` EntityCommandBufferSystem` which you want to do the playback. +1. Use the singleton to create an `EntityCommandBuffer` instance. +1. Records commands to the `EntityCommandBuffer`. + +For example: + +[!code-cs[conversion](../DocCodeSamples.Tests/EntityCommandBuffers.cs#ecb_from_ecbsystem)] + +>[!IMPORTANT] +>Don't manually play back or dispose of an `EntityCommandBuffer` that you've created with an `EntityCommandBufferSystem`. The `EntityCommandBufferSystem` does both for you when it runs. + +In each update, an `EntityCommandBufferSystem`: + +1. Completes all registered jobs, plus all jobs scheduled against its [singleton component](components-singleton.md). This ensures that any relevant jobs have finished their recording. +1. Plays back all ECBs created via the system in the same order they were created. +1. Disposes of the `EntityCommandBuffer` instances. + +## Default EntityCommandBufferSystem systems + +The default [world](concepts-worlds.md) has the following default `EntityCommandBufferSystem` systems: + +* `BeginInitializationEntityCommandBufferSystem` +* `EndInitializationEntityCommandBufferSystem` +* `BeginFixedStepSimulationEntityCommandBufferSystem` +* `EndFixedStepSimulationEntityCommandBufferSystem` +* `BeginVariableRateSimulationEntityCommandBufferSystem` +* `EndVariableRateSimulationEntityCommandBufferSystem` +* `BeginSimulationEntityCommandBufferSystem` +* `EndSimulationEntityCommandBufferSystem` +* `BeginPresentationEntityCommandBufferSystem` + +Because structural changes can't happen in the frame after Unity gives the rendering data to the renderer, there's no `EndPresentationEntityCommandBufferSystem` system. You can use `BeginInitializationEntityCommandBufferSystem` instead: the end of one frame is the beginning of the next frame. + +The `EntityCommandBufferSystem` systems update at the beginning and end of the standard [system groups](concepts-systems.md#system-groups), and at the beginning and end of the fixed and variable rate simulation groups. For more information, refer to the documentation on [System update order](systems-update-order.md). + +If you can't use the default systems for your application, then you can create your own `EntityCommandBufferSystem`: + +[!code-cs[conversion](../DocCodeSamples.Tests/EntityCommandBuffers.cs#ecb_define_ecbsystem)] + + +## Deferred entities + +The `EntityCommandBuffer` methods `CreateEntity` and `Instantiate` record commands that create entities. These methods only record commands and don't create entities. As such, they return `Entity` values with negative indices that represent placeholder entities that don't exist yet. These placeholder `Entity` values are only meaningful in recorded commands of the same ECB: + +[!code-cs[conversion](../DocCodeSamples.Tests/EntityCommandBuffers.cs#ecb_deferred_entities)] + +Values recorded in an `AddComponent`, `SetComponent`, or `SetBuffer` command might have `Entity` fields. In playback, Unity remaps any placeholder `Entity` values in these components or buffers to the corresponding actual entities: + +[!code-cs[conversion](../DocCodeSamples.Tests/EntityCommandBuffers.cs#ecb_deferred_remapping)] + +## Additional resources + +* [Playback entity command buffers](systems-entity-command-buffer-playback.md) \ No newline at end of file diff --git a/Documentation~/systems-entity-command-buffer-playback.md b/Documentation~/systems-entity-command-buffer-playback.md new file mode 100644 index 00000000..190d79d8 --- /dev/null +++ b/Documentation~/systems-entity-command-buffer-playback.md @@ -0,0 +1,33 @@ +# Entity command buffer playback + +If you split the recording of commands in an entity command buffer (ECB) across multiple threads [in a parallel job](systems-entity-command-buffer-use.md#parallel-jobs) it means that the order of the commands is non-deterministic because they depend on job scheduling. + +Determinism isn't always essential, but code which produces deterministic results is easier to debug. There are also networking scenarios which require consistent results across different machines. However, determinism can have an impact on performance, so you might want to accept non-determinism in some projects. + +## Deterministic playback in parallel jobs + +You can't avoid the non-deterministic order of recording [in parallel jobs](systems-entity-command-buffer-use.md#parallel-jobs), but you can make the playback order of the commands deterministic in the following way: + +1. Record an int sort key passed as the first argument to each ECB method. +1. Use the sort keys to sort the commands on playback, before Unity performs the commands. + +If the recorded sort keys are independent from the scheduling, then the sorting makes the playback order deterministic. Also, Unity always plays back commands with larger sort keys after commands with smaller sort keys. + +In a parallel job, the sort key you need for each entity is a number that has a fixed and unique association with that chunk in the job's query. You can use the `ChunkIndexInQuery` value in a parallel job as an index. The index has a zero-based numbering system, so in the list of archetype chunks that match the job's query, the first chunk has index 0, the second chunk has index 1, and the third chunk has index 2. + +The following example code shows an ECB used in a parallel job: + +[!code-cs[conversion](../DocCodeSamples.Tests/EntityCommandBuffers.cs#ecb_multi_threaded)] + + +## Multi playback + +If you call the `Playback` method more than once, it throws an exception. To avoid this, create an `EntityCommandBuffer` instance with the `PlaybackPolicy.MultiPlayback` option: + +[!code-cs[conversion](../DocCodeSamples.Tests/EntityCommandBuffers.cs#ecb_multi_playback)] + +You can use multi-playback if you want to repeatedly spawn a set of entities. To do this, create and configure a set of new entities with an `EntityCommandBuffer`, and then repeat playback to respawn another matching set of entities. + +## Additional resources + +* [Automatically play back entity command buffers](systems-entity-command-buffer-automatic-playback.md) diff --git a/Documentation~/systems-entity-command-buffer-use.md b/Documentation~/systems-entity-command-buffer-use.md new file mode 100644 index 00000000..72367ea9 --- /dev/null +++ b/Documentation~/systems-entity-command-buffer-use.md @@ -0,0 +1,34 @@ +# Use an entity command buffer + +You can record [entity command buffers](systems-entity-command-buffers.md) (ECBs) in jobs, and on the main thread. + +## Use an entity command buffer in a job + +You can't perform [structural changes](concepts-structural-changes) in a job, except inside an `ExclusiveEntityTransaction`, so you can use an ECB to record structural changes to play back after the job is complete. For example: + +[!code-cs[conversion](../DocCodeSamples.Tests/EntityCommandBuffers.cs#ecb_single_threaded)] + +### Parallel jobs + +If you want to use an ECB in a [parallel job](xref:JobSystemParallelForJobs), use [`EntityCommandBuffer.ParallelWriter`](xref:Unity.Entities.EntityCommandBuffer.ParallelWriter), which concurrently records in a thread-safe way to a command buffer: + +[!code-cs[conversion](../DocCodeSamples.Tests/EntityCommandBuffers.cs#ecb_parallel)] + +>[!NOTE] +>Only recording needs to be thread-safe for concurrency in parallel jobs. Playback is always single-threaded on the main thread. + +For information on deterministic playback in parallel jobs, refer to the documentation on [Entity command buffer playback](systems-entity-command-buffer-playback.md#deterministic-playback-in-parallel-jobs) + +## Use an entity command buffer on the main thread + +You can record ECB changes on the main thread, such as in the following situations: + +* To delay your changes. +* To play back a set of changes multiple times. To do this, refer to the information on [multi-playback](systems-entity-command-buffer-playback.md#multi-playback). +* To play back a lot of different kinds of changes in one consolidated place. This is more efficient than interspersing the changes across different parts of the frame. + +Every structural change operation triggers a [sync point](concepts-structural-changes.md#sync-points), which means that the operation must wait for some or all scheduled jobs to complete. If you combine the structural changes into an ECB, the frame has fewer sync points. + +>[!NOTE] +> If you have a lot of the same types of commands in an ECB, and you can afford to make the change instantly, it can be faster to use the EntityManager variants on whole batches of entities at once. + diff --git a/Documentation~/systems-entity-command-buffers.md b/Documentation~/systems-entity-command-buffers.md index 0536b7c4..596a1651 100644 --- a/Documentation~/systems-entity-command-buffers.md +++ b/Documentation~/systems-entity-command-buffers.md @@ -2,193 +2,39 @@ uid: systems-entity-command-buffers --- -# Scheduling data changes with an EntityCommandBuffer +# Entity command buffer overview -To queue entity data changes instead of performing the changes instantly, you can use the [`EntityCommandBuffer`](xref:Unity.Entities.EntityCommandBuffer) struct, which creates a thread-safe command buffer. This is useful if you want to defer any [structural changes](concepts-structural-changes.md) while jobs complete. +An entity command buffer (ECB) stores a queue of thread-safe commands which you can add to and later play back. You can use an ECB to schedule [structural changes](concepts-structural-changes.md) from jobs and perform changes on the main thread after the jobs complete. You can also use ECBs on the main thread to delay changes, or play back a set of changes multiple times. -## EntityCommandBuffer methods +The [methods in `EntityCommandBuffer`](xref:Unity.Entities.EntityCommandBuffer.) record commands, which mirror methods available in [`EntityManager`](xref:Unity.Entities.EntityManager.html). For example: -You can use the methods in `EntityCommandBuffer` to record commands, which mirror some of the [`EntityManager`](xref:Unity.Entities.EntityManager) methods, for example: +* `CreateEntity(EntityArchetype)`: Registers a command that creates a new entity with the specified archetype. +* `DestroyEntity(Entity)`: Registers a command that destroys the entity. +* `SetComponent<T>(Entity, T)`: Registers a command that sets the value for a component of type `T` on the entity. +* `AddComponent<T>(Entity)`: Registers a command that adds a component of type `T` to the entity. +* `RemoveComponent<T>(EntityQuery)`: Registers a command that removes a component of type `T` from all entities that match the query. -- `CreateEntity(EntityArchetype)`: Creates a new entity with the specified archetype. -- `DestroyEntity(Entity)`: Destroys the entity. -- `SetComponent(Entity, T)`: Sets the value for a component of type `T` on the entity. -- `AddComponent(Entity)`: Adds a component of type `T` to the entity. -- `RemoveComponent(EntityQuery)`: Removes a component of type `T` from all entities that match the query. +## Comparison between EntityCommandBuffer and EntityManager -Unity only makes the changes recorded in an `EntityCommandBuffer` when it calls the `Playback` method on the main thread. If you attempt to record any further changes to the command buffer after playback, then Unity throws an exception. +You can use `EntityCommandBuffer` or `EntityManager` to manage structural changes. The difference between the two options are as follows: -`EntityCommandBuffer` has a job safety handle, similar to a [native container](https://docs.unity3d.com/Manual/JobSystemNativeContainer.html). The safety checks throw an exception if you try to do any of the following on an incomplete scheduled job that uses a command buffer: +* If you want to queue up structural changes from a job, [you must use an ECB](systems-entity-command-buffer-use.md). +* If you want to perform structural changes on the main thread, and have them happen instantly, use the methods in `EntityManager`. +* If you want to perform structural change on the main thread, and you want them to happen at a later point (such as after a job completes), you should use an ECB. -* Access the `EntityCommandBuffer` through its `AddComponent`, `Playback`, `Dispose`, or other methods. -* Schedule another job that accesses the same `EntityCommandBuffer`, unless the new job depends on the already scheduled job. - -## Use EntityCommandBuffer in a single-threaded job - -Unity can't perform [structural changes](concepts-structural-changes.md) in a job, so you can use a command buffer for entities to defer structural changes until Unity completes the job. For example: - -[!code-cs[conversion](../DocCodeSamples.Tests/EntityCommandBuffers.cs#ecb_single_threaded)] - -## Use EntityCommandBuffer in a parallel job - -If you want to use an entity command buffer in a parallel job, use `EntityCommandBuffer.ParallelWriter`, which concurrently records in a thread-safe way to a command buffer: - -```c# -EntityCommandBuffer ecb = new EntityCommandBuffer(Allocator.TempJob); - -// Methods of this writer record commands to -// the EntityCommandBuffer in a thread-safe way. -EntityCommandBuffer.ParallelWriter parallelEcb = ecb.AsParallelWriter(); -``` - -> [!NOTE] -> Only recording needs to be thread-safe for concurrency. Playback is always single-threaded on the main thread. - -### Deterministic playback - -Because recording of the commands is split across threads, the order of recorded commands depends on job scheduling, so is non-deterministic. - -Determinism isn't always essential, but code which produces deterministic results is easier to debug. There are also networking scenarios which require consistent results across different machines. However, determinism can has an impact on performance, so you might need to accept indeterminism in some projects. - -You can't avoid the indeterminate order of recording, but you can make the playback order of the commands deterministic in the following way: - -1. Each command records a 'sort key' `int` passed as the first argument to each command method. You must call the lambda parameter `entityInQueryIndex`, or `Entities.ForEach` won't be able to recognize the int. -1. On playback, sort the commands by their sort keys before the commands are enacted. - -As long as the recorded sort keys are independent from the scheduling, the sorting makes the playback order deterministic. - -In a parallel job, the sort key you need for each entity is a number that has a fixed and unique association with that entity in the job's query. - -The `entityInQueryIndex` value provided in a parallel job meets those criteria. In the list of archetype chunks that match the job's query, entities have the following indexes: - -- The first entity of the first chunk has `entityInQueryIndex` 0 -- The second entity of the first chunk has `entityInQueryIndex` 1 -- The first entity of the second chunk has an `entityInQueryIndex` which is the count of the first chunk -- The first entity of the third chunk has an `entityInQueryIndex` which is the sum of the counts of the first two chunks - -The `entityInQueryIndex` follows this pattern throughout. - -The following example code shows an entity command buffer used in a parallel job: - -[!code-cs[conversion](../DocCodeSamples.Tests/EntityCommandBuffers.cs#ecb_multi_threaded)] - - -## Reusing an EntityCommandBuffer instance - -It's best practice to give each job its own command buffer. This is because recording a set of commands to several command buffers has little overhead compared to recording the same commands in a single command buffer. - -However, you can reuse the same `EntityCommandBuffer` in non-parallel jobs, as long as those jobs don't overlap in scheduling. If you reuse an `EntityCommandBuffer` instance in a parallel job, this might lead to unexpected sort orders of the commands in playback, unless the sort keys for each job are in different ranges. - -## Multi-playback - -If you call the `Playback` method more than once, it throws an exception. To avoid this, create an `EntityCommandBuffer` instance with the `PlaybackPolicy.MultiPlayback` option: - -[!code-cs[conversion](../DocCodeSamples.Tests/EntityCommandBuffers.cs#ecb_multi_playback)] - -Multi-playback is useful if you want to repeatedly spawn a set of entities. To do this, create and configure a set of new entities with an `EntityCommandBuffer`, and then repeat playback to respawn another matching set of entities. - -## Use an EntityCommandBuffer on the main thread +The changes recorded in an ECB only are applied when `Playback` is called on the main thread. If you try to record any further changes to the ECB after playback, then Unity throws an exception. -You can record command buffer changes on the main thread. This can be useful in the following situations: +## Entity command buffer safety -- To delay your changes. -- To play back a set of changes multiple times. -- To play back a lot of changes in one consolidated place. This is more efficient than interspersing the changes across different parts of the frame. +`EntityCommandBuffer` has a job safety handle, similar to a [native container](xref:JobSystemNativeContainer). This safety is only available in the Editor, and not in player builds. The safety checks throw an exception if you try to do any of the following on an incomplete scheduled job that uses an ECB: -Every structural change operation triggers a [sync point](concepts-structural-changes.md#sync-points), which means that the operation must wait for some or all scheduled jobs to complete. If you combine the structural changes into a command buffer, the frame has fewer sync points. - -## Automatically playback and dispose of command buffers with EntityCommandBufferSystem - -You can use [`EntityCommandBufferSystem`](xref:Unity.Entities.EntityCommandBufferSystem) to play back and dispose of a command buffer rather than manually doing it yourself. To do this: - -1. Get the instance of the `EntityCommandBufferSystem` which you want to do the playback. -1. Create an `EntityCommandBuffer` instance via the system. -1. Schedule a job that writes commands to the `EntityCommandBuffer`. -1. Register the scheduled job for the system to complete. - -For example: - -[!code-cs[conversion](../DocCodeSamples.Tests/EntityCommandBuffers.cs#ecb_from_ecbsystem)] - -> [!IMPORTANT] -> Don't manually play back and dispose an `EntityCommandBuffer` that an `EntityCommandBufferSystem` has created. `EntityCommandBufferSystem` does both for you. - -In each update, an `EntityCommandBufferSystem`: - -1. Completes all registered jobs, which ensures that they have finished their recording). -1. Playbacks all entity command buffers created via the system in the same order they were created. -1. Disposes of the `EntityCommandBuffer`. - -### Default `EntityCommandBufferSystem` systems - -The default [world](concepts-worlds.md) has the following default `EntityCommandBufferSystem` systems: - -- `BeginInitializationEntityCommandBufferSystem` -- `EndInitializationEntityCommandBufferSystem` -- `BeginSimulationEntityCommandBufferSystem` -- `EndSimulationEntityCommandBufferSystem` -- `BeginPresentationEntityCommandBufferSystem` - -Because structural changes can't happen in the frame after Unity hands off the rendering data to the renderer, there's no `EndPresentationEntityCommandBufferSystem` system. You can use `BeginInitializationEntityCommandBufferSystem` instead: the end of one frame is the beginning of the next frame. - -These update at the beginning and end of the standard [system groups](concepts-systems.md#system-groups). For more information, see the documentation on [System update order](systems-update-order.md). - -The default systems should be enough for most use cases, but you can create your own `EntityCommandBufferSystem` if necessary: - -[!code-cs[conversion](../DocCodeSamples.Tests/EntityCommandBuffers.cs#ecb_define_ecbsystem)] - -## Deferred entities - -The `EntityCommandBuffer` methods `CreateEntity` and `Instantiate` record commands that create entities. These methods only record commands and don't create entities. As such, they return `Entity` values with negative indexes that represent placeholder entities that don't exist yet. These placeholder `Entity` values are only meaningful in recorded commands of the same ECB. - -[!code-cs[conversion](../DocCodeSamples.Tests/EntityCommandBuffers.cs#ecb_deferred_entities)] - -Values recorded in an `AddComponent`, `SetComponent`, or `SetBuffer` command might have `Entity` fields. In playback, Unity remaps any placeholder `Entity` values in these components or buffers to the corresponding actual entities. - -[!code-cs[conversion](../DocCodeSamples.Tests/EntityCommandBuffers.cs#ecb_deferred_remapping)] - -## Use command buffers in the `Entities.ForEach` method -To use a command buffer in the [`Entities.ForEach`](iterating-data-entities-foreach.md) method, pass an `EntityCommandBuffer` parameter to the lambda expression itself. Only a small subset of `EntityCommandBuffer` methods are supported, and they have the `[SupportedInEntitiesForEach]` attribute: - -- `Entity Instantiate(Entity entity)` -- `void DestroyEntity(Entity entity)` -- `void AddComponent(Entity e, T component) where T : unmanaged, IComponentData` -- `void SetComponent(Entity e, T component) where T : unmanaged, IComponentData` -- `void RemoveComponent(Entity e)` - -For example, the following code does this: -1. It checks each entity to see whether its `HealthLevel` is 0. -1. If true, it records a command to destroy the entity. -1. It also specifies that the `EndSimulationEntityCommandBufferSystem` should play back the command. - -```c# -public struct HealthLevel : IComponentData -{ - public int Value; -} - -Entities - .WithDeferredPlaybackSystem - .ForEach( - (Entity entity, EntityCommandBuffer buffer, HealthLevel healthLevel) => - { - if (healthLevel == 0) - { - buffer.DestroyEntity(entity); - } - } - ).ScheduleParallel(); -``` - -When you use any of these methods within a `ForEach()` function, at runtime the compiler generates the code necessary to create, populate, play back, and dispose of an `EntityCommandBuffer` instance, or an `EntityCommandBuffer.ParallelWriter` instance, if `ScheduleParallel()` is called. - -Invoking these methods outside of `ForEach()` results in an exception. - -### Play back an `EntityCommandBuffer` in `Entities.forEach` +* Access the `EntityCommandBuffer` through its `AddComponent`, `Playback`, `Dispose`, or other methods. +* Schedule another job that accesses the same `EntityCommandBuffer`, unless the new job [depends on](xref:JobSystemJobDependencies) the already scheduled job. -To pass an `EntityCommandBuffer` parameter to the `ForEach()` function, you must also call one of the following methods to specify when you want to play back the commands: +>[!NOTE] +>It’s best practice to use a separate ECB for each distinct job. This is because if you reuse an ECB in consecutive jobs, the jobs might use an overlapping set of sort keys (such as if both use `ChunkIndexInQuery`), and the commands that the jobs record might be interleaved. -- **Deferred playback:** Call `WithDeferredPlaybackSystem()`, where `T` identifies the entity command buffer system that plays back the commands. It must be a type that derives from `EntityCommandBufferSystem`. -- **Immediate playback:** call `WithImmediatePlayback()` to execute the entity commands immediately after the `ForEach()` function has finished all iterations. You can only use `WithImmediatePlayback()` with `Run()`. +## Additional resources -The compiler automatically generates code to create and dispose of any `EntityCommandBuffer` instances. +* [Use an entity command buffer](systems-entity-command-buffer-use.md) +* [Entity command buffer playback](systems-entity-command-buffer-playback.md) \ No newline at end of file diff --git a/Documentation~/systems-icustombootstrap.md b/Documentation~/systems-icustombootstrap.md new file mode 100644 index 00000000..9ebf1c7a --- /dev/null +++ b/Documentation~/systems-icustombootstrap.md @@ -0,0 +1,32 @@ +# Manage systems in multiple worlds + +You can create multiple [worlds](concepts-worlds.md), and you can instantiate the same system type(s) in more than one world. You can also update each system at different rates from different points in the update order. The [Netcode package](https://docs.unity3d.com/Packages/com.unity.netcode@latest) uses this to create separate worlds for client and server running in the same process. +Doing this manually in user code is uncommon, and an advanced use case. + +To do this, you can use the [`ICustomBootstrap`](xref:Unity.Entities.ICustomBootstrap) interface to manage systems in multiple worlds. The Netcode package contains an implementation example which you can refer to. + +When you implement this interface, Unity calls it before the default world initialization and uses the return value to determine if the default world initialization should run: + +``` c# +public interface ICustomBootstrap +{ + // Create your own set of worlds or your own custom default world in this method. + // If true is returned, the default world bootstrap doesn't run at all and no additional worlds are created. + bool Initialize(string defaultWorldName); +} +``` + +You can use a custom bootstrapper to create worlds, get a filtered list of systems from +`DefaultWorldInitialization.GetAllSystems`, and add a set of systems to a world with +[`DefaultWorldInitialization.AddSystemsToRootLevelSystemGroups`](xref:Unity.Entities.DefaultWorldInitialization.AddSystemsToRootLevelSystemGroups*). You don't need to add the same list of systems that `DefaultWorldInitialization.GetAllSystems` returns and you can add +or remove systems to modify the list. You can also create your own list of systems without using `DefaultWorldInitialization`. + +The following is a typical procedure of a custom `MyCustomBootstrap.Initialize` implementation: + +1. Create the set of worlds you want your game or application to have. +1. For each created world: + 1. Generate a list of systems you want in that world. You can use [`DefaultWorldInitialization.GetAllSystems`](xref:Unity.Entities.DefaultWorldInitialization.GetAllSystems*) but it isn't required. + 1. Call `DefaultWorldInitialization.AddSystemsToRootLevelSystemGroups` to add the list of systems to the world. +This also creates the systems in an order that respects [`CreateAfter`](xref:Unity.Entities.CreateAfterAttribute)/[`CreateBefore`](xref:Unity.Entities.CreateBeforeAttribute). + 1. If you don't want to manually update the world, call `ScriptBehaviourUpdateOrder.AppendWorldToCurrentPlayerLoop` to add the world to the player loop. +1. If you created the default world, set `World.DefaultGameObjectInjectionWorld` to the default world and return `true`. If you didn't create the default world and want the default bootstrap to do that for you, return `false`. diff --git a/Documentation~/systems-intro.md b/Documentation~/systems-intro.md index 77140813..936c65ed 100644 --- a/Documentation~/systems-intro.md +++ b/Documentation~/systems-intro.md @@ -13,6 +13,7 @@ Understand the different options to create systems, and best practices around wo |[Using SystemBase](systems-systembase.md)|Work with `SystemBase` based systems.| |[Define and manage system data](systems-data.md)|Access system data in components.| |[System groups](systems-update-order.md)|Understand how to group systems and their update order.| +|[Manage systems in multiple worlds](systems-icustombootstrap.md)|Understand how to manage systems in multiple worlds.| ## Additional resources diff --git a/Documentation~/systems-schedule-changes.md b/Documentation~/systems-schedule-changes.md new file mode 100644 index 00000000..93588545 --- /dev/null +++ b/Documentation~/systems-schedule-changes.md @@ -0,0 +1,10 @@ +# Schedule data changes + +You can schedule data changes to defer [structural changes](concepts-structural-changes.md), delay changes, or play back a set of changes multiple times with an [entity command buffer](systems-entity-command-buffers.md). + +|**Topic**|**Description**| +|---|---| +|[Entity command buffer overview](systems-entity-command-buffers.md)|Understand entity command buffers.| +|[Use an entity command buffer](systems-entity-command-buffer-use.md)|Use an entity command buffer in a job or on the main thread.| +|[Entity command buffer playback](systems-entity-command-buffer-playback.md)|Understand Unity plays back entity command buffers.| +|[Automatic playback and disposal of entity command buffers](systems-entity-command-buffer-automatic-playback.md)|Automatically play back and dispose of entity command buffers.| diff --git a/Documentation~/systems-systemapi-query.md b/Documentation~/systems-systemapi-query.md index 5cb80942..1971dfc2 100644 --- a/Documentation~/systems-systemapi-query.md +++ b/Documentation~/systems-systemapi-query.md @@ -15,58 +15,29 @@ You can overload the method with up to seven type parameters. The supported type ## SystemAPI.Query implementation -Whenever you invoke `SystemAPI.Query`, the source-generator solution creates an `EntityQuery` field on the system itself, and caches an `EntityQuery` that consists of the queried types, with their respective read-write/read-only access modes, in this field. During compilation, the source-generation solution replaces the `SystemAPI.Query` invocation in a `foreach` statement with an enumerator that iterates through the cached query’s data. +Whenever you invoke `SystemAPI.Query`, the source generator solution creates an `EntityQuery` field on the system itself. It also caches an `EntityQuery` that consists of the queried types, with their respective read-write/read-only access modes in this field. During compilation, the source-generation solution replaces the `SystemAPI.Query` invocation in a `foreach` statement with an enumerator that iterates through the cached query’s data. Additionally, the source-generation solution caches all the required type handles, and automatically injects `TypeHandle.Update(SystemBase system)` or `TypeHandle.Update(ref SystemState state)` as necessary before every `foreach`. This ensures that the type handles are safe to use. The source generators also generate code to automatically complete all necessary read and read-write dependencies before each `foreach` statement. -## Use SystemAPI.Query to query data +## Query data -The following is an example that uses `SystemAPI.Query` to iterate through every entity that has both `LocalToWorldTransform` and `RotationSpeed` components: +The following is an example that uses `SystemAPI.Query` to iterate through every entity that has both `LocalTransform` and `RotationSpeed` components: -```cs -public partial struct RotationSpeedSystem : ISystem -{ - [BurstCompile] - public void OnCreate(ref SystemState state) - { - } +[!code-cs[](../DocCodeSamples.Tests/SystemAPIExamples.cs#query-data)] - [BurstCompile] - public void OnDestroy(ref SystemState state) - { - } +Because the example modifies the `LocalTransform` data, it's wrapped inside `RefRW`, as a read-write reference. However, because it only reads the `RotationSpeed` data, it uses `RefRO`. `RefRO` usage is entirely optional and you can use the following instead as valid code: - [BurstCompile] - public void OnUpdate(ref SystemState state) - { - float deltaTime = SystemAPI.Time.DeltaTime; - - foreach (var (transform, speed) in SystemAPI.Query, RefRO>()) - transform.ValueRW.Value = transform.ValueRO.Value.RotateY(speed.ValueRO.RadiansPerSecond * deltaTime); - } -} -``` - -Because the example modifies the `LocalToWorldTransform` data, it's wrapped inside `RefRW`, as a read-write reference. However, because it only reads the `RotationSpeed` data, it uses `RefRO`. `RefRO` usage is entirely optional: you could use the following instead as valid code: - -```c# -`foreach (var (transform, speed) in SystemAPI.Query, RotationSpeed)` -``` +[!code-cs[](../DocCodeSamples.Tests/SystemAPIExamples.cs#query-data-alt)] [`RefRW.ValueRW`](xref:Unity.Entities.RefRW`1.ValueRW*), [`RefRW.ValueRO`](xref:Unity.Entities.RefRW`1.ValueRO*), and [`RefRO.ValueRO`](xref:Unity.Entities.RefRO`1.ValueRO*) all return a reference to the component. When called, `ValueRW` conducts a safety check for read-write access, and `ValueRO` does the same for read-only access. -## Accessing entities in the `foreach` statement +## Accessing entities in the foreach statement `Unity.Entities.Entity` isn't a supported type parameter. Every query is already an implicit filter of all existing entities. To get access to the entity, use [`WithEntityAccess`](xref:Unity.Entities.QueryEnumerable`1.WithEntityAccess*). For example: -```c# -foreach (var (transform, speed, entity) in SystemAPI.Query, RefRO>().WithEntityAccess()) -{ - // Do stuff; -} -``` +[!code-cs[](../DocCodeSamples.Tests/SystemAPIExamples.cs#entity-access)] Note that the `Entity` argument comes last in the returned tuple. @@ -78,39 +49,8 @@ Note that the `Entity` argument comes last in the returned tuple. `DynamicBuffer` type parameters in `SystemAPI.Query` are read-write access by default. However, if you want read-only access, you have to create your own implementation, similar to the following: -```c# -var bufferHandle = systemState.GetBufferTypeHandle(isReadOnly: true); -var myBufferElementQuery = SystemAPI.QueryBuilder().WithAll().Build(); -var chunks = myBufferElementQuery.ToArchetypeChunkArray(Allocator.Temp); - -foreach (var chunk in chunks) -{ - var numEntities = chunk.Count; - var bufferAccessor = chunk.GetBufferAccessor(bufferHandle); - - for (int j = 0; j < numEntities; j++) - { - var dynamicBuffer = bufferAccessor[j]; - // Read from `dynamicBuffer` and perform various operations - } -} -``` +[!code-cs[](../DocCodeSamples.Tests/SystemAPIExamples.cs#dynamic-buffer)] ### Reusing SystemAPI.Query -You can't store `SystemAPI.Query()` in a variable and then use it in one or more multiple `foreach` statements. This is because the implementation of the API relies on knowing what the query types are at compile time. For example: - -```c# -var myQuery = SystemAPI.Query(); - -if (firstCondition) - myQuery = myQuery.WithNone(); -if (secondCondition) - myQuery = myQuery.WithAll(); -if (thirdCondition) - myQuery = myQuery.WithAny(); - -foreach (var element in myQuery) { // Do stuff; } -``` - -The source-generation solution doesn't know at compile-time what `EntityQuery` to generate and cache, which type handles it should call `Update` on, nor which dependencies to complete. It's also performance intensive to check whether `myQuery` is always used, without additional chaining with `.WithXXX()` methods. +You can't store `SystemAPI.Query` in a variable and then use it in multiple `foreach` statements: there isn't a way to reuse `SystemAPI.Query`. This is because the implementation of the API relies on knowing what the query types are at compile time. The source-generation solution doesn't know at compile-time what `EntityQuery` to generate and cache, which type handles to call `Update` on, nor which dependencies to complete. diff --git a/Documentation~/systems-update-order.md b/Documentation~/systems-update-order.md index eb638b02..4459dd0f 100644 --- a/Documentation~/systems-update-order.md +++ b/Documentation~/systems-update-order.md @@ -4,13 +4,31 @@ uid: systems-update-order # System groups -A system group can have systems and other system groups as its children. A system group has an update method that you can override, and the base method updates the group's children in a sorted order. +A system group can have systems and other system groups as its children. A system group updates its children in a sorted order on the main thread. -To create a system group, create a class that inherits from [`ComponentSystemGroup`](xref:Unity.Entities.ComponentSystemGroup). Because systems belong to a world, you must use [`World.GetOrCreateSystem`](xref:Unity.Entities.World.GetOrCreateSystem*) to create a system. To add a system to a group, use `group.AddSystemToUpdateList`. You can add other system groups to existing system groups. +To create your own system group, create a class that inherits from [`ComponentSystemGroup`](xref:Unity.Entities.ComponentSystemGroup). Use the [`UpdateInGroup`](xref:Unity.Entities.UpdateInGroupAttribute) attribute on the member systems to specify which systems need to be updated in a given group. Using the `UpdateInGroup` attribute ensures that Unity creates all systems in an order that respects the [`CreateAfter`](xref:Unity.Entities.CreateAfterAttribute) and [`CreateBefore`](xref:Unity.Entities.CreateBeforeAttribute) attributes. + +## Creation order of systems + +By default, Unity creates systems in an order that doesn't respect system groups, but does respect `CreateAfter` and `CreateBefore` attributes. + +`OnCreate` methods are called in the same order as systems are created. + +It's best practice to use the [`CreateAfter`](xref:Unity.Entities.CreateAfterAttribute) and [`CreateBefore`](xref:Unity.Entities.CreateBeforeAttribute) attributes, and either the default world creation or [`ICustomBootstrap`](systems-icustombootstrap.md) for advanced use cases. This is the recommended way of creating systems over calling [`World.GetOrCreateSystem`](xref:Unity.Entities.World.CreateSystem*). + +This reduces the likelihood of errors such as if the system creation order changes to another one that also satisfies the attribute constraints. If you must refer to another system in your `OnCreate` method, you should use `CreateAfter` to ensure that your system is created after the other system you're referring to, and then use [`World.GetExistingSystem`](xref:Unity.Entities.World.GetExistingSystem*) to retrieve it. + +More commonly, you might want to access a [singleton component](components-singleton.md) or other resource that another system creates in that system's `OnCreate` method. You can add `[CreateAfter(typeof(OtherSystem))]` to the system type to ensure that your `OnCreate` method runs after `OtherSystem.OnCreate`. + +## Destruction order of systems + +When you call [`World.Dispose`](xref:Unity.Entities.World.Dispose), Unity destroys the systems in reverse order of their actual creation. Unity destroys them in this order even if their creation broke the `CreateAfter` or `CreateBefore` constraints (for example if you manually called `CreateSystem` out of order). ## Update order of systems -Every time you add a group to a system group, it re-sorts the system update order. To control the update order of a system group, add the [`UpdateBefore`](xref:Unity.Entities.UpdateBeforeAttribute) or [`UpdateAfter`](xref:Unity.Entities.UpdateAfterAttribute) attribute to a system to specify which systems it should update before or after. These attributes only apply relative to children of the same system group. For example: +Every time you add a group to a system group, the group re-sorts the system update order for that group before updating again. To control the update order of a system group, add the [`UpdateBefore`](xref:Unity.Entities.UpdateBeforeAttribute) or [`UpdateAfter`](xref:Unity.Entities.UpdateAfterAttribute) attribute to a system to specify which systems it should update before or after. These attributes only apply relative to direct children of the same system group. + +You can also use the [`OrderFirst`](xref:Unity.Entities.UpdateInGroupAttribute.OrderFirst) or [`OrderLast`](xref:Unity.Entities.UpdateInGroupAttribute.OrderLast) parameters to customize the update order, and these take precedence over `UpdateBefore` and `UpdateAfter`. For example: ```c# // If PeachSystem and DaisySystem are children of the same group, then the @@ -20,96 +38,45 @@ Every time you add a group to a system group, it re-sorts the system update orde public partial class PeachSystem : SystemBase { } ``` -There are a set of [default system groups](#default-system-groups) that you can use to update systems in the correct phase of a frame. You can nest one group inside another so that all systems in your group update in the correct phase and update according to the order within their group. - -## Component system groups - -The [`ComponentSystemGroup`](xref:Unity.Entities.ComponentSystemGroup) class represents a list of related component systems that Unity must update together in a specific order. `ComponentSystemGroup` inherits from [`ComponentSystemBase`](xref:Unity.Entities.ComponentSystemBase), so you can order it relative to other systems, and it has an `OnUpdate()` method. This also means that you can nest a `ComponentSystemGroup` in another `ComponentSystemGroup`, and form a hierarchy. - -By default, when you call the `Update()` method in `ComponentSystemGroup`, it calls `Update()` on each system in its sorted list of member systems. If any member systems are system groups, they recursively update their own members. The resulting system ordering follows a depth-first traversal of a tree. - -## System ordering attributes +Because everything in a group's update list updates together, the global order of system updates represents a hierarchical ordering, where all direct children of a group are ordered first by `OrderFirst` and `OrderLast`, and then by `UpdateBefore` and `UpdateAfter` constraints. Because the children can also be system groups, these children groups might update many grandchild systems before returning to the current group's update list. -You can use the following attributes on a system to determine its update order: +In the Editor, you can use the Systems window (**Window > Entities > Systems**) to see the full list of system groups in your application, and their ordering: -|**Attribute**|**Description**| -|---|---| -|`UpdateInGroup`| Specify a `ComponentSystemGroup` that this system should be a member of. If you don't set this attribute, Unity automatically adds it to the default world's `SimulationSystemGroup`. The optional `OrderFirst` and `OrderLast` parameters allow systems to be sorted before or after all other systems in the group. For more information, see the section on [Default system groups](#default-system-groups).| -|`UpdateBefore`
`UpdateAfter`| Order systems relative to other systems. The system type specified for these attributes must be a member of the same group. Unity handles ordering across group boundaries at the appropriate deepest group that contains both systems.

For example, if `CarSystem` is in `CarGroup`, and `TruckSystem` is in `TruckGroup`, and `CarGroup` and `TruckGroup` are both members of `VehicleGroup`, then the ordering of `CarGroup` and `TruckGroup` implicitly determines the relative ordering of `CarSystem` and `TruckSystem`. You don't need to explicitly order the systems.| -|`CreateBefore`
`CreateAfter`| Order system creation relative to other systems. The same ordering rules for `UpdateBefore` and `UpdateAfter` apply here. By default, systems are created in the same order they are updated. These attributes override the default behavior. System destruction order is defined as the reverse of creation order.| -|`DisableAutoCreation`|Prevents Unity from creating the system during the default world initialization. You must explicitly create and update the system. However, you can add a system with this tag to a `ComponentSystemGroup`’s update list, and it automatically updates just like the other systems in that list.| +![](images/editor-system-window.png)
_Systems window showing hierarchical ordering of system updates_ -If you add the `DisableAutoCreation` attribute to a component system or system group, Unity doesn't create it or add it to the default system groups. To manually create the system, use [`World.GetOrCreateSystem()`](xref:Unity.Entities.World.GetOrCreateSystem*) and call `MySystem.Update()` from the main thread to update it. You can use this to insert systems elsewhere in the Unity player loop, for example, if you have a system that should run later or earlier in the frame. +For more information, see the [Systems window](editor-systems-window.md) documentation. ## Default system groups -The default World contains a hierarchy of `ComponentSystemGroup` instances. There are three root-level system groups in the Unity player loop: - -* InitializationSystemGroup: Updates at the end of the `Initialization` phase of the player loop. -* SimulationSystemGroup: Updates at the end of the `Update` phase of the player loop. -* PresentationSystemGroup: Updates at the end of the `PreLateUpdate` phase of the player loop. +There is a set of default system groups that you can use to update systems in the correct +phase of a frame. You can nest one system group inside another so that all systems in a group update in the correct phase and update according to the order within their group. -The default system groups also have a number of pre-defined member systems: +The default world contains a hierarchy of `ComponentSystemGroup` instances. There are three root-level system groups in the Unity player loop: -**InitializationSystemGroup:** +* **InitializationSystemGroup**: Updates at the end of the `Initialization` phase of the player loop. +* **SimulationSystemGroup**: Updates at the end of the `Update` phase of the player loop. +* **PresentationSystemGroup**: Updates at the end of the `PreLateUpdate` phase of the player loop. -* BeginInitializationEntityCommandBufferSystem -* CopyInitialTransformFromGameObjectSystem -* SubSceneLiveConversionSystem -* SubSceneStreamingSystem -* EndInitializationEntityCommandBufferSystem +## System ordering attributes -**SimulationSystemGroup:** +You can use the following attributes on a system to determine its update order: -* BeginSimulationEntityCommandBufferSystem -* FixedStepSimulationSystemGroup -* VariableRateSimulationSystemGroup -* TransformSystemGroup - * ParentSystem - * LocalToWorldSystem -* LateSimulationSystemGroup -* EndSimulationEntityCommandBufferSystem +|**Attribute**| **Description**| +|---|---| +|`UpdateInGroup`| Specify a `ComponentSystemGroup` that this system should be a member of. If you don't set this attribute, it's automatically be added to the default world's `SimulationSystemGroup`. You can use the optional `OrderFirst` and `OrderLast` parameters to sort systems before or after all other systems in the group that don't have the same parameter set.| +|`UpdateBefore`
`UpdateAfter`| Order systems relative to other systems within the same group. When you apply this attribute to groups, they implicitly constrain all member systems inside them.

For example, if `CarSystem` is in `CarGroup`, and `TruckSystem` is in `TruckGroup`, and `CarGroup` and `TruckGroup` are both members of `VehicleGroup`, then the ordering of `CarGroup` and `TruckGroup` implicitly determines the relative ordering of `CarSystem` and `TruckSystem`. | +|`CreateBefore`
`CreateAfter`| Order system creation relative to other systems. The same ordering rules for `UpdateBefore` and `UpdateAfter` apply here, except that there are no groups, and no `OrderFirst`/`OrderLast`. These attributes override the default behavior. System destruction order is defined as the reverse of creation order. | +|`DisableAutoCreation`| Prevents Unity from creating the system during the default world initialization. See [Manual system creation](#manual-system-creation) below.| -**PresentationSystemGroup:** +## Manual system creation -* BeginPresentationEntityCommandBufferSystem -* CreateMissingRenderBoundsFromMeshRenderer -* RenderingSystemBootstrap -* RenderBoundsUpdateSystem -* RenderMeshSystem -* LODGroupSystemV1 -* LodRequirementsUpdateSystem -* EndPresentationEntityCommandBufferSystem +If you need to create a system manually, mark the system in question with the [`DisableAutoCreation`](xref:Unity.Entities.DisableAutoCreationAttribute), and then you can use [`World.CreateSystem`](xref:Unity.Entities.World.CreateSystem*) to create a system. To add a system to a group, use the [`AddSystemToUpdateList`](xref:Unity.Entities.ComponentSystemGroup.AddSystemToUpdateList*) method. You can add also other system groups to existing system groups. -Note that the specific contents of this list is subject to change. +One problem with this approach is that it doesn't guarantee that systems get created in an order that respects the `CreateAfter` and `CreateBefore` attributes on each system. ## Multiple worlds -You can create multiple [worlds](concepts-worlds.md) and you can instantiate the same component system class in more than one world. You can also update each instance at different rates from different points in the update order. - -You can't manually update every system in a given world, but you can control which systems are created in which world, and which of the existing system groups to add them to. - -For example, you can create a custom world that instantiates `SystemX` and `SystemY`, and add `SystemX` to the default world’s SimulationSystemGroup, and add `SystemY` to the default world’s PresentationSystemGroup. These systems can order themselves relative to their group siblings as usual, and Unity updates them along with the corresponding group. - -You can also use the [`ICustomBootstrap`](xref:Unity.Entities.ICustomBootstrap) interface to manage systems in multiple worlds: - -``` c# -public interface ICustomBootstrap -{ - // Create your own set of worlds or your own custom default world in this method. - // If true is returned, the default world bootstrap doesn't run at all and no additional worlds are created. - bool Initialize(string defaultWorldName); -} -``` -When you implement this interface, Unity calls it before the default world initialization and uses the return value to determine if the default world initialization should run. - -You can use a custom bootstrapper to create worlds, get a filtered list of systems from `DefaultWorldInitialization.GetAllSystems`, and add a set of systems to a world with [`DefaultWorldInitialization.AddSystemsToRootLevelSystemGroups`](xref:Unity.Entities.DefaultWorldInitialization.AddSystemsToRootLevelSystemGroups*). You don't need to add the same list of systems that `DefaultWorldInitialization.GetAllSystems` returns and you can add or remove systems to modify the list. You can also create your own list of systems without using `DefaultWorldInitialization`. +You can create multiple [worlds](concepts-worlds.md) and you can instantiate the same component system class in more than one world. You can also update each instance at different rates from different points in the update order. The [Netcode package](https://docs.unity3d.com/Packages/com.unity.netcode@latest) uses this to create separate worlds for client and server running in the same process. -For example, here’s a typical procedure of a custom `MyCustomBootstrap.Initialize` implementation: +To do this, use the `ICustomBootstrap` interface to manage systems in multiple worlds. For more information, see the documentation on [ICustomBootstrap](systems-icustombootstrap.md). -1. Create the set of worlds you want your game or application to have. -1. For each created world: - 1. Generate a list of systems you want in that world. You can use [`DefaultWorldInitialization.GetAllSystems`](xref:Unity.Entities.DefaultWorldInitialization.GetAllSystems*) but it isn't required. - 1. Call `DefaultWorldInitialization.AddSystemsToRootLevelSystemGroups` to add the list of systems to the world. This also orders the systems correctly. - 1. If you don't want to manually update the world, call `ScriptBehaviourUpdateOrder.AppendWorldToCurrentPlayerLoop` to add the world to the player loop. -1. If you created the default world, set `World.DefaultGameObjectInjectionWorld` to the default world and return `true`. If you didn't create the default world and want the default bootstrap to do that for you, return `false`. diff --git a/Documentation~/transform-aspect.md b/Documentation~/transform-aspect.md deleted file mode 100644 index 70e587f4..00000000 --- a/Documentation~/transform-aspect.md +++ /dev/null @@ -1,39 +0,0 @@ -# Transform aspect - -The [aspect](aspects-intro.md) system has the built in [`TransformAspect`](xref:Unity.Transforms.TransformAspect), which contains references to all three transform components of a child entity: - -* [`LocalTransform`](xref:Unity.Transforms.LocalTransform) -* [`WorldTransform`](xref:Unity.Transforms.WorldTransform) -* [`ParentTransform`](xref:Unity.Transforms.ParentTransform) - -For any root entities, `TransformAspect`'s `LocalTransform` will be the same as `WorldTransform`. - -`TransformAspect` is a convenient way to work with transforms in your project. It contains logic that keeps `LocalTransform` and `WorldTransform` in sync with each other. It allows you to manipulate an entity in world space, even if it is part of a hierarchy. - -This example illustrates using `TransformAspect` to rotate the turret of a tank: - -```c# -using Unity.Burst; -using Unity.Entities; -using Unity.Mathematics; -using Unity.Transforms; - -[BurstCompile] -partial struct TurretRotationSystem : ISystem -{ - [BurstCompile] - public void OnUpdate(ref SystemState state) - { - // The amount of rotation around Y required to do 360 degrees in 2 seconds. - var rotation = quaternion.RotateY(state.Time.DeltaTime * math.PI); - - // The classic C# foreach is what we often refer to as "Idiomatic foreach" (IFE). - // Aspects provide a higher level interface than directly accessing component data. - // Using IFE with aspects is a powerful and expressive way of writing main thread code. - foreach (var transform in SystemAPI.Query()) - { - transform.RotateWorld(rotation); - } - } -} -``` diff --git a/Documentation~/transforms-concepts.md b/Documentation~/transforms-concepts.md index a77efd2c..3176bc28 100644 --- a/Documentation~/transforms-concepts.md +++ b/Documentation~/transforms-concepts.md @@ -2,69 +2,65 @@ You can use the [`Unity.Transforms`](xref:Unity.Transforms) namespace to control the position, rotation, and scale of any entity in your project. -You can also use the built-in [aspect](aspects-intro.md) `TransformAspect`, which will help you manage entities that are in a hierarchy. For more information, see the [TransformAspect](transform-aspect.md) documentation. +The following components are used in the transform system: +* [`LocalToWorld`](xref:Unity.Entities.TransformAuthoring.LocalToWorld) +* [`LocalTransform`](xref:Unity.Transforms.LocalTransform) +* [`PostTransformMatrix`](xref:Unity.Transforms.PostTransformMatrix) +* [`Parent`](xref:Unity.Transforms.Parent) +* [`Child`](xref:Unity.Transforms.Child) -## The LocalTransform component - -The `LocalTransform` component has three properties: +There are two systems that update the transform system: +* [`ParentSystem`](xref:Unity.Transforms.ParentSystem) +* [`LocalToWorldSystem`](xref:Unity.Transforms.LocalToWorldSystem) -```c# -public struct LocalTransform : IComponentData, ITransformData -{ - public float3 Position; - public float Scale; - public quaternion Rotation; -} -``` - -If the entity has a `Parent` component, its Position, Rotation, and Scale are relative to that parent. If the entity doesn't have a `Parent` component, the transform is relative to the origin of the world. - -## The WorldTransform component - -The `WorldTransform` component has the same three properties as `LocalTransform`. But unlike the latter, they are always relative to the origin of the world. - -It is important to note that `WorldTransform` has a derived value, so writing to it has no effect. It is computed by the transform systems from `LocalTransform`, combined with the `WorldTransform` of the parent entity, if there is one. If there is no parent, `WorldTransform` is simply a copy of `LocalTransform`. This gives you access to an entity's final world position, rotation, and scale, regardless of whether it has a parent or not. +## Transform hierarchy -## The ParentTransform component +`Unity.Transforms` is hierarchical, which means that you can transform Entities based on their relationship to each other. -The `ParentTransform` component has the same three properties as `LocalTransform`. +For example, a car body can be the parent of its wheels. The wheels are children of the car body. When the car body moves, the wheels move with it. You can also move and rotate the wheels relative to the car body. -If an entity has a `Parent` component, it will also have a `ParentTransform` component. This is simply a copy of the parent's `WorldTransform`. This is provided so that you don't need to look up the parent when you need the parent's `WorldTransform`. The [TransformAspect](transform-aspect.md) makes use of this to keep `LocalTransform` and `WorldTransform` in sync with each other. +An entity can have multiple children, but only one parent. Children can have their own child entities. These multiple levels of parent-child relationships form a transform hierarchy. The entity at the top of a hierarchy, without a parent, is the **root**. -It is important to note that `ParentTransform` has a derived value, so writing to it has no effect. +To declare a Transform hierarchy, you must do this from the bottom up. This means that you use [`Parent`](xref:Unity.Transforms.Parent) to declare an Entity's parent, rather than declare its children. If you want to declare a child of an Entity, find the Entities that you want to be children and set their parent to be the target Entity. For more information, see the [Using a hierarchy](transforms-using.md#using-a-hierarchy) documentation. ## The LocalToWorld component -The `LocalToWorld` (`float4x4`) matrix represents the transform from local space to world space. This matrix is what the rendering system will use to render the geometry. +The `LocalToWorld` matrix represents the transform from local space to world space. This matrix is what the rendering system uses to render the entity's geometry. By default, the `LocalToWorldSystem` updates this component from the `LocalToWorld` component. This default update means that you don't need to update `LocalToWorld`, and the system does that for you. To disable this automatic update use a `[WriteGroup(typeof(LocalToWorld))]` attribute on a component. With this write group, you have complete control over `LocalToWorld`. For more information, see the documentation on [write groups](systems-write-groups.md). -`LocalToWorld` is normally derived from `WorldTransform`, which is itself derived from `LocalTransform`. This behavior may be overridden by using a `[WriteGroup(typeof(LocalToWorld))]` on a component. With this write group, you have complete control over `LocalToWorld`. See documentation on [write groups](systems-write-groups.md). +>[!NOTE] +>The `LocalToWorld` component value might be out of date or invalid while the `SimulationSystemGroup` is running. This is because the transform system only updates the component value when the `TransformSystemGroup` runs. It might also contain additional offsets applied for graphical smoothing purposes. Therefore, while the `LocalToWorld` component might be useful as a fast approximation of an entity's world-space transformation when its latency is acceptable, you shouldn't rely on it if you need an accurate, up-to-date world transform for simulation purposes. In those cases, use the [`ComputeWorldTransformMatrix`](xref:Unity.Transforms.Helpers.ComputeWorldTransformMatrix*) method. -## The PostTransformScale component +## The LocalTransform component -Transform components only support uniform scale. To render nonuniformly scaled geometry, you may use a `PostTransformScale` (`float3x3`). This will be applied in the following manner: +The `LocalTransform` component has three properties. They control the position, rotation, and scale of the entity. When this component is present, it controls `LocalToWorld`: -``` -LocalToWorld = WorldTransform.ToMatrix() * PostTransformScale +```c# +public struct LocalTransform : IComponentData +{ + public float3 Position; + public float Scale; + public quaternion Rotation; +} ``` -## Latency +If the entity has a `Parent` component, Position, Rotation, and Scale are relative to that parent. If the entity doesn't have a `Parent` component, the transform is relative to the origin of the world. -`WorldTransform`, `ParentTransform`, and `LocalToWorld` are all derived from `LocalTransform`. Because this is done by the transform systems, their values are updated once those systems have run. +## The PostTransformMatrix component -## Computation +`LocalTransform` only supports uniform scale. To render geometry with nonuniform scale, you must use a `PostTransformMatrix` matrix. This is applied after `LocalTransform`. You can also use this component to introduce shear transforms, or to translate the entity relative to its pivot. -If `WorldTransform`, `ParentTransform`, and `LocalTransform` are all present on an entity, ECS computes `WorldTransform` as: +## The Parent component -```c# -WorldTransform = LocalTransform * ParentTransform -``` +The `Parent` component defines the hierarchy. You put this on every child that you want to be part of a hierarchy. -## Transform hierarchy +## The Child component -`Unity.Transforms` is hierarchical, which means that you can transform Entities based on their relationship to each other. +The `Child` component buffer holds all children of a parent. `ParentSystem` manages this buffer and its contents. You only need to manage the `Parent` component. The system will take care of maintaining the corresponding `Child` component. -For example, a car body can be the parent of its wheels. The wheels are children of the car body. When the car body moves, the wheels move with it. You can also move and rotate the wheels relative to the car body. +## The ParentSystem -An entity can have multiple children, but only one parent. Children can have their own child entities. These multiple levels of parent-child relationships form a transform hierarchy. The entity at the top of a hierarchy, without a parent, is the **root**. +The `ParentSystem` maintains the `Child` component buffer, based on the `Parent` component of the children. When you set a `Parent` component on a child, Unity only updates the parent's `Child` component when the `ParentSystem` has run. -To declare a Transform hierarchy, you must do this from the bottom up. This means that you use [`Parent`](xref:Unity.Transforms.Parent) to declare an Entity's parent, rather than declare its children. If you want to declare a child of an Entity, find the Entities that you want to be children and set their parent to be the target Entity. For more information, see the [Using a hierarchy](transforms-using.md#using-a-hierarchy) documentation. +## The LocalToWorldSystem + +`LocalToWorldSystem` computes and updates the `LocalToWorld` component, based on the `LocalTransform` component and the hierarchy. When you set a `LocalTransform` component on an entity, Unity only updates its `LocalToWorld` component when the `LocalToWorldSystem` has run. diff --git a/Documentation~/transforms-intro.md b/Documentation~/transforms-intro.md index 463d66ec..813ccb21 100644 --- a/Documentation~/transforms-intro.md +++ b/Documentation~/transforms-intro.md @@ -6,8 +6,6 @@ This section contains information about how transforms work in Entities, and how |---|---| |[Transform concepts](transforms-concepts.md)|How transforms work in Entities.| |[Using transforms](transforms-using.md)|How to use transforms in your project.| -|[Transform aspect](transform-aspect.md)|How to use `TransformAspect` to manage the transforms in your project. | ## Additional resources * [`Unity.Transforms` API documentation](xref:Unity.Transforms) -* [`TransformAspect` API documentation](xref:Unity.Transforms.TransformAspect) \ No newline at end of file diff --git a/Documentation~/transforms-using.md b/Documentation~/transforms-using.md index 391b952f..5bb29acb 100644 --- a/Documentation~/transforms-using.md +++ b/Documentation~/transforms-using.md @@ -2,10 +2,10 @@ To use transforms in your project, use the [`Unity.Transforms`](xref:Unity.Transforms) namespace to control the position, rotation, and scale of any entity in your project. -There are three transform components. They are structurally identical, but their purpose is different. The most commonly used component is `LocalTransform`. It represents the relative position, rotation, and scale of the component. If there is a parent, the transform is relative to that parent. If there is no parent, the transform is relative to the world origin. You can read and write to this component. +`LocalTransform` represents the relative position, rotation, and scale of the entity. If there is a parent, the transform is relative to that parent. If there is no parent, the transform is relative to the world origin. You can read and write to this component. ```c# -public struct LocalTransform : IComponentData, ITransformData +public struct LocalTransform : IComponentData { public float3 Position; public float Scale; @@ -13,52 +13,32 @@ public struct LocalTransform : IComponentData, ITransformData } ``` -The second component is `WorldTransform`. It has the same properties as `LocalTransform`, but this transform is always relative to the world origin. Because the value is derived from `LocalTransform`, writing to it has no effect. You should treat this as read-only. It is used internally in `TransformAspect`. - -```c# -public struct WorldTransform : IComponentData, ITransformData -{ - public float3 Position; - public float Scale; - public quaternion Rotation; -} -``` +## Using the API -The third component is `ParentTransform`. It also has the same properties as `LocalTransform`. This is a copy of the parent's `WorldTransform`, so that you don't have to look up the parent if you only need its transform. This is also a derived value, so writing to it has no effect. You should treat it as read-only. It is used internally in the `TransformAspect`. +There are no methods in the API to modify `LocalTransform`. All methods return a new value, and do not change the transform itself. So if you want to modify the transform, you must use the assignment operator. For example, to rotate a transform around the Z axis: ```c# -public struct ParentTransform : IComponentData, ITransformData -{ - public float3 Position; - public float Scale; - public quaternion Rotation; -} + myTransform = myTransform.RotateZ(someAngle); ``` -## Using the API - -All three transform components (`LocalTransform`, `WorldTransform`, and `ParentTransform`) have the same API. This API is implemented as extension methods. There are no methods in the API to modify the transform. All methods return a new value, and do not change the transform itself. So if you want to modify the transform, you must use the assignment operator. For example, to rotate a transform around the Z axis: +The only way to modify `LocalTransform` directly is by writing to the Position, Rotation, and Scale properties. For example: ```c# - myTransform = myTransform.RotateZ(someAngle); + myTransform.Position += math.up(); ``` -The only way to modify the transform directly is by writing to the Position, Rotation, and Scale properties. For example: +This code is equivalent to: ```c# - transform.Position += math.up(); + myTransform = myTransform.Translate(math.up()); ``` -For the complete non-static API, see [`Unity.Transforms.TransformDataHelpers`](xref:Unity.Transforms.TransformDataHelpers) - -There is also a static API, which mainly constructs transforms for you. So if you want to create a `LocalTransform` with a specified position, but using default rotation and scale, use this: +There are several methods to construct a transform for you. So if you want to create a `LocalTransform` with a specified position, but using default rotation and scale, use this: ```c# - var myNewTransform = LocalTransform.FromPosition(1, 2, 3); + var myTransform = LocalTransform.FromPosition(1, 2, 3); ``` -All three transform components have the same static API. See [`Unity.Transforms.LocalTransform`](xref:Unity.Transforms.LocalTransform) - ## Using a hierarchy You can use `LocalTransform` by itself. However, if you want to use a hierarchy of Entities, you must also use `Parent`. To set a parent of a child entity use [`Parent`](xref:Unity.Transforms.Parent): @@ -70,13 +50,4 @@ public struct Parent : IComponentData } ``` -To make sure that the parents find their children, and to set up their child component, run [ParentSystem](xref:Unity.Transforms.ParentSystem). - -To specify how to position, rotate, and scale the child relative to its parent use [`LocalTransform`](xref:Unity.Transforms.LocalTransform). For example, this is how you could rotate the wheels on a parent car object: - -```c# -void Execute(ref LocalTransform transform) -{ - transform = transform.RotateZ(angleChange); -} -``` +To make sure that the parents find their children, and to set up their child component, [ParentSystem](xref:Unity.Transforms.ParentSystem) must run. diff --git a/Documentation~/upgrade-guide.md b/Documentation~/upgrade-guide.md index 285acb2a..fe4711c6 100644 --- a/Documentation~/upgrade-guide.md +++ b/Documentation~/upgrade-guide.md @@ -2,6 +2,7 @@ To upgrade from Entities 0.51 to 1.0, you need to do the following: +* [Update ISystem](#update-isystem) * [Update Transforms in your project](#update-transforms-in-your-project) * [Update conversions in your project](#update-conversions-in-your-project) * [Remove GenerateAuthoringComponent](#remove-generateauthoringcomponent) @@ -20,10 +21,13 @@ To upgrade from Entities 0.51 to 1.0, you need to do the following: * [Update SystemBase helper methods to SystemAPI](#update-systembase-helper-methods-to-systemapi) * [Add the Entities Graphics package to your project](#add-the-entities-graphics-package-to-your-project) * [Modify blob assets that use new or default](#modify-blob-assets-that-use-new-or-default) +* [Update partials in your project](#update-partials-in-your-project) -## ISystem changes -ISystem now uses C#'s default implemented methods. So you don't have to implement every function. -```cs +## Update ISystem + +ISystem now uses C#'s default implemented methods, so you don't have to implement every function: + +```c# // Before partial struct MySystem : ISystem { public void OnCreate(ref SystemState state){} @@ -41,10 +45,9 @@ partial struct MySystem : ISystem { } ``` +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: -You now no longer need to put `[BurstCompile]` on the struct of an `ISystem`. -While it is still needed on your `OnCreate`, `OnStartRunning`, `OnUpdate`, `OnStopRunning` and `OnDestroy`. You now no longer need to put `BurstCompile` on the struct itself. -```cs +```c# // Before [BurstCompile] partial struct MySystem : ISystem { @@ -67,27 +70,20 @@ partial struct MySystem : ISystem { 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](transforms-intro.md) documentation. -The Transform system is under active development and subject to change up until the 1.0 release. The original, deprecated transform system is available via the `ENABLE_TRANSFORM_V1` define until then. +Note: The Transform system is under active development and subject to change up until the 1.0 release. ### LocalTransform -There are three new transform components. See the [Using Transforms](transforms-using.md) document. For upgrade purposes, however, the only new component of importance is `LocalTransform`: +The three components `Translation`, `Rotation`, and `Scale` have been combined into one component, named `LocalTransform`. ```c# -public struct LocalTransform : IComponentData, ITransformData +public struct LocalTransform : IComponentData { - public float3 Position; - public float Scale; + public float3 Position; + public float Scale; public quaternion Rotation; } ``` -### Equivalence -What was once a threesome of components has now been combined into one component, named `LocalTransform`. - -* Translation: `LocalTransform.Position` -* Rotation: `LocalTransform.Rotation` -* Scale: `LocalTransform.Scale` - The following is an example of how to convert your code to use the `LocalTransform` component: ```c# @@ -103,16 +99,19 @@ 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 -The `NonUniformScale` component has been removed, and there isn't an equivalent. Non-uniform scaling isn't supported in the transform hierarchy. To non-uniformly scale the geometry, use a [`PostTransformScale`](xref:Unity.Transforms.PostTransformScale) before it's passed on to rendering. +`LocalTransform` has a single uniform scale property. The `NonUniformScale` component has been removed. To non-uniformly scale the geometry, use a [`PostTransformMatrix`](xref:Unity.Transforms.PostTransformMatrix). For example: -### Relativity +```c# +EntityManager.AddComponent(myEntity, new PostTransformMatrix { Value = float4x4.Scale(1, 2, 3) }); +``` -The `LocalTransform` component is relative to its parent. If there is no [`Parent`](xref:Unity.Transforms.Parent) component, `LocalTransform` will be relative to the origin of the world. In contrast, `WorldTransform` is always relative to the world origin. It is computed and written by `LocalToWorldSystem`. Therefore, `WorldTransform` has a derived value, and writing to it has no effect. +### Relativity -Note that `WorldTransform` is updated by a system, so it won't reflect changes made in `LocalTransform` until that system has run. +The `LocalTransform` component is relative to its parent. If there is no [`Parent`](xref:Unity.Transforms.Parent) component, `LocalTransform` is relative to the origin of the world. ### Initialization @@ -125,25 +124,25 @@ var r = new Rotation { Value = quaternion.RotateZ(1) }; var s = new Scale { Value = 2 } // AFTER -var t = LocalTransform.FromPositionRotationScale(new float3(1, 2, 3), quaternion.RotateZ(1), 2); +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`](xref:Unity.Transforms.LocalTransform). All initializers begin with "From". +To see the full list of initializer variations available, see the API documentation for [`LocalTransform`](xref:Unity.Transforms.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 is an invalid transform. Where necessary, use [`LocalTransform.Identity`](xref:Unity.Transforms.LocalTransform.Identity) as a default value, like so: +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`](xref:Unity.Transforms.LocalTransform.Identity) as a default value, like so: ```c# var t = LocalTransform.Identity; ``` ### Changing an individual transform property -To create a new transform that differs in only one property, helper functions are available. For example, to modify only the position, leaving rotation and scale at their original value: +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: ```c# -t = t.WithPosition(1, 2, 3); +SomeFunction(myTransform.WithPosition(1, 2, 3)); ``` -To see the full list of modifier variations available, see the API documentation for [`TransformDataHelpers`](xref:Unity.Transforms.TransformDataHelpers). +To see the full list of modifier variations available, see the API documentation for [`LocalTransform`](xref:Unity.Transforms.LocalTransform). ## Update conversions in your project @@ -597,7 +596,7 @@ 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: -```csharp +```c# // Before void MyMethod(SystemBase mySystem) { var mySingleton = mySystem.GetSingleton(); @@ -613,7 +612,7 @@ void MyMethod(SystemBase mySystem) { For `GetComponent`, `SetComponent`, `HasComponent`, `GetBuffer` and `Exists`, you can either use the equating `EntityManager` methods, or cache your own lookups: -```csharp +```c# // Before void MyMethod(SystemBase mySystem, Entity e) { var myComponent = mySystem.GetComponent(e); @@ -632,7 +631,7 @@ void MyMethod(SystemBase mySystem, Entity e, ComponentLookup alread `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: -```csharp +```c# // Before var readonlyBuffer = this.GetBuffer(e, true); @@ -654,7 +653,7 @@ For further information upgrading to Entities Graphics, see the [Entities Graphi Blob assets created with `new` or `default` now produce an error. To fix this, do the following: -```cs +```c# /// Before var myBlob = new MyBlob(...); @@ -673,7 +672,7 @@ struct MyBlob { 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: -```cs +```c# /// Before unsafe struct MyBlob { @@ -701,3 +700,18 @@ unsafe struct MyBlob { 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: + +```c# +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 +``` diff --git a/Editor Default Resources/uss/Common/dots-editor-common.uss b/Editor Default Resources/uss/Common/dots-editor-common.uss index 0a8fc40e..31ab752a 100644 --- a/Editor Default Resources/uss/Common/dots-editor-common.uss +++ b/Editor Default Resources/uss/Common/dots-editor-common.uss @@ -15,8 +15,8 @@ .search-icon { - width: 16px; - height: 16px; + width: 18px; + height: 18px; background-image: var(--search-icon); } diff --git a/Editor Default Resources/uxml/Components/components-window.uxml b/Editor Default Resources/uxml/Components/components-window.uxml index 66872204..1ea9c6b2 100644 --- a/Editor Default Resources/uxml/Components/components-window.uxml +++ b/Editor Default Resources/uxml/Components/components-window.uxml @@ -1,15 +1,8 @@ - + - - + + - + diff --git a/Editor Default Resources/uxml/Journaling/entities-journaling-window.uxml b/Editor Default Resources/uxml/Journaling/entities-journaling-window.uxml index e90f9998..6045a05b 100644 --- a/Editor Default Resources/uxml/Journaling/entities-journaling-window.uxml +++ b/Editor Default Resources/uxml/Journaling/entities-journaling-window.uxml @@ -1,33 +1,36 @@ - + - - + + - + - - - + + + - + - + - + + + + - - + + - + - + - - + + - + diff --git a/Unity.Core/AssemblyInfo.cs b/Unity.Core/AssemblyInfo.cs index be4d4e13..9591c736 100644 --- a/Unity.Core/AssemblyInfo.cs +++ b/Unity.Core/AssemblyInfo.cs @@ -3,5 +3,3 @@ [assembly: InternalsVisibleTo("Unity.Entities.Runtime.Build")] [assembly: InternalsVisibleTo("Unity.Entities.Runtime")] -[assembly: InternalsVisibleTo("Unity.Tiny.Scenes")] -[assembly: InternalsVisibleTo("Unity.Tiny.Codec.Tests")] diff --git a/Unity.Core/Compression/Codec.cs b/Unity.Core/Compression/Codec.cs index 4eaa03a0..3c506720 100644 --- a/Unity.Core/Compression/Codec.cs +++ b/Unity.Core/Compression/Codec.cs @@ -42,6 +42,20 @@ static public unsafe int CompressUpperBound(Codec codec, int size) /// /// static public unsafe int Compress(Codec codec, in byte* src, int srcSize, out byte* dst, Allocator allocator = Allocator.Temp) + { + return Compress(codec, src, srcSize, out dst, (AllocatorManager.AllocatorHandle) allocator); + } + + /// + /// Compresses the passed in `src` data into newly allocated `dst` buffer. Users must free `dst` manually after calling `Compress` + /// + /// + /// + /// + /// + /// + /// + static public unsafe int Compress(Codec codec, in byte* src, int srcSize, out byte* dst, AllocatorManager.AllocatorHandle allocator) { int boundedSize = CompressUpperBound(codec, srcSize); dst = (byte*)Memory.Unmanaged.Allocate(boundedSize, 16, allocator); diff --git a/Unity.Deformations/Unity.Deformations.asmdef b/Unity.Deformations/Unity.Deformations.asmdef index 380bd3a9..dd1a5fbf 100644 --- a/Unity.Deformations/Unity.Deformations.asmdef +++ b/Unity.Deformations/Unity.Deformations.asmdef @@ -1,8 +1,10 @@ { "name": "Unity.Deformations", + "rootNamespace": "", "references": [ "Unity.Entities", - "Unity.Mathematics" + "Unity.Mathematics", + "Unity.Collections" ], "includePlatforms": [], "excludePlatforms": [], @@ -13,4 +15,4 @@ "defineConstraints": [], "versionDefines": [], "noEngineReferences": false -} +} \ No newline at end of file diff --git a/Unity.Entities.Build/DotsGlobalSettings.cs b/Unity.Entities.Build/DotsGlobalSettings.cs index 0a3f308b..22927b09 100644 --- a/Unity.Entities.Build/DotsGlobalSettings.cs +++ b/Unity.Entities.Build/DotsGlobalSettings.cs @@ -196,7 +196,14 @@ public enum PlayerType /// Returns the for the build. public PlayerType GetPlayerType() { - return EditorUserBuildSettings.standaloneBuildSubtarget == StandaloneBuildSubtarget.Server ? PlayerType.Server : PlayerType.Client; + //When switching from dedicated server to other standalone platform, the standaloneBuildSubtarget + //is not reset. At the moment this is the safest and suggest check to verify we are building for the + //dedicated server platform. + #if UNITY_SERVER + return PlayerType.Server; + #else + return PlayerType.Client; + #endif } /// diff --git a/Unity.Entities.Build/Unity.Entities.Build.asmdef b/Unity.Entities.Build/Unity.Entities.Build.asmdef index 50bfba24..5cacc4ac 100644 --- a/Unity.Entities.Build/Unity.Entities.Build.asmdef +++ b/Unity.Entities.Build/Unity.Entities.Build.asmdef @@ -3,7 +3,8 @@ "rootNamespace": "Unity.Entities.Build", "references": [ "Unity.Entities", - "Unity.Mathematics" + "Unity.Mathematics", + "Unity.Collections" ], "includePlatforms": [ "Editor" @@ -16,4 +17,4 @@ "defineConstraints": [], "versionDefines": [], "noEngineReferences": false -} +} \ No newline at end of file diff --git a/Unity.Entities.CodeGen.Tests/Unity.Entities.CodeGen.Tests.TestTypes/TestTypes.cs b/Unity.Entities.CodeGen.Tests/Unity.Entities.CodeGen.Tests.TestTypes/TestTypes.cs deleted file mode 100644 index 7560f533..00000000 --- a/Unity.Entities.CodeGen.Tests/Unity.Entities.CodeGen.Tests.TestTypes/TestTypes.cs +++ /dev/null @@ -1,9 +0,0 @@ -using Unity.Collections.LowLevel.Unsafe; -using Unity.Entities; - -namespace Unity.Entities.CodeGen.Tests.TestTypes -{ - public struct BoidInAnotherAssembly : IComponentData - { - } -} diff --git a/Unity.Entities.CodeGen/StaticTypeRegistry/StaticTypeRegistryPostProcessor.cs b/Unity.Entities.CodeGen/StaticTypeRegistry/StaticTypeRegistryPostProcessor.cs index 81adb7f3..d6283bfb 100644 --- a/Unity.Entities.CodeGen/StaticTypeRegistry/StaticTypeRegistryPostProcessor.cs +++ b/Unity.Entities.CodeGen/StaticTypeRegistry/StaticTypeRegistryPostProcessor.cs @@ -64,6 +64,8 @@ internal partial class StaticTypeRegistryPostProcessor : EntitiesILPostProcessor TypeReference m_SystemTypeRef; TypeReference m_SystemIntRef; TypeReference m_SystemLongRef; + TypeReference m_SystemIntPtrRef; + TypeReference m_SystemUIntPtrRef; TypeReference m_TypeInfoRef; MethodReference m_TypeInfoConstructorRef; TypeReference m_FieldInfoRef; @@ -77,8 +79,11 @@ internal partial class StaticTypeRegistryPostProcessor : EntitiesILPostProcessor MethodReference m_SystemGuidHashFn; MethodReference m_BurstRuntimeGetHashCode64Ref; + Dictionary m_ChunkSerializableCache; + TypeDefinition GeneratedRegistryDef; MethodDefinition GeneratedRegistryCCTORDef; + bool IsReleaseConfig; bool IsMono; bool IsNetDots; @@ -99,7 +104,6 @@ protected override bool PostProcessImpl(TypeDefinition[] componentSystemTypes) IsToolConfig = EntitiesILPostProcessors.Defines.Contains("UNITY_ENTITIES_RUNTIME_TOOLING"); ArchBits = EntitiesILPostProcessors.Defines.Contains("UNITY_DOTSRUNTIME64") ? 64 : 32; - (var typeGenInfoList, var systemList) = GatherTypeInformation(); if (typeGenInfoList.Count > 0 || systemList.Count > 0) { @@ -129,11 +133,15 @@ protected override bool PostProcessImpl(TypeDefinition[] componentSystemTypes) return madeChange; } - void InitializeReferences() + void InitializeForTypeGeneration() { + // Initialize References m_SystemTypeRef = AssemblyDefinition.MainModule.ImportReference(typeof(Type)); m_SystemIntRef = AssemblyDefinition.MainModule.ImportReference(typeof(int)); m_SystemLongRef = AssemblyDefinition.MainModule.ImportReference(typeof(long)); + m_SystemIntPtrRef = AssemblyDefinition.MainModule.ImportReference(typeof(IntPtr)); + m_SystemUIntPtrRef = AssemblyDefinition.MainModule.ImportReference(typeof(UIntPtr)); + m_GetTypeFromHandleFnRef = AssemblyDefinition.MainModule.ImportReference(typeof(Type).GetMethod("GetTypeFromHandle")); // I think we can remove this and make the GetHashCode generation less dumb @@ -168,6 +176,9 @@ void InitializeReferences() { typeof(int), typeof(int), typeof(int) })); + + // Initialize Internal State + m_ChunkSerializableCache = new Dictionary(); } TypeCategory FindTypeCategoryForType(TypeDefinition typeDef) @@ -296,7 +307,7 @@ TypeCategory FindTypeCategoryForTypeRecursive(TypeDefinition typeDef) // Move the CreateTypeGenInfo here so we can keep assemblies with no components quick to process if (components.Count > 0 || systemList.Count > 0) { - InitializeReferences(); + InitializeForTypeGeneration(); GenerateFieldInfoForRegisteredComponents(); foreach (var typePair in components) { diff --git a/Unity.Entities.CodeGen/StaticTypeRegistry/TypeInfoGeneration.cs b/Unity.Entities.CodeGen/StaticTypeRegistry/TypeInfoGeneration.cs index 688b8ccf..fffc7572 100644 --- a/Unity.Entities.CodeGen/StaticTypeRegistry/TypeInfoGeneration.cs +++ b/Unity.Entities.CodeGen/StaticTypeRegistry/TypeInfoGeneration.cs @@ -12,6 +12,10 @@ using SystemList = System.Collections.Generic.List; using Unity.Cecil.Awesome; using static Unity.Entities.CodeGen.TypeReferenceExtensions; +using Unity.Collections.LowLevel.Unsafe; +using TypeAttributes = Mono.Cecil.TypeAttributes; +using FieldAttributes = Mono.Cecil.FieldAttributes; +using TypeInfo = Unity.Entities.TypeManager.TypeInfo; namespace Unity.Entities.CodeGen { @@ -963,14 +967,15 @@ bool HasNativeContainer(TypeReference type) return false; } - if (type.Resolve().HasAttribute("Unity.Collections.LowLevel.Unsafe.NativeContainerAttribute")) + var typeDef = type.Resolve(); + if (typeDef.HasAttribute(typeof(NativeContainerAttribute).FullName)) return true; // The incoming `type` should be a full generic instance. This genericResolver // will help us resolve the generic parameters of any of its fields var genericResolver = TypeResolver.For(type); - foreach (var typeField in type.Resolve().Fields) + foreach (var typeField in typeDef.Fields) { // 1) enums which infinitely recurse because the values in the enum are of the same enum type // 2) statics which infinitely recurse themselves (Such as vector3.zero.zero.zero.zero) @@ -993,6 +998,73 @@ bool HasNativeContainer(TypeReference type) return false; } + // True when a component is valid to using in world serialization. A component IsSerializable when it is valid to blit + // the data across storage media. Thus components containing pointers have an IsSerializable of false as the component + // is blittable but no longer valid upon deserialization. + private (bool isSerializable, bool hasChunkSerializableAttribute) IsTypeValidForSerialization(TypeReference type) + { + var result = (false, false); + if (m_ChunkSerializableCache.TryGetValue(type, out result)) + return result; + + var typeDef = type.Resolve(); + if (typeDef.HasAttribute(typeof(ChunkSerializableAttribute).FullName)) + { + result = (true, true); + m_ChunkSerializableCache.Add(type, result); + return result; + } + + var genericResolver = TypeResolver.For(type); + foreach (var typeField in typeDef.Fields) + { + if (typeField.IsStatic) + continue; + + var fieldType = genericResolver.ResolveFieldType(typeField); + if (fieldType.IsGenericParameter) + continue; + + if (fieldType.IsPointer || (fieldType == m_SystemIntPtrRef || fieldType == m_SystemUIntPtrRef)) + { + m_ChunkSerializableCache.Add(type, result); + return result; + } + else if (fieldType.IsValueType() && !fieldType.IsPrimitive && !fieldType.IsEnum()) + { + if (!IsTypeValidForSerialization(fieldType).isSerializable) + { + m_ChunkSerializableCache.Add(type, result); + return result; + } + } + } + + result = (true, false); + m_ChunkSerializableCache.Add(type, result); + return result; + } + + // A component type is "chunk serializable" if it meets the following rules: + // - It is decorated with [ChunkSerializable] attribute (this is an override for all other rules) + // - The type is blittable AND + // - The type does not contain any pointer types (including IntPtr) AND + // - If the type is a shared component, it does not contain an entity reference + private bool IsComponentChunkSerializable(TypeReference type, TypeCategory category, bool hasEntityReference) + { + var isSerializable = IsTypeValidForSerialization(type); + + // Shared Components are expected to be handled specially when serializing and are not required to be blittable. + // They cannot contain an entity reference today as they are not patched however for unmanaged components + // we should be able to correct this behaviour DOTS-7613 + if (!isSerializable.hasChunkSerializableAttribute && category == TypeCategory.ISharedComponentData && hasEntityReference) + { + isSerializable.isSerializable = false; + } + + return isSerializable.isSerializable; + } + internal unsafe TypeGenInfo CreateTypeGenInfo(TypeReference typeRef, TypeCategory typeCategory) { // We will be referencing this type in generated functions to make it's type internal if it's private @@ -1013,7 +1085,6 @@ internal unsafe TypeGenInfo CreateTypeGenInfo(TypeReference typeRef, TypeCategor ref mightHaveEntityRefs, ref mightHaveBlobRefs)); - if (!isManaged) { entityOffsets = TypeUtils.GetEntityFieldOffsets(typeRef, ArchBits); @@ -1072,6 +1143,8 @@ internal unsafe TypeGenInfo CreateTypeGenInfo(TypeReference typeRef, TypeCategor mightHaveBlobRefs = false; } + bool isChunkSerializable = IsComponentChunkSerializable(typeRef, typeCategory, mightHaveEntityRefs); + if (alignAndSize.empty || typeCategory == TypeCategory.ISharedComponentData) typeIndex |= ZeroSizeInChunkTypeFlag; @@ -1105,7 +1178,10 @@ internal unsafe TypeGenInfo CreateTypeGenInfo(TypeReference typeRef, TypeCategor if (isIEquatable) typeIndex |= IEquatableTypeFlag; - if(typeCategory == TypeCategory.ISharedComponentData && isManaged && !isIEquatable) + if (!isChunkSerializable) + typeIndex |= IsNotChunkSerializableTypeFlag; + + if (typeCategory == TypeCategory.ISharedComponentData && isManaged && !isIEquatable) throw new ArgumentException($"Type '{typeRef.FullName}' is a ISharedComponentData and has managed references, you must implement IEquatable"); CalculateMemoryOrderingAndStableHash(typeRef, out ulong memoryOrdering, out ulong stableHash); diff --git a/Unity.Entities.Editor.PerformanceTests/Hierarchy/WorldGenerator/WorldGenerator.cs b/Unity.Entities.Editor.PerformanceTests/Hierarchy/WorldGenerator/WorldGenerator.cs index 1db5da78..be24e9f4 100644 --- a/Unity.Entities.Editor.PerformanceTests/Hierarchy/WorldGenerator/WorldGenerator.cs +++ b/Unity.Entities.Editor.PerformanceTests/Hierarchy/WorldGenerator/WorldGenerator.cs @@ -14,11 +14,7 @@ namespace Unity.Entities.Editor.PerformanceTests class WorldGenerator : IDisposable { // Minimum set to be picked up by ParentSystem + GUID for tracking entities after changes -#if !ENABLE_TRANSFORM_V1 - static readonly ComponentType[] k_BasicArchetype = { typeof(EntityGuid), typeof(LocalToWorld), typeof(WorldTransform), typeof(LocalTransform) }; -#else - static readonly ComponentType[] k_BasicArchetype = { typeof(EntityGuid), typeof(LocalToWorld), typeof(LocalToParent) }; -#endif + static readonly ComponentType[] k_BasicArchetype = { typeof(EntityGuid), typeof(LocalToWorld), typeof(LocalTransform) }; static readonly ComponentType[][] k_ArchetypeVariants = { new ComponentType[] {typeof(SegmentId)}, diff --git a/Unity.Entities.Editor.Tests/Inspector/ComponentsTabTests.cs b/Unity.Entities.Editor.Tests/Inspector/ComponentsTabTests.cs index 791f4c43..541561b7 100644 --- a/Unity.Entities.Editor.Tests/Inspector/ComponentsTabTests.cs +++ b/Unity.Entities.Editor.Tests/Inspector/ComponentsTabTests.cs @@ -42,7 +42,6 @@ public void Setup() m_Root.ForceUpdateBindings(); } -#if !ENABLE_TRANSFORM_V1 [Test] public void ComponentsTab_Comparer() { @@ -68,39 +67,6 @@ public void ComponentsTab_Comparer() nameof(YComponent) })); } -#else - [Test] - public void ComponentsTab_Comparer() - { - var componentTypes = new[] - { - nameof(XComponent), - nameof(BComponent), - nameof(Rotation), - nameof(YComponent), - nameof(Translation), - nameof(NonUniformScale), - nameof(LocalToParent), - nameof(DComponent), - nameof(LocalToWorld), - }; - - Array.Sort(componentTypes, EntityInspectorComponentsComparer.Instance); - - Assert.That(componentTypes, Is.EquivalentTo(new[] - { - nameof(Translation), - nameof(Rotation), - nameof(NonUniformScale), - nameof(LocalToWorld), - nameof(LocalToParent), - nameof(BComponent), - nameof(DComponent), - nameof(XComponent), - nameof(YComponent) - })); - } -#endif [Test] public void ComponentsTab_UpdateComponentOrder_AddNewComponentsBeginning() @@ -250,7 +216,6 @@ public void ComponentsTab_UpdateComponentOrder_AddAndRemoveMultipleComponentsBeg $"{nameof(ComponentsTabTests)}_{nameof(DComponent)}", })); -#if !ENABLE_TRANSFORM_V1 m_World.EntityManager.RemoveComponent(m_InspectedEntity); m_World.EntityManager.RemoveComponent(m_InspectedEntity); m_World.EntityManager.AddComponent(m_InspectedEntity); @@ -261,20 +226,6 @@ public void ComponentsTab_UpdateComponentOrder_AddAndRemoveMultipleComponentsBeg $"{nameof(ComponentsTabTests)}_{nameof(CComponent)}", $"{nameof(ComponentsTabTests)}_{nameof(DComponent)}", })); -#else - m_World.EntityManager.RemoveComponent(m_InspectedEntity); - m_World.EntityManager.RemoveComponent(m_InspectedEntity); - m_World.EntityManager.AddComponent(m_InspectedEntity); - m_World.EntityManager.AddComponent(m_InspectedEntity); - m_Root.ForceUpdateBindings(); - Assert.That(GetComponentsOrderFromUI(), Is.EquivalentTo(new[] - { - nameof(Translation), - nameof(Rotation), - $"{nameof(ComponentsTabTests)}_{nameof(CComponent)}", - $"{nameof(ComponentsTabTests)}_{nameof(DComponent)}", - })); -#endif } [Test] @@ -304,7 +255,6 @@ public void ComponentsTab_UpdateComponentOrder_AddAndRemoveMultipleComponentsEnd })); } -#if !ENABLE_TRANSFORM_V1 [Test] public void ComponentsTab_UpdateComponentOrder_AddAndRemoveMultipleComponentsMiddle() { @@ -333,38 +283,6 @@ public void ComponentsTab_UpdateComponentOrder_AddAndRemoveMultipleComponentsMid $"{nameof(ComponentsTabTests)}_{nameof(YComponent)}", })); } -#else - [Test] - public void ComponentsTab_UpdateComponentOrder_AddAndRemoveMultipleComponentsMiddle() - { - m_World.EntityManager.AddComponent(m_InspectedEntity, new ComponentTypeSet(typeof(Translation), typeof(Rotation), typeof(CComponent), typeof(DComponent), typeof(YComponent))); - m_Root.ForceUpdateBindings(); - Assert.That(GetComponentsOrderFromUI(), Is.EquivalentTo(new[] - { - nameof(Translation), - nameof(Rotation), - $"{nameof(ComponentsTabTests)}_{nameof(CComponent)}", - $"{nameof(ComponentsTabTests)}_{nameof(DComponent)}", - $"{nameof(ComponentsTabTests)}_{nameof(YComponent)}", - })); - - m_World.EntityManager.RemoveComponent(m_InspectedEntity); - m_World.EntityManager.RemoveComponent(m_InspectedEntity); - m_World.EntityManager.AddComponent(m_InspectedEntity); - m_World.EntityManager.AddComponent(m_InspectedEntity); - m_World.EntityManager.AddComponent(m_InspectedEntity); - m_Root.ForceUpdateBindings(); - Assert.That(GetComponentsOrderFromUI(), Is.EquivalentTo(new[] - { - nameof(Translation), - $"{nameof(ComponentsTabTests)}_{nameof(AComponent)}", - $"{nameof(ComponentsTabTests)}_{nameof(DComponent)}", - $"{nameof(ComponentsTabTests)}_{nameof(EComponent)}", - $"{nameof(ComponentsTabTests)}_{nameof(XComponent)}", - $"{nameof(ComponentsTabTests)}_{nameof(YComponent)}", - })); - } -#endif [Test] public void ComponentsTab_UpdateComponentOrder_RemoveAll() @@ -387,7 +305,6 @@ public void ComponentsTab_UpdateComponentOrder_RemoveAll() Assert.That(GetComponentsOrderFromUI(), Is.Empty); } -#if !ENABLE_TRANSFORM_V1 [Test] public void ComponentsTab_UpdateComponentOrder_AddAndRemoveRandomOrder1() { @@ -415,39 +332,6 @@ public void ComponentsTab_UpdateComponentOrder_AddAndRemoveRandomOrder1() $"{nameof(ComponentsTabTests)}_{nameof(EComponent)}" })); } -#else - [Test] - public void ComponentsTab_UpdateComponentOrder_AddAndRemoveRandomOrder1() - { - m_World.EntityManager.AddComponent(m_InspectedEntity, new ComponentTypeSet(new ComponentType[] { typeof(BComponent), typeof(EComponent), typeof(CComponent), typeof(Translation), typeof(Rotation), typeof(NonUniformScale) })); - m_Root.ForceUpdateBindings(); - - Assert.That(GetComponentsOrderFromUI(), Is.EquivalentTo(new[] - { - nameof(Translation), - nameof(Rotation), - nameof(NonUniformScale), - $"{nameof(ComponentsTabTests)}_{nameof(BComponent)}", - $"{nameof(ComponentsTabTests)}_{nameof(CComponent)}", - $"{nameof(ComponentsTabTests)}_{nameof(EComponent)}" - })); - - m_World.EntityManager.RemoveComponent(m_InspectedEntity); - m_World.EntityManager.RemoveComponent(m_InspectedEntity); - m_World.EntityManager.RemoveComponent(m_InspectedEntity); - m_World.EntityManager.AddComponent(m_InspectedEntity, new ComponentTypeSet(typeof(AComponent), typeof(DComponent))); - - m_Root.ForceUpdateBindings(); - Assert.That(GetComponentsOrderFromUI(), Is.EquivalentTo(new[] - { - nameof(Translation), - $"{nameof(ComponentsTabTests)}_{nameof(AComponent)}", - $"{nameof(ComponentsTabTests)}_{nameof(BComponent)}", - $"{nameof(ComponentsTabTests)}_{nameof(DComponent)}", - $"{nameof(ComponentsTabTests)}_{nameof(EComponent)}" - })); - } -#endif [Test] public void ComponentsTab_UpdateComponentOrder_AddAndRemoveRandomOrder2() diff --git a/Unity.Entities.Editor.Tests/LiveProperties/LivePropertiesTests.cs b/Unity.Entities.Editor.Tests/LiveProperties/LivePropertiesTests.cs index 1c777e7e..f9d3fbbb 100644 --- a/Unity.Entities.Editor.Tests/LiveProperties/LivePropertiesTests.cs +++ b/Unity.Entities.Editor.Tests/LiveProperties/LivePropertiesTests.cs @@ -153,6 +153,8 @@ public IEnumerator ShouldUpdateProperties_MissingConversion_Test() var go = new GameObject("go"); var comp = go.AddComponent(); + var transformUsageFlagComponent = go.AddComponent(); + transformUsageFlagComponent.flags = TransformUsageFlags.Dynamic; SceneManager.MoveGameObjectToScene(go, subscene.EditingScene); diff --git a/Unity.Entities.Editor.Tests/Search/QuickSearchTests.cs b/Unity.Entities.Editor.Tests/Search/QuickSearchTests.cs new file mode 100644 index 00000000..78de8424 --- /dev/null +++ b/Unity.Entities.Editor.Tests/Search/QuickSearchTests.cs @@ -0,0 +1,153 @@ +//#define PROFILING_TESTS +using System; +using System.Collections; +using System.Collections.Generic; +using NUnit.Framework; +using UnityEngine; +using UnityEditor; +using UnityEditor.Search; +using UnityEngine.UIElements; +using UnityEngine.UIElements.UIR; +using PointerType = UnityEngine.PointerType; +using UnityEngine.TestTools; + +namespace Unity.Entities.Editor.Tests +{ + public class QuickSearchTestCase + { + public string query; + public int? expectedCount; + public object[] expectedValues; + public string[] expectedLabels; + public bool ordered; + public string errorMessage; + public bool keepDuplicateValue; + public bool printResults = true; + + public override string ToString() + { + return $"`{query}`"; + } + } + + public class QuickSearchTests + { + static Dictionary s_EventHandlerOffs = new(); + + public static IEnumerator WaitForResultsPending(ISearchList result) + { + while (result.pending) + yield return null; + } + + public static IEnumerator WaitForSceneHierarchyChanged(Action hierarchyModifier) + { + bool hierarchyHasChanged = false; + Action onHierarchyChanged = () => hierarchyHasChanged = true; + EditorApplication.hierarchyChanged += onHierarchyChanged; + + hierarchyModifier(); + + int maxIter = 100; + while (!hierarchyHasChanged && maxIter-- > 0) + { + yield return null; + } + EditorApplication.hierarchyChanged -= onHierarchyChanged; + } + + public static IEnumerator CompoundEnumerator(params IEnumerator[] enumerators) + { + foreach (var enumerator in enumerators) + { + yield return enumerator; + } + } + + public static IEnumerator WaitForSeconds(float seconds) + { + var waitStartTime = EditorApplication.timeSinceStartup; + while (true) + { + if ((EditorApplication.timeSinceStartup - waitStartTime) > seconds) + yield break; + + yield return null; + } + } + + public static IEnumerator WaitForSeconds(Func predicate, float timeoutSeconds) + { + return WaitForSeconds(predicate, timeoutSeconds, $"Timeout: Waited more than {timeoutSeconds} seconds"); + } + + public static IEnumerator WaitForSeconds(Func predicate, float timeoutSeconds, string message) + { + var waitStartTime = EditorApplication.timeSinceStartup; + while (true) + { + if (predicate()) + yield break; + + if ((EditorApplication.timeSinceStartup - waitStartTime) > timeoutSeconds) + Assert.Fail(message); + + yield return null; + } + } + + public static IEnumerator WaitFor(Func predicate, int times = 500) + { + for (int i = 0; i < times; ++i) + { + if (predicate()) + break; + yield return null; + } + } + + public static IEnumerator FetchItems(string providerId, string query, List items) + { + using (var searchContext = SearchService.CreateContext(providerId, query)) + using (var fetchedItems = SearchService.Request(searchContext, SearchFlags.Sorted)) + { + while (fetchedItems.pending) + yield return null; + + Assert.IsNotEmpty(fetchedItems); + items.AddRange(fetchedItems); + } + } + + [OneTimeTearDown] + public void CloseAllSearchViews() + { + +#if PROFILING_TESTS + UnityEditorInternal.ProfilerDriver.enabled = false; +#endif + } + + [TearDown] + public void CleanTestData() + { + foreach (var off in s_EventHandlerOffs.Values) + { + off(); + } + s_EventHandlerOffs.Clear(); + + } + + [InitializeOnLoadMethod] + internal static void SetupTestPreferences() + { + if (UnityEditorInternal.InternalEditorUtility.isHumanControllingUs) + return; + EditorPrefs.SetInt("ApplicationIdleTime", 0); + EditorPrefs.SetInt("InteractionMode", 1); + } + + } + +} diff --git a/Unity.Entities.Editor.Tests/Search/QuickSearchTests.cs.meta b/Unity.Entities.Editor.Tests/Search/QuickSearchTests.cs.meta new file mode 100644 index 00000000..c8485836 --- /dev/null +++ b/Unity.Entities.Editor.Tests/Search/QuickSearchTests.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: c086e6200fa05cb4d8a26565f590ddc1 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Unity.Entities.Editor.Tests/Search/SearchQueryTests.cs b/Unity.Entities.Editor.Tests/Search/SearchQueryTests.cs new file mode 100644 index 00000000..d512e1aa --- /dev/null +++ b/Unity.Entities.Editor.Tests/Search/SearchQueryTests.cs @@ -0,0 +1,332 @@ +using NUnit.Framework; +using System.Collections; +using System.Collections.Generic; +using UnityEngine; +using UnityEditor; +using UnityEditor.Search; +using UnityEngine.TestTools; +using Unity.Entities; + +namespace Unity.Entities.Editor.Tests +{ + [System.Serializable] + public struct SearchBoid : ISharedComponentData + { + public float CellRadius; + public float SeparationWeight; + public float AlignmentWeight; + public float TargetWeight; + public float ObstacleAversionDistance; + public float MoveSpeed; + } + + [System.Serializable] + public struct SearchBoidTarget : ISharedComponentData + { + public float CellRadius; + public float SeparationWeight; + public float AlignmentWeight; + public float TargetWeight; + public float ObstacleAversionDistance; + public float MoveSpeed; + } + + [System.Serializable] + public struct SearchSimulate : ISharedComponentData + { + public float CellRadius; + public float SeparationWeight; + public float AlignmentWeight; + public float TargetWeight; + public float ObstacleAversionDistance; + public float MoveSpeed; + } +} + +namespace Unity.Entities.Editor.Tests +{ + public class QueryTestCase + { + public string query; + public string processedQuery; + public string filter; + public string op; + public object value; + + internal HierarchySearchProvider.HierarchyQueryDescriptor queryDescriptor; + + public QueryTestCase(string query) + { + this.query = query; + } + + public override string ToString() + { + return query; + } + } + + public class SearchQueryTests + { + static ComponentType[] GetComponentTypes(IEnumerable strs) + { + var dummy = ""; + return HierarchySearchProvider.GetComponentTypes(strs, ref dummy); + } + + public static IEnumerable GetConvertToHierarchySearchTests() + { + + + var s = "c=SearchBoid"; + yield return new QueryTestCase(s) + { + queryDescriptor = new HierarchySearchProvider.HierarchyQueryDescriptor(s) + { + entityQuery = new EntityQueryDesc() + { + All = GetComponentTypes(new[] { "Searchboid" }) + } + } + }; + + s = "c=SearchBoid c=SearchBoidTarget -c=SearchSimulate"; + yield return new QueryTestCase(s) + { + queryDescriptor = new HierarchySearchProvider.HierarchyQueryDescriptor(s) + { + entityQuery = new EntityQueryDesc() + { + All = GetComponentTypes(new[] { "Searchboid", "SearchboidTarget" }), + None = GetComponentTypes(new[] { "SearchSimulate" }), + } + } + }; + + s = "all=SearchBoid any=SearchBoidTarget none=SearchSimulate"; + yield return new QueryTestCase(s) + { + queryDescriptor = new HierarchySearchProvider.HierarchyQueryDescriptor(s) + { + entityQuery = new EntityQueryDesc() + { + All = GetComponentTypes(new[] { "Searchboid" }), + Any = GetComponentTypes(new[] { "SearchBoidTarget" }), + None = GetComponentTypes(new[] { "SearchSimulate" }) + } + } + }; + + s = "this all=SearchBoid is any=SearchBoidTarget text none=SearchSimulate values"; + yield return new QueryTestCase(s) + { + queryDescriptor = new HierarchySearchProvider.HierarchyQueryDescriptor(s) + { + entityQuery = new EntityQueryDesc() + { + All = GetComponentTypes(new[] { "Searchboid" }), + Any = GetComponentTypes(new[] { "SearchBoidTarget" }), + None = GetComponentTypes(new[] { "SearchSimulate" }) + }, + searchValueTokens = new[] { "this", "is", "text", "values" }, + searchValueTokenStr = "this is text values" + } + }; + + s = "this is text k=GameObject"; + yield return new QueryTestCase(s) + { + queryDescriptor = new HierarchySearchProvider.HierarchyQueryDescriptor(s) + { + entityQuery = null, + searchValueTokens = new[] { "this", "is", "text" }, + searchValueTokenStr = "this is text", + kind = NodeKind.GameObject + } + }; + + s = "ei=12 all=SearchBoid"; + yield return new QueryTestCase(s) + { + queryDescriptor = new HierarchySearchProvider.HierarchyQueryDescriptor(s) + { + entityQuery = new EntityQueryDesc() + { + All = GetComponentTypes(new[] { "Searchboid" }) + }, + entityIndex = 12 + } + + }; + + s = "k=GameObject disabled:true all=SearchBoid depth=12"; + yield return new QueryTestCase(s) + { + queryDescriptor = new HierarchySearchProvider.HierarchyQueryDescriptor(s) + { + entityQuery = new EntityQueryDesc() + { + All = GetComponentTypes(new[] { "Searchboid" }) + }, + kind = NodeKind.GameObject, + unusedFilters = "disabled:true depth=12" + } + }; + + s = "w=editor sea disabled:true none=SearchBoid"; + yield return new QueryTestCase(s) + { + queryDescriptor = new HierarchySearchProvider.HierarchyQueryDescriptor(s) + { + entityQuery = new EntityQueryDesc() + { + None = GetComponentTypes(new[] { "Searchboid" }) + }, + world = "editor", + searchValueTokens = new[] { "sea" }, + searchValueTokenStr = "sea", + unusedFilters = "disabled:true" + } + }; + + s = "w=editor sea disabled:true +includeprefab none=SearchBoid"; + yield return new QueryTestCase(s) + { + queryDescriptor = new HierarchySearchProvider.HierarchyQueryDescriptor(s) + { + entityQuery = new EntityQueryDesc() + { + None = GetComponentTypes(new[] { "Searchboid" }), + Options = EntityQueryOptions.IncludePrefab + }, + world = "editor", + searchValueTokens = new[] { "sea" }, + searchValueTokenStr = "sea", + unusedFilters = "disabled:true" + } + }; + + s = "w=editor sea disabled:true +includeprefab +IncludeDisabled none=SearchBoid"; + yield return new QueryTestCase(s) + { + queryDescriptor = new HierarchySearchProvider.HierarchyQueryDescriptor(s) + { + entityQuery = new EntityQueryDesc() + { + None = GetComponentTypes(new[] { "Searchboid" }), + Options = EntityQueryOptions.IncludePrefab | EntityQueryOptions.IncludeDisabledEntities + }, + world = "editor", + searchValueTokens = new[] { "sea" }, + searchValueTokenStr = "sea", + unusedFilters = "disabled:true" + } + }; + + s = "none=Droid"; + yield return new QueryTestCase(s) + { + queryDescriptor = new HierarchySearchProvider.HierarchyQueryDescriptor(s) + { + entityQuery = null, + entityQueryResult = HierarchyQueryBuilder.Result.Invalid("Unknown components"), + } + }; + } + + [Test] + public void ConvertToHierarchySearch([ValueSource(nameof(GetConvertToHierarchySearchTests))] QueryTestCase test) + { + var entityQueryDescriptor = HierarchySearchProvider.CreateHierarchyQueryDescriptor(test.query); + Assert.NotNull(test.queryDescriptor); + Assert.IsTrue(entityQueryDescriptor.query.valid, "Query is invalid"); + + Assert.AreEqual(test.queryDescriptor.kind, entityQueryDescriptor.kind, "Kind is different"); + Assert.AreEqual(test.queryDescriptor.entityIndex, entityQueryDescriptor.entityIndex, "entityIndex is different"); + Assert.AreEqual(test.queryDescriptor.world, entityQueryDescriptor.world, "World is different"); + + Assert.AreEqual(test.queryDescriptor.originalQueryStr, entityQueryDescriptor.originalQueryStr, "OriginalQuery is different"); + Assert.AreEqual(test.queryDescriptor.processedQueryStr, entityQueryDescriptor.processedQueryStr, "processedQueryStr is different"); + Assert.AreEqual(test.queryDescriptor.unusedFilters, entityQueryDescriptor.unusedFilters, "UnusedFilters is different"); + + CollectionAssert.AreEqual(test.queryDescriptor.searchValueTokens, entityQueryDescriptor.searchValueTokens, "searchValueTokens are different"); + Assert.AreEqual(test.queryDescriptor.searchValueTokenStr, entityQueryDescriptor.searchValueTokenStr, "searchValueTokenStr is different"); + + Assert.IsTrue((test.queryDescriptor.entityQuery == null && entityQueryDescriptor.entityQuery == null) || (test.queryDescriptor.entityQuery != null && entityQueryDescriptor.entityQuery != null), "EntityQuery doesn't match"); + + if (test.queryDescriptor.entityQueryResult.Filter != null) + Assert.AreEqual(test.queryDescriptor.entityQueryResult.IsValid, entityQueryDescriptor.entityQueryResult.IsValid, "entityQueryResult is different"); + + if (test.queryDescriptor.entityQuery != null) + { + CollectionAssert.AreEqual(test.queryDescriptor.entityQuery.All, entityQueryDescriptor.entityQuery.All, "entityQuery.All is different"); + CollectionAssert.AreEqual(test.queryDescriptor.entityQuery.None, entityQueryDescriptor.entityQuery.None, "entityQuery.None is different"); + CollectionAssert.AreEqual(test.queryDescriptor.entityQuery.Any, entityQueryDescriptor.entityQuery.Any, "entityQuery.Any is different"); + } + } + + public static IEnumerable GetPreprocessQueryTests() + { + yield return new QueryTestCase("c=Boid") { processedQuery = "all=Boid" }; + yield return new QueryTestCase("-c=Boid") { processedQuery = "none=Boid" }; + yield return new QueryTestCase("pow c=Acceleration -c=Boid") { processedQuery = "pow all=Acceleration none=Boid" }; + yield return new QueryTestCase("pow all=Acceleration none=Boid") { processedQuery = "pow all=Acceleration none=Boid" }; + yield return new QueryTestCase("pow c=Acceleration any=Ping -c=Boid") { processedQuery = "pow all=Acceleration any=Ping none=Boid" }; + + yield return new QueryTestCase("c=Acceleration pow -c=Boid") { processedQuery = "all=Acceleration pow none=Boid" }; + yield return new QueryTestCase("all=Acceleration pow none=Boid") { processedQuery = "all=Acceleration pow none=Boid" }; + yield return new QueryTestCase("c=Acceleration pow any=Ping -c=Boid") { processedQuery = "all=Acceleration pow any=Ping none=Boid" }; + + yield return new QueryTestCase("c=Acceleration -c=Boid pow") { processedQuery = "all=Acceleration none=Boid pow" }; + yield return new QueryTestCase("all=Acceleration none=Boid pow") { processedQuery = "all=Acceleration none=Boid pow" }; + yield return new QueryTestCase("c=Acceleration any=Ping -c=Boid pow") { processedQuery = "all=Acceleration any=Ping none=Boid pow" }; + } + + [Test] + public void PreprocessQuery([ValueSource(nameof(GetPreprocessQueryTests))] QueryTestCase test) + { + var preprocessQuery = HierarchySearchProvider.PreprocessQuery(test.query); + Assert.AreEqual(test.processedQuery, preprocessQuery); + } + + public static IEnumerable GetReplaceFilterInQuery() + { + yield return new QueryTestCase("p:") { filter = "ei", op = "=", value = 12, processedQuery = "p:ei=12" }; + yield return new QueryTestCase("p:ei=233") { filter = "ei", op = "=", value = 12, processedQuery = "p:ei=12" }; + yield return new QueryTestCase("p:ei=12") { filter = "ei", op = "=", value = 12, processedQuery = "p:ei=12" }; + + yield return new QueryTestCase("p:tweak") { filter = "ei", op = "=", value = 12, processedQuery = "p:tweak ei=12" }; + yield return new QueryTestCase("p:tweak ei=233") { filter = "ei", op = "=", value = 12, processedQuery = "p:tweak ei=12" }; + yield return new QueryTestCase("p:ei=233 tweak") { filter = "ei", op = "=", value = 12, processedQuery = "p:ei=12 tweak" }; + + yield return new QueryTestCase("p:tweak fo=12") { filter = "ei", op = "=", value = 12, processedQuery = "p:tweak fo=12 ei=12" }; + yield return new QueryTestCase("p:tweak ei=233 fo=12") { filter = "ei", op = "=", value = 12, processedQuery = "p:tweak ei=12 fo=12" }; + yield return new QueryTestCase("p:tweak fo=12 ei=233") { filter = "ei", op = "=", value = 12, processedQuery = "p:tweak fo=12 ei=12" }; + + + yield return new QueryTestCase("") { filter = "ei", op = "=", value = 12, processedQuery = "ei=12" }; + yield return new QueryTestCase("ei=233") { filter = "ei", op = "=", value = 12, processedQuery = "ei=12" }; + yield return new QueryTestCase("ei=12") { filter = "ei", op = "=", value = 12, processedQuery = "ei=12" }; + + yield return new QueryTestCase("tweak") { filter = "ei", op = "=", value = 12, processedQuery = "tweak ei=12" }; + yield return new QueryTestCase("tweak ei=233") { filter = "ei", op = "=", value = 12, processedQuery = "tweak ei=12" }; + yield return new QueryTestCase("ei=233 tweak") { filter = "ei", op = "=", value = 12, processedQuery = "ei=12 tweak" }; + + yield return new QueryTestCase("tweak fo=12") { filter = "ei", op = "=", value = 12, processedQuery = "tweak fo=12 ei=12" }; + yield return new QueryTestCase("tweak ei=233 fo=12") { filter = "ei", op = "=", value = 12, processedQuery = "tweak ei=12 fo=12" }; + yield return new QueryTestCase("tweak fo=12 ei=233") { filter = "ei", op = "=", value = 12, processedQuery = "tweak fo=12 ei=12" }; + } + + [Test] + public void ReplaceFilterInQuery([ValueSource(nameof(GetReplaceFilterInQuery))] QueryTestCase test) + { + var newQuery = SearchUtils.AddOrReplaceFilterInQuery(test.query, test.filter, test.op, test.value); + Assert.AreEqual(test.processedQuery, newQuery); + } + } + + public class ComponentsSearchProviderTests : QuickSearchTests + { + + } +} diff --git a/Unity.Entities.Editor.Tests/Search/SearchQueryTests.cs.meta b/Unity.Entities.Editor.Tests/Search/SearchQueryTests.cs.meta new file mode 100644 index 00000000..fedcbd1c --- /dev/null +++ b/Unity.Entities.Editor.Tests/Search/SearchQueryTests.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: c76e1377b8eae5a43b727b2b9a025aff +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Unity.Entities.Editor.Tests/SystemSchedule/SystemScheduleTestUtilities.cs b/Unity.Entities.Editor.Tests/SystemSchedule/SystemScheduleTestUtilities.cs index c900cddb..174b520c 100644 --- a/Unity.Entities.Editor.Tests/SystemSchedule/SystemScheduleTestUtilities.cs +++ b/Unity.Entities.Editor.Tests/SystemSchedule/SystemScheduleTestUtilities.cs @@ -120,7 +120,7 @@ public static void ExpandAllGroupNodes(SystemTreeView treeView, ITreeViewItem it public class UpdateSystemGraph : IEditModeTestYieldInstruction { - const int k_WaitFrames = 1000; + const int k_WaitFrames = 6000; readonly SystemScheduleWindow m_SystemScheduleWindow; readonly Type m_GivenSystemType; int m_Count; diff --git a/Unity.Entities.Editor.Tests/SystemSchedule/SystemScheduleWindowIntegrationTests.cs b/Unity.Entities.Editor.Tests/SystemSchedule/SystemScheduleWindowIntegrationTests.cs index 627167e8..af00b485 100644 --- a/Unity.Entities.Editor.Tests/SystemSchedule/SystemScheduleWindowIntegrationTests.cs +++ b/Unity.Entities.Editor.Tests/SystemSchedule/SystemScheduleWindowIntegrationTests.cs @@ -1,10 +1,7 @@ -using System; using System.Collections; using NUnit.Framework; using System.IO; using System.Linq; -using Unity.Entities.Conversion; -using Unity.Entities.UI; using Unity.Scenes; using Unity.Scenes.Editor; using UnityEditor; @@ -20,6 +17,7 @@ namespace Unity.Entities.Editor.Tests partial class SystemScheduleWindowIntegrationTests { World m_DefaultWorld; + World m_TestWorld; ComponentSystemGroup m_TestSystemGroup; ComponentSystemBase m_TestSystem1; ComponentSystemBase m_TestSystem2; @@ -114,9 +112,10 @@ public void TearDown() SystemScheduleTestUtilities.DestroySystemsWindow(m_SystemScheduleWindow); if (EditorWindow.HasOpenInstances()) - { EditorWindow.GetWindow().Close(); - } + + if (m_TestWorld is { IsCreated: true }) + m_TestWorld.Dispose(); } void CreateTestSystems(World world) @@ -249,11 +248,11 @@ public IEnumerator SystemScheduleWindow_CustomWorld() { var previousPlayerLoop = PlayerLoop.GetCurrentPlayerLoop(); - var world = new World(k_SystemScheduleTestWorld); - CreateTestSystems(world); + m_TestWorld = new World(k_SystemScheduleTestWorld); + CreateTestSystems(m_TestWorld); var playerLoop = PlayerLoop.GetDefaultPlayerLoop(); - ScriptBehaviourUpdateOrder.AppendWorldToPlayerLoop(world, ref playerLoop); + ScriptBehaviourUpdateOrder.AppendWorldToPlayerLoop(m_TestWorld, ref playerLoop); PlayerLoop.SetPlayerLoop(playerLoop); yield return new SystemScheduleTestUtilities.UpdateSystemGraph(typeof(SystemScheduleTestGroup)); @@ -261,14 +260,14 @@ public IEnumerator SystemScheduleWindow_CustomWorld() Assert.That(m_SystemScheduleWindow.SelectedWorld.Name, Is.EqualTo(k_SystemScheduleTestWorld)); Assert.That(m_SystemScheduleWindow.rootVisualElement.Q().CheckIfTreeViewContainsGivenSystemType(typeof(SystemScheduleTestGroup), out _), Is.True); - world.Dispose(); + m_TestWorld.Dispose(); PlayerLoop.SetPlayerLoop(previousPlayerLoop); m_SystemScheduleWindow.Update(); Assert.That(m_SystemScheduleWindow.SelectedWorld.Name, Is.EqualTo(k_SystemScheduleEditorWorld)); - if (world.IsCreated) - world.Dispose(); + if (m_TestWorld is { IsCreated: true }) + m_TestWorld.Dispose(); } [UnityTest] @@ -286,11 +285,11 @@ public IEnumerator SystemScheduleWindow_HashCodeForTwoSystemsWithSameType() var testSystemInDefaultWorldHash = testSystemItemDefaultWorld.Node.Hash; // Create test system in test world. - var world = new World(k_SystemScheduleTestWorld); - CreateTestSystems(world); + m_TestWorld = new World(k_SystemScheduleTestWorld); + CreateTestSystems(m_TestWorld); var playerLoop = PlayerLoop.GetCurrentPlayerLoop(); - ScriptBehaviourUpdateOrder.AppendWorldToPlayerLoop(world, ref playerLoop); + ScriptBehaviourUpdateOrder.AppendWorldToPlayerLoop(m_TestWorld, ref playerLoop); PlayerLoop.SetPlayerLoop(playerLoop); // Wait a frame for the window to update. @@ -303,8 +302,8 @@ public IEnumerator SystemScheduleWindow_HashCodeForTwoSystemsWithSameType() var testSystemInTestWorldHash = testSystemItemTestWorld.Node.Hash; - if (world.IsCreated) - world.Dispose(); + if (m_TestWorld.IsCreated) + m_TestWorld.Dispose(); PlayerLoop.SetPlayerLoop(previousPlayerLoop); Assert.That(testSystemInDefaultWorldHash, Is.Not.EqualTo(testSystemInTestWorldHash)); @@ -317,8 +316,8 @@ public IEnumerator SystemScheduleWindow_ScheduleSystemInDifferentWorld() var oldSystemCount = m_SystemScheduleWindow.rootVisualElement.Q().m_SystemTreeView.items.Count(); - var testWorld = new World(k_SystemScheduleTestWorld); - var managedSystem = testWorld.GetOrCreateSystemManaged(); + m_TestWorld = new World(k_SystemScheduleTestWorld); + var managedSystem = m_TestWorld.GetOrCreateSystemManaged(); var simulationSystemGroup = World.DefaultGameObjectInjectionWorld.GetOrCreateSystemManaged(); simulationSystemGroup.AddSystemToUpdateList(managedSystem); @@ -334,8 +333,8 @@ public IEnumerator SystemScheduleWindow_ScheduleSystemInDifferentWorld() if (managedSystem != null) World.DefaultGameObjectInjectionWorld.GetOrCreateSystemManaged().RemoveSystemFromUpdateList(managedSystem); - if (testWorld.IsCreated) - testWorld.Dispose(); + if (m_TestWorld.IsCreated) + m_TestWorld.Dispose(); } [UnityTest] diff --git a/Unity.Entities.Editor.Tests/SystemSchedule/TestSystems.cs b/Unity.Entities.Editor.Tests/SystemSchedule/TestSystems.cs index c5340fdc..e563820b 100644 --- a/Unity.Entities.Editor.Tests/SystemSchedule/TestSystems.cs +++ b/Unity.Entities.Editor.Tests/SystemSchedule/TestSystems.cs @@ -29,7 +29,7 @@ protected override void OnUpdate() } } - struct SystemScheduleTestUnmanagedSystem : ISystem + partial struct SystemScheduleTestUnmanagedSystem : ISystem { } diff --git a/Unity.Entities.Editor.Tests/Utilities/AutoCompleteTests.cs b/Unity.Entities.Editor.Tests/Utilities/AutoCompleteTests.cs index 0dff1085..a8f1e6e8 100644 --- a/Unity.Entities.Editor.Tests/Utilities/AutoCompleteTests.cs +++ b/Unity.Entities.Editor.Tests/Utilities/AutoCompleteTests.cs @@ -192,6 +192,9 @@ public override string ToString() new TestCase() { token = "c:a", shouldAutoComplete = false }, new TestCase() { token = "C:", shouldAutoComplete = false }, new TestCase() { token = "C:a", shouldAutoComplete = false }, + + new TestCase() { token = "k=a", shouldAutoComplete = false }, + new TestCase() { token = "dummy=a", shouldAutoComplete = false }, }; [Test] @@ -200,5 +203,47 @@ public void Run([ValueSource(nameof(testCases))] TestCase testCase) var autoComplete = ComponentTypeAutoComplete.Instance; Assert.AreEqual(autoComplete.ShouldStartAutoCompletion(testCase.token, testCase.token.Length), testCase.shouldAutoComplete); } + + static TestCase[] advancedTestCases = new[] + { + new TestCase() { token = "c=a", shouldAutoComplete = true }, + new TestCase() { token = "c=acc", shouldAutoComplete = true }, + new TestCase() { token = "C=acc", shouldAutoComplete = true }, + + new TestCase() { token = "none=acc", shouldAutoComplete = true }, + new TestCase() { token = "nOne=acc", shouldAutoComplete = true }, + + new TestCase() { token = "any=acc", shouldAutoComplete = true }, + new TestCase() { token = "Any=acc", shouldAutoComplete = true }, + + new TestCase() { token = "all=acc", shouldAutoComplete = true }, + new TestCase() { token = "aLL=acc", shouldAutoComplete = true }, + + new TestCase() { token = "none:acc", shouldAutoComplete = false }, + new TestCase() { token = "all:acc", shouldAutoComplete = false }, + new TestCase() { token = "any:acc", shouldAutoComplete = false }, + + new TestCase() { token = "c:a", shouldAutoComplete = false }, + new TestCase() { token = "C:a", shouldAutoComplete = false }, + + new TestCase() { token = "c", shouldAutoComplete = false }, + new TestCase() { token = "C", shouldAutoComplete = false }, + + new TestCase() { token = "c=", shouldAutoComplete = false }, + new TestCase() { token = "C=", shouldAutoComplete = false }, + + new TestCase() { token = "c:", shouldAutoComplete = false }, + new TestCase() { token = "C:", shouldAutoComplete = false }, + + new TestCase() { token = "k=a", shouldAutoComplete = false }, + new TestCase() { token = "dummy=a", shouldAutoComplete = false }, + }; + + [Test] + public void RunAdvanceEntityQuery([ValueSource(nameof(advancedTestCases))] TestCase testCase) + { + var autoComplete = ComponentTypeAutoComplete.EntityQueryInstance; + Assert.AreEqual(autoComplete.ShouldStartAutoCompletion(testCase.token, testCase.token.Length), testCase.shouldAutoComplete); + } } } diff --git a/Unity.Entities.Editor.Tests/Utilities/WorldListChangeTrackerTests.cs b/Unity.Entities.Editor.Tests/Utilities/WorldListChangeTrackerTests.cs index cb04f10b..97a4f943 100644 --- a/Unity.Entities.Editor.Tests/Utilities/WorldListChangeTrackerTests.cs +++ b/Unity.Entities.Editor.Tests/Utilities/WorldListChangeTrackerTests.cs @@ -35,11 +35,7 @@ public void DoNotDetectChangesWithinWorlds() Assert.That(m_Tracker.HasChanged(), Is.False); var nextSequenceNumber = World.NextSequenceNumber; -#if !ENABLE_TRANSFORM_V1 using var q = w.EntityManager.CreateEntityQuery(typeof(LocalTransform)); -#else - using var q = w.EntityManager.CreateEntityQuery(typeof(Rotation)); -#endif Assert.That(nextSequenceNumber, Is.Not.EqualTo(World.NextSequenceNumber), "World.NextSequenceNumber must be different to the previously captured one because we created a query."); Assert.That(m_Tracker.HasChanged(), Is.False, "No changes should be detected by the tracker since no world has been created or destroyed"); diff --git a/Unity.Entities.Editor/Archetypes/ArchetypesWindow+Recorder.cs b/Unity.Entities.Editor/Archetypes/ArchetypesWindow+Recorder.cs index d51d89e2..4d4a29b1 100644 --- a/Unity.Entities.Editor/Archetypes/ArchetypesWindow+Recorder.cs +++ b/Unity.Entities.Editor/Archetypes/ArchetypesWindow+Recorder.cs @@ -9,7 +9,7 @@ namespace Unity.Entities.Editor { partial class ArchetypesWindow { - unsafe class ArchetypesMemoryDataRecorder : IDisposable + internal unsafe class ArchetypesMemoryDataRecorder : IDisposable { class Recorder : IDisposable { diff --git a/Unity.Entities.Editor/Archetypes/ArchetypesWindow.cs b/Unity.Entities.Editor/Archetypes/ArchetypesWindow.cs index 5ce37d69..73d5c1e9 100644 --- a/Unity.Entities.Editor/Archetypes/ArchetypesWindow.cs +++ b/Unity.Entities.Editor/Archetypes/ArchetypesWindow.cs @@ -94,7 +94,7 @@ IEnumerable GetTreeViewData() } } - static unsafe bool MemCmp(NativeArray lhs, NativeArray rhs) + internal static unsafe bool MemCmp(NativeArray lhs, NativeArray rhs) where T : unmanaged { if (lhs.Length != rhs.Length) diff --git a/Unity.Entities.Editor/AssemblyInfo.cs b/Unity.Entities.Editor/AssemblyInfo.cs index c036b22f..96569154 100644 --- a/Unity.Entities.Editor/AssemblyInfo.cs +++ b/Unity.Entities.Editor/AssemblyInfo.cs @@ -2,6 +2,5 @@ [assembly: InternalsVisibleTo("Unity.Entities.Editor.Tests")] [assembly: InternalsVisibleTo("Unity.Entities.Editor.PerformanceTests")] -[assembly: InternalsVisibleTo("Unity.Tiny.Authoring")] [assembly: InternalsVisibleTo("Unity.NetCode.Editor")] [assembly: InternalsVisibleTo("Unity.Environment.Editor")] diff --git a/Unity.Entities.Editor/Component/ComponentsWindow.cs b/Unity.Entities.Editor/Component/ComponentsWindow.cs index 8d8cea6b..f65b54e5 100644 --- a/Unity.Entities.Editor/Component/ComponentsWindow.cs +++ b/Unity.Entities.Editor/Component/ComponentsWindow.cs @@ -8,6 +8,7 @@ using UnityEngine; using UnityEngine.UIElements; using ListView = UnityEngine.UIElements.ListView; +using Unity.Editor.Bridge; namespace Unity.Entities.Editor { @@ -161,6 +162,7 @@ void OnEnable() void Build() { Resources.Templates.ContentProvider.ComponentsWindow.Clone(rootVisualElement); + Resources.Templates.DotsEditorCommon.AddStyles(rootVisualElement); m_ListView = rootVisualElement.Q(className:"components-window__list-view"); m_ListView.fixedItemHeight = Constants.ListView.ItemHeight; @@ -199,6 +201,7 @@ void Build() }); m_SearchElement.value = m_State.SearchString; m_SearchElement.Search(); + m_SearchElement.parent.Add(SearchUtils.CreateJumpButton(() => ComponentSearchProvider.OpenProvider(m_SearchElement.value))); SetSelection(); } diff --git a/Unity.Entities.Editor/Constants.cs b/Unity.Entities.Editor/Constants.cs index 3e8f9083..8946640f 100644 --- a/Unity.Entities.Editor/Constants.cs +++ b/Unity.Entities.Editor/Constants.cs @@ -69,6 +69,9 @@ public static class SystemSchedule public static class ComponentSearch { public const string Token = "c"; + public const string All = "all"; + public const string None = "none"; + public const string Any = "any"; public const string Op = "="; public const string TokenCaseInsensitive = "cC"; public const string TokenOp = "c="; @@ -78,6 +81,8 @@ public static class Hierarchy { public const string EntityIndexToken = "ei"; public const string EntityIndexTokenOpEqual = "ei="; + public const string NodeKindOpEqual = "k="; + public const string KindToken = "k"; } public static class Inspector diff --git a/Unity.Entities.Editor/Hierarchy/Controls/HierarchyElement.cs b/Unity.Entities.Editor/Hierarchy/Controls/HierarchyElement.cs index 8a7b291c..c03121e6 100644 --- a/Unity.Entities.Editor/Hierarchy/Controls/HierarchyElement.cs +++ b/Unity.Entities.Editor/Hierarchy/Controls/HierarchyElement.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using Unity.Editor.Bridge; using UnityEditor; @@ -95,9 +95,6 @@ enum ViewType static readonly int[] k_SingleSelectionBuffer = new int[1]; const string k_ListViewName = "unity-tree-view__list-view"; - - static readonly string k_ComponentTypeNotFoundTitle = L10n.Tr("Type not found"); - static readonly string k_ComponentTypeNotFoundContent = L10n.Tr("\"{0}\" is not a component type"); static readonly string k_NoItemFoundTitle = L10n.Tr("No item matches your search"); ViewType m_ViewType; @@ -286,10 +283,10 @@ public void Refresh() { var filter = m_Nodes.GetFilter(); - if (!filter.IsValid && !string.IsNullOrEmpty(filter.ErrorComponentType)) + if (!filter.IsValid) { - m_MessageElement.Title = k_ComponentTypeNotFoundTitle; - m_MessageElement.Message = string.Format(k_ComponentTypeNotFoundContent, filter.ErrorComponentType); + m_MessageElement.Title = filter.ErrorCategory; + m_MessageElement.Message = filter.ErrorMsg; SetView(ViewType.Message); return; } diff --git a/Unity.Entities.Editor/Hierarchy/HierarchySettings.cs b/Unity.Entities.Editor/Hierarchy/HierarchySettings.cs index 7a268a81..e38df595 100644 --- a/Unity.Entities.Editor/Hierarchy/HierarchySettings.cs +++ b/Unity.Entities.Editor/Hierarchy/HierarchySettings.cs @@ -1,9 +1,9 @@ -using JetBrains.Annotations; +using JetBrains.Annotations; using Unity.Properties; using Unity.Entities.UI; using UnityEditor; -using UnityEditor.UIElements; using UnityEngine.UIElements; +using System; namespace Unity.Entities.Editor { @@ -12,8 +12,12 @@ class HierarchySettings : ISetting { [InternalSetting] public HierarchyConfiguration Configuration = new HierarchyConfiguration(); + public event Action UseAdvanceSearchSettingChanged; + void ISetting.OnSettingChanged(PropertyPath path) { + if (path.Equals(new PropertyPath($"{nameof(Configuration)}.{nameof(Configuration.AdvancedSearch)}"))) + UseAdvanceSearchSettingChanged?.Invoke(); } [UsedImplicitly] @@ -31,10 +35,12 @@ public override VisualElement Build() var updateModeType = new VisualElement(); var millisecondsBetweenUpdateCycles = new VisualElement(); var excludeUnnamedNodesForSearch = new VisualElement(); + var useAdvanceSearch = new VisualElement(); DoDefaultGui(updateModeType, nameof(Configuration) + "." + nameof(HierarchyConfiguration.UpdateMode)); DoDefaultGui(millisecondsBetweenUpdateCycles, nameof(Configuration) + "." + nameof(HierarchyConfiguration.MinimumMillisecondsBetweenHierarchyUpdateCycles)); DoDefaultGui(excludeUnnamedNodesForSearch, nameof(Configuration) + "." + nameof(HierarchyConfiguration.ExcludeUnnamedNodesForSearch)); + DoDefaultGui(useAdvanceSearch, nameof(Configuration) + "." + nameof(HierarchyConfiguration.AdvancedSearch)); root.Add(updateModeType); root.Add(millisecondsBetweenUpdateCycles); @@ -66,6 +72,7 @@ public override VisualElement Build() } root.Add(excludeUnnamedNodesForSearch); + root.Add(useAdvanceSearch); return root; } diff --git a/Unity.Entities.Editor/Hierarchy/HierarchyWindow.cs b/Unity.Entities.Editor/Hierarchy/HierarchyWindow.cs index 029dc271..761f9931 100644 --- a/Unity.Entities.Editor/Hierarchy/HierarchyWindow.cs +++ b/Unity.Entities.Editor/Hierarchy/HierarchyWindow.cs @@ -14,15 +14,39 @@ partial class HierarchyWindow : DOTSEditorWindow { static readonly ProfilerMarker k_OnUpdateMarker = new ProfilerMarker($"{nameof(HierarchyWindow)}.{nameof(OnUpdate)}"); - static readonly string k_FilterComponentType = L10n.Tr("Component type"); - static readonly string k_FilterComponentTypeTooltip = L10n.Tr("Filter entities that have the specified component type"); + static readonly string k_FilterComponentType = L10n.Tr("All Components"); + static readonly string k_FilterComponentTypeTooltip = L10n.Tr("Filter entities that have all the specified component types"); + + static readonly string k_FilterAnyComponentType = L10n.Tr("Any Component"); + static readonly string k_FilterAnyComponentTypeTooltip = L10n.Tr("Filter entities that have any of the specified component types"); + + static readonly string k_FilterNoneComponentType = L10n.Tr("No Component"); + static readonly string k_FilterNoneComponentTypeTooltip = L10n.Tr("Filter entities that have none of the specified component types"); static readonly string k_FilterIndexToken = L10n.Tr("Entity Index"); static readonly string k_FilterIndexTokenTooltip = L10n.Tr("Filter entities that have the specified index"); + static readonly string k_FilterKindToken = L10n.Tr("Node Kind"); + static readonly string k_FilterKindTokenTooltip = L10n.Tr("Filter entities that have the specified Node type."); + static readonly string k_WindowName = L10n.Tr("Entities Hierarchy"); static readonly Vector2 k_MinWindowSize = Constants.MinWindowSize; + static readonly EntityQueryOptions[] k_EntityQueryOptions = new [] { + EntityQueryOptions.FilterWriteGroup, + EntityQueryOptions.IgnoreComponentEnabledState, + EntityQueryOptions.IncludeDisabledEntities, + EntityQueryOptions.IncludePrefab, + EntityQueryOptions.IncludeSystems + }; + + static readonly NodeKind[] k_NodeKinds = new[] { + NodeKind.Entity, + NodeKind.GameObject, + NodeKind.Scene, + NodeKind.SubScene + }; + static readonly HierarchyDecoratorCollection s_Decorators = new HierarchyDecoratorCollection(); internal static void AddDecorator(IHierarchyItemDecorator decorator) => s_Decorators.Add(decorator); @@ -31,8 +55,10 @@ partial class HierarchyWindow : DOTSEditorWindow readonly Cooldown m_BackgroundUpdateCooldown = new Cooldown(TimeSpan.FromMilliseconds(250)); bool m_IsVisible; + Hierarchy m_Hierarchy; HierarchyElement m_HierarchyElement; + HierarchySettings m_HierarchySettings; /// /// Flag indicating we should try to apply the global selection on the next update cycle. @@ -44,6 +70,7 @@ partial class HierarchyWindow : DOTSEditorWindow /// uint m_GlobalSelectionRequestUpdateVersion; SearchElement m_SearchElement; + AutoComplete m_SearchAutocomplete; [MenuItem(Constants.MenuItems.HierarchyWindow, false, Constants.MenuItems.HierarchyWindowPriority)] static void OpenWindow() => GetWindow(); @@ -60,12 +87,15 @@ void OnEnable() Resources.AddCommonVariables(rootVisualElement); // Initialize the data models. + m_HierarchySettings = UserSettings.GetOrCreate(Constants.Settings.Hierarchy); m_Hierarchy = new Hierarchy(Allocator.Persistent, dataModeController.dataMode) { - Configuration = UserSettings.GetOrCreate(Constants.Settings.Hierarchy).Configuration, + Configuration = m_HierarchySettings.Configuration, State = SessionState.GetOrCreate($"{GetType().Name}.{nameof(HierarchyState)}") }; + m_HierarchySettings.UseAdvanceSearchSettingChanged += OnUseAdvanceSearchSettingChanged; + // Initialize the view models. CreateToolbar(rootVisualElement, m_Hierarchy); m_HierarchyElement = CreateHierarchyElement(rootVisualElement, m_Hierarchy); @@ -81,6 +111,7 @@ void OnEnable() void OnDisable() { + m_HierarchySettings.UseAdvanceSearchSettingChanged -= OnUseAdvanceSearchSettingChanged; EditorApplication.update -= OnBackgroundUpdate; Selection.selectionChanged -= OnGlobalSelectionChanged; dataModeController.dataModeChanged -= OnDataModeChanged; @@ -99,6 +130,13 @@ void OnBecameVisible() RequestGlobalSelectionRestoration(); } + void OnUseAdvanceSearchSettingChanged() + { + SetupSearchOptions(); + if (!string.IsNullOrEmpty(m_SearchElement.value)) + m_SearchElement.ClearSearchString(); + } + void RequestGlobalSelectionRestoration() { // Request the selection to be updated after the next update cycle. @@ -144,18 +182,68 @@ VisualElement CreateToolbar(VisualElement root, Hierarchy hierarchy) m_SearchElement = AddSearchElement(toolbar, UssClasses.DotsEditorCommon.SearchFieldContainer); m_SearchElement.RegisterSearchQueryHandler(query => { - hierarchy.SetSearchQuery(query.SearchString, query.Tokens); + if (hierarchy.Configuration.AdvancedSearch) + { + var searchString = query.SearchString; + if (string.IsNullOrEmpty(searchString)) + { + hierarchy.SetFilter(null); + return; + } + + var desc = HierarchySearchProvider.CreateHierarchyQueryDescriptor(searchString); + var filter = HierarchySearchProvider.CreateHierarchyFilter(hierarchy.HierarchySearch, desc, hierarchy.Allocator); + hierarchy.SetFilter(filter); + } + else + { + hierarchy.SetSearchQuery(query.SearchString, query.Tokens); + } }); - m_SearchElement.AddSearchFilterPopupItem(Constants.ComponentSearch.Token, k_FilterComponentType, k_FilterComponentTypeTooltip, Constants.ComponentSearch.Op); - m_SearchElement.AddSearchFilterPopupItem(Constants.Hierarchy.EntityIndexToken, k_FilterIndexToken, k_FilterIndexTokenTooltip, "="); - m_SearchElement.EnableAutoComplete(ComponentTypeAutoComplete.Instance); + SetupSearchOptions(); + m_SearchElement.parent.Add(SearchUtils.CreateJumpButton(() => HierarchySearchProvider.OpenProvider(m_SearchElement.value))); root.Add(toolbar); return toolbar; } + void SetupSearchOptions() + { + m_SearchElement.ClearSearchFilterPopupItem(); + if (m_Hierarchy.Configuration.AdvancedSearch) + { + // Advanced supported Filters: + // all, none, any, EntityQueryOptions + // kind, ei + m_SearchElement.AddSearchFilterPopupItem(Constants.ComponentSearch.All, k_FilterComponentType, k_FilterComponentTypeTooltip, Constants.ComponentSearch.Op); + m_SearchElement.AddSearchFilterPopupItem(Constants.ComponentSearch.None, k_FilterNoneComponentType, k_FilterNoneComponentType, Constants.ComponentSearch.Op); + m_SearchElement.AddSearchFilterPopupItem(Constants.ComponentSearch.Any, k_FilterAnyComponentType, k_FilterAnyComponentType, Constants.ComponentSearch.Op); + m_SearchElement.AddSearchFilterPopupItem(Constants.Hierarchy.EntityIndexToken, k_FilterIndexToken, k_FilterIndexTokenTooltip, "="); + + foreach(var opt in k_EntityQueryOptions) + { + m_SearchElement.AddSearchFilterPopupItem($"+{opt}", "Option", k_FilterIndexTokenTooltip, " ", isCompleteFilter: true); + } + + foreach(var k in k_NodeKinds) + { + m_SearchElement.AddSearchFilterPopupItem($"{Constants.Hierarchy.KindToken}={k}", k_FilterKindToken, k_FilterKindTokenTooltip, " ", isCompleteFilter: true); + } + + m_SearchAutocomplete?.Dispose(); + m_SearchAutocomplete = new AutoComplete(m_SearchElement, ComponentTypeAutoComplete.EntityQueryInstance); + } + else + { + m_SearchElement.AddSearchFilterPopupItem(Constants.ComponentSearch.Token, k_FilterComponentType, k_FilterComponentTypeTooltip, Constants.ComponentSearch.Op); + m_SearchElement.AddSearchFilterPopupItem(Constants.Hierarchy.EntityIndexToken, k_FilterIndexToken, k_FilterIndexTokenTooltip, "="); + m_SearchAutocomplete?.Dispose(); + m_SearchAutocomplete = new AutoComplete(m_SearchElement, ComponentTypeAutoComplete.Instance); + } + } + void OnGlobalSelectionChanged() => ApplyGlobalSelection(false); void ApplyGlobalSelection(bool allowReentry) @@ -213,89 +301,94 @@ void SetSelection(UnityEngine.Object obj) } } - void OnHierarchySelectionChanged(HierarchyNodeHandle handle) + public static void SelectHierarchyNode(Hierarchy hierarchy, HierarchyNodeHandle handle, DataMode dataMode) { - if (!m_IsVisible) - return; - switch (handle.Kind) { case NodeKind.Entity: - { - var entity = handle.ToEntity(); - - if (entity != Entity.Null) { - var undoGroup = Undo.GetCurrentGroup(); + var entity = handle.ToEntity(); - var world = m_Hierarchy.World; - var authoringObject = world.EntityManager.Debug.GetAuthoringObjectForEntity(entity); - - if (authoringObject == null) - { - EntitySelectionProxy.SelectEntity(world, entity); - } - else + if (entity != Entity.Null) { - var context = EntitySelectionProxy.CreateInstance(world, entity); - // Selected entities should always try to show up in Runtime mode - SelectionBridge.SetSelection(authoringObject, context, DataMode.Runtime); + var undoGroup = Undo.GetCurrentGroup(); + + var world = hierarchy.World; + var authoringObject = world.EntityManager.Debug.GetAuthoringObjectForEntity(entity); + + if (authoringObject == null) + { + EntitySelectionProxy.SelectEntity(world, entity); + } + else + { + var context = EntitySelectionProxy.CreateInstance(world, entity); + // Selected entities should always try to show up in Runtime mode + SelectionBridge.SetSelection(authoringObject, context, DataMode.Runtime); + } + + Undo.CollapseUndoOperations(undoGroup); } - Undo.CollapseUndoOperations(undoGroup); + break; } - break; - } - case NodeKind.SubScene: - { - var undoGroup = Undo.GetCurrentGroup(); - var subScene = m_Hierarchy.GetUnityObject(handle) as SubScene; - SelectionBridge.SetSelection(subScene ? subScene.gameObject : null, HierarchySelectionContext.CreateInstance(handle), DataMode.Disabled); - Undo.CollapseUndoOperations(undoGroup); - break; - } + { + var undoGroup = Undo.GetCurrentGroup(); + var subScene = hierarchy.SubSceneMap.GetSubSceneMonobehaviourFromHandle(handle); + SelectionBridge.SetSelection(subScene ? subScene.gameObject : null, HierarchySelectionContext.CreateInstance(handle), DataMode.Disabled); + Undo.CollapseUndoOperations(undoGroup); + break; + } case NodeKind.Scene: - { - var undoGroup = Undo.GetCurrentGroup(); - SelectionBridge.SetSelection(null, HierarchySelectionContext.CreateInstance(handle), DataMode.Disabled); - Undo.CollapseUndoOperations(undoGroup); - break; - } + { + var undoGroup = Undo.GetCurrentGroup(); + SelectionBridge.SetSelection(null, HierarchySelectionContext.CreateInstance(handle), DataMode.Disabled); + Undo.CollapseUndoOperations(undoGroup); + break; + } case NodeKind.GameObject: - { - var undoGroup = Undo.GetCurrentGroup(); - var gameObject = m_Hierarchy.GetUnityObject(handle) as GameObject; + { + var undoGroup = Undo.GetCurrentGroup(); + var gameObject = hierarchy.GetUnityObject(handle) as GameObject; - // Don't reselect yourself - if (Selection.activeObject == gameObject) - return; + // Don't reselect yourself + if (Selection.activeObject == gameObject) + return; - var world = m_Hierarchy.World; - EntitySelectionProxy context; + var world = hierarchy.World; + EntitySelectionProxy context; - if (world == null || !world.IsCreated) - context = null; - else - { - var primaryEntity = world.EntityManager.Debug.GetPrimaryEntityForAuthoringObject(gameObject); + if (world == null || !world.IsCreated) + context = null; + else + { + var primaryEntity = world.EntityManager.Debug.GetPrimaryEntityForAuthoringObject(gameObject); - context = primaryEntity != Entity.Null && world.EntityManager.SafeExists(primaryEntity) - ? EntitySelectionProxy.CreateInstance(world, primaryEntity) - : null; - } + context = primaryEntity != Entity.Null && world.EntityManager.SafeExists(primaryEntity) + ? EntitySelectionProxy.CreateInstance(world, primaryEntity) + : null; + } - // Selected GameObjects should use whatever the current DataMode for the hierarchy is. - SelectionBridge.SetSelection(gameObject, context, dataModeController.dataMode); - Undo.CollapseUndoOperations(undoGroup); - break; - } + // Selected GameObjects should use whatever the current DataMode for the hierarchy is. + SelectionBridge.SetSelection(gameObject, context, dataMode); + Undo.CollapseUndoOperations(undoGroup); + break; + } } } + void OnHierarchySelectionChanged(HierarchyNodeHandle handle) + { + if (!m_IsVisible) + return; + + SelectHierarchyNode(m_Hierarchy, handle, dataModeController.dataMode); + } + protected override void OnWorldSelected(World world) { m_Hierarchy.SetWorld(world); @@ -310,6 +403,7 @@ protected override void OnUpdate() if (m_Hierarchy == null) return; + using (k_OnUpdateMarker.Auto()) { m_Hierarchy.Update(m_IsVisible); diff --git a/Unity.Entities.Editor/Hierarchy/Model/ChangeTracking/ComponentDataDiffer.cs b/Unity.Entities.Editor/Hierarchy/Model/ChangeTracking/ComponentDataDiffer.cs index 376589e9..b011019b 100644 --- a/Unity.Entities.Editor/Hierarchy/Model/ChangeTracking/ComponentDataDiffer.cs +++ b/Unity.Entities.Editor/Hierarchy/Model/ChangeTracking/ComponentDataDiffer.cs @@ -304,6 +304,9 @@ public void Dispose() /// if the specified component type can be watched. otherwise. public static bool CanWatch(ComponentType componentType) { + if (!TypeManager.IsInitialized) + throw new InvalidOperationException($"{nameof(TypeManager)} has not been initialized properly"); + var typeInfo = TypeManager.GetTypeInfo(componentType.TypeIndex); return typeInfo.Category == TypeManager.TypeCategory.ComponentData && UnsafeUtility.IsUnmanaged(componentType.GetManagedType()); } diff --git a/Unity.Entities.Editor/Hierarchy/Model/ChangeTracking/SharedComponentDataDiffer.cs b/Unity.Entities.Editor/Hierarchy/Model/ChangeTracking/SharedComponentDataDiffer.cs index 7cbe9332..0c59b199 100644 --- a/Unity.Entities.Editor/Hierarchy/Model/ChangeTracking/SharedComponentDataDiffer.cs +++ b/Unity.Entities.Editor/Hierarchy/Model/ChangeTracking/SharedComponentDataDiffer.cs @@ -57,7 +57,12 @@ public SharedComponentDataDiffer(ComponentType componentType) public ComponentType WatchedComponentType { get; } public static bool CanWatch(ComponentType componentType) - => TypeManager.GetTypeInfo(componentType.TypeIndex).Category == TypeManager.TypeCategory.ISharedComponentData; + { + if (!TypeManager.IsInitialized) + throw new InvalidOperationException($"{nameof(TypeManager)} has not been initialized properly"); + + return TypeManager.GetTypeInfo(componentType.TypeIndex).Category == TypeManager.TypeCategory.ISharedComponentData; + } public unsafe void Dispose() { diff --git a/Unity.Entities.Editor/Hierarchy/Model/ChangeTracking/UnmanagedSharedComponentDataDiffer.cs b/Unity.Entities.Editor/Hierarchy/Model/ChangeTracking/UnmanagedSharedComponentDataDiffer.cs index e5f3388d..92c6596d 100644 --- a/Unity.Entities.Editor/Hierarchy/Model/ChangeTracking/UnmanagedSharedComponentDataDiffer.cs +++ b/Unity.Entities.Editor/Hierarchy/Model/ChangeTracking/UnmanagedSharedComponentDataDiffer.cs @@ -312,6 +312,9 @@ public UnmanagedSharedComponentDataDiffer(ComponentType componentType) { static bool CanWatch(ComponentType componentType) { + if (!TypeManager.IsInitialized) + throw new InvalidOperationException($"{nameof(TypeManager)} has not been initialized properly"); + var typeInfo = TypeManager.GetTypeInfo(componentType.TypeIndex); return typeInfo.Category == TypeManager.TypeCategory.ISharedComponentData && UnsafeUtility.IsUnmanaged(componentType.GetManagedType()); } diff --git a/Unity.Entities.Editor/Hierarchy/Model/Hierarchy.cs b/Unity.Entities.Editor/Hierarchy/Model/Hierarchy.cs index 83259799..bd10dce0 100644 --- a/Unity.Entities.Editor/Hierarchy/Model/Hierarchy.cs +++ b/Unity.Entities.Editor/Hierarchy/Model/Hierarchy.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; @@ -64,6 +64,8 @@ class HierarchyConfiguration /// A flag to disable considering unnamed nodes when searching. This will accelerate performance. /// [CreateProperty] public bool ExcludeUnnamedNodesForSearch = false; + + [CreateProperty] public bool AdvancedSearch = true; } /// @@ -287,6 +289,9 @@ public enum UpdateModeType /// HierarchyFilter m_Filter; + internal Allocator Allocator => m_Allocator; + internal HierarchySearch HierarchySearch => m_Search; + internal readonly SubSceneMap SubSceneMap; /// @@ -381,14 +386,16 @@ public void Dispose() /// Optional; pre processed set of tokens by a search backend. public void SetSearchQuery(string searchString, ICollection tokens) { - m_Filter?.Dispose(); - - // Construct a hierarchy filter based on the given search parameters. - m_Filter = !string.IsNullOrEmpty(searchString) + var filter = !string.IsNullOrEmpty(searchString) ? m_Search.CreateHierarchyFilter(searchString, tokens, m_Allocator) : null; + SetFilter(filter); + } - // Set the 'view' nodes filter. This is filtering that will be applied after the fact on the nodes. + internal void SetFilter(HierarchyFilter filter) + { + m_Filter?.Dispose(); + m_Filter = filter; m_HierarchyNodes.SetFilter(m_Filter); } diff --git a/Unity.Entities.Editor/Hierarchy/Model/HierarchyFilter.cs b/Unity.Entities.Editor/Hierarchy/Model/HierarchyFilter.cs index 04dcecbb..333a8f22 100644 --- a/Unity.Entities.Editor/Hierarchy/Model/HierarchyFilter.cs +++ b/Unity.Entities.Editor/Hierarchy/Model/HierarchyFilter.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using Unity.Collections; using Unity.Profiling; +using UnityEditor; using UnityEditor.SceneManagement; namespace Unity.Entities.Editor @@ -30,6 +31,8 @@ class HierarchyFilter : IDisposable readonly int m_EntityIndex; + readonly NodeKind m_Kind; + /// /// Gets the current active . /// @@ -46,19 +49,48 @@ class HierarchyFilter : IDisposable public bool IsValid => m_QueryResult.IsValid; /// - /// Returns the name of the invalid component type encountered during filtering. + /// Returns why the filter is invalid. /// - public string ErrorComponentType => m_QueryResult.ErrorComponentType; + public string ErrorMsg { get; set; } + + public string ErrorCategory { get; set; } + + static readonly string k_ComponentTypeNotFoundTitle = L10n.Tr("Type not found"); + static readonly string k_ComponentTypeNotFoundContent = L10n.Tr("\"{0}\" is not a component type"); internal HierarchyFilter(HierarchySearch hierarchySearch, string searchString, ICollection tokens, Allocator allocator) + : this(hierarchySearch, HierarchyQueryBuilder.BuildQuery(searchString), tokens, -1, NodeKind.None, allocator, true) + { + } + + internal HierarchyFilter(HierarchySearch hierarchySearch, HierarchyQueryBuilder.Result result, ICollection tokens, int entityIndex, NodeKind nodeKind, Allocator allocator, bool parseTokensForFilter) { m_HierarchySearch = hierarchySearch; - m_QueryResult = HierarchyQueryBuilder.BuildQuery(searchString); + m_QueryResult = result; + + if (!string.IsNullOrEmpty(m_QueryResult.ErrorComponentType)) + { + ErrorCategory = k_ComponentTypeNotFoundTitle; + ErrorMsg = string.Format(k_ComponentTypeNotFoundContent, m_QueryResult.ErrorComponentType); + } + m_Tokens = new NativeList(tokens?.Count ?? 1, allocator); - m_EntityIndex = -1; + m_EntityIndex = entityIndex; + m_Kind = nodeKind; - if (null != tokens) - PreProcessTokens(tokens, m_Tokens, ref m_EntityIndex); + if (tokens != null) + { + if (parseTokensForFilter) + { + PreProcessTokens(tokens, m_Tokens, ref m_EntityIndex, ref m_Kind); + } + else + { + foreach (var t in tokens) + m_Tokens.Add(ProcessToken(t)); + ProcessSearchValueTokens(m_Tokens); + } + } } public void Dispose() @@ -66,7 +98,7 @@ public void Dispose() m_Tokens.Dispose(); } - static void PreProcessTokens(IEnumerable inputTokens, NativeList processedTokens, ref int targetIndex) + void PreProcessTokens(IEnumerable inputTokens, NativeList processedTokens, ref int targetIndex, ref NodeKind kind) { using var marker = k_PreProcessTokensMarker.Auto(); @@ -79,8 +111,7 @@ static void PreProcessTokens(IEnumerable inputTokens, NativeList inputTokens, NativeList 2) @@ -103,16 +147,26 @@ static void PreProcessTokens(IEnumerable inputTokens, NativeList FixedString64Bytes.UTF8MaxLengthInBytes ? token.Substring(0, FixedString64Bytes.UTF8MaxLengthInBytes) : token; - - // Use the fixed string to-lower variant to be compatible with filtering. - processedTokens.Add(FixedStringUtility.ToLower(truncatedToken)); + processedTokens.Add(ProcessToken(token)); } } if (processedTokens.Length <= 1) return; + ProcessSearchValueTokens(processedTokens); + } + + static FixedString64Bytes ProcessToken(string token) + { + var truncatedToken = token.Length > FixedString64Bytes.UTF8MaxLengthInBytes ? token.Substring(0, FixedString64Bytes.UTF8MaxLengthInBytes) : token; + + // Use the fixed string to-lower variant to be compatible with filtering. + return FixedStringUtility.ToLower(truncatedToken); + } + + void ProcessSearchValueTokens(NativeList processedTokens) + { // Factor out repeats by removing short strings contained in longer strings // e.g.: [GameObject, Object, a, z] -> [GameObject, z] for (var i = processedTokens.Length - 1; i >= 0; --i) @@ -163,6 +217,8 @@ public NativeBitArray Apply(HierarchyNodeStore.Immutable nodes, Allocator alloca m_HierarchySearch.ApplyEntityQueryFilter(nodes, m_QueryResult.EntityQueryDesc, mask); m_HierarchySearch.ApplyNameFilter(nodes, m_Tokens, mask); m_HierarchySearch.ApplyIncludeSubSceneFilter(nodes, mask); + if (m_Kind != NodeKind.None) + m_HierarchySearch.ApplyNodeKindFilter(nodes, m_Kind, mask); if (PrefabStageUtility.GetCurrentPrefabStage() != null) m_HierarchySearch.ApplyPrefabStageFilter(nodes, mask); diff --git a/Unity.Entities.Editor/Hierarchy/Model/HierarchyNodeHandle.cs b/Unity.Entities.Editor/Hierarchy/Model/HierarchyNodeHandle.cs index 2aa65bb9..75a36f5e 100644 --- a/Unity.Entities.Editor/Hierarchy/Model/HierarchyNodeHandle.cs +++ b/Unity.Entities.Editor/Hierarchy/Model/HierarchyNodeHandle.cs @@ -16,8 +16,7 @@ enum NodeKind Entity = 1 << 1, GameObject = 1 << 2, Scene = 1 << 3, - SubScene = 1 << 4, - Custom = 1 << 5 + SubScene = 1 << 4 } /// diff --git a/Unity.Entities.Editor/Hierarchy/Model/HierarchyUpdater.cs b/Unity.Entities.Editor/Hierarchy/Model/HierarchyUpdater.cs index 388ec632..0cec4982 100644 --- a/Unity.Entities.Editor/Hierarchy/Model/HierarchyUpdater.cs +++ b/Unity.Entities.Editor/Hierarchy/Model/HierarchyUpdater.cs @@ -187,9 +187,16 @@ public void SetWorld(World world) m_SubSceneChangeTracker.SetWorld(m_World); m_HierarchyEntityChangeTracker?.Dispose(); - m_HierarchyEntityChangeTracker = null != world - ? new HierarchyEntityChangeTracker(m_World, m_Allocator) { OperationMode = m_HierarchyEntityChangeTrackerOperationMode } - : null; + + if (world == null) + m_HierarchyEntityChangeTracker = null; + else if (!TypeManager.IsInitialized) + { + Debug.LogError($"{nameof(TypeManager)} has not been initialized properly"); + m_HierarchyEntityChangeTracker = null; + } + else + m_HierarchyEntityChangeTracker = new HierarchyEntityChangeTracker(m_World, m_Allocator) { OperationMode = m_HierarchyEntityChangeTrackerOperationMode }; Reset(); } diff --git a/Unity.Entities.Editor/Hierarchy/Utility/ComponentTypeAutoComplete.cs b/Unity.Entities.Editor/Hierarchy/Utility/ComponentTypeAutoComplete.cs index f4ae39db..5c5b86c4 100644 --- a/Unity.Entities.Editor/Hierarchy/Utility/ComponentTypeAutoComplete.cs +++ b/Unity.Entities.Editor/Hierarchy/Utility/ComponentTypeAutoComplete.cs @@ -6,15 +6,21 @@ namespace Unity.Entities.Editor class ComponentTypeAutoComplete : AutoComplete.IAutoCompleteBehavior { static ComponentTypeAutoComplete s_Instance; + static ComponentTypeAutoComplete s_EntityQueryInstance; static string k_FilterToken = $"\\b([{Constants.ComponentSearch.TokenCaseInsensitive}]{Constants.ComponentSearch.Op})(?(\\S)*)$"; - static readonly Regex k_Regex = new Regex(k_FilterToken, RegexOptions.Compiled | RegexOptions.Singleline | RegexOptions.ExplicitCapture); + static string k_EntityQueryFilterToken = $"\\b([{Constants.ComponentSearch.TokenCaseInsensitive}]|{Constants.ComponentSearch.Any}|{Constants.ComponentSearch.All}|{Constants.ComponentSearch.None}){Constants.ComponentSearch.Op}(?(\\S)*)$"; + public static ComponentTypeAutoComplete Instance => s_Instance ?? (s_Instance = new ComponentTypeAutoComplete(false)); + public static ComponentTypeAutoComplete EntityQueryInstance => s_EntityQueryInstance ?? (s_EntityQueryInstance = new ComponentTypeAutoComplete(true)); - public static ComponentTypeAutoComplete Instance => s_Instance ?? (s_Instance = new ComponentTypeAutoComplete()); + Regex m_Regex = new Regex(k_FilterToken, RegexOptions.Compiled | RegexOptions.Singleline | RegexOptions.ExplicitCapture); static ComponentTypeAutoComplete() => ComponentTypesTrie.Initialize(); - ComponentTypeAutoComplete() { } + ComponentTypeAutoComplete(bool supportsEntityQuery) + { + m_Regex = new Regex(supportsEntityQuery ? k_EntityQueryFilterToken : k_FilterToken, RegexOptions.Compiled | RegexOptions.Singleline | RegexOptions.ExplicitCapture | RegexOptions.IgnoreCase); + } public bool ShouldStartAutoCompletion(string input, int caretPosition) { @@ -23,7 +29,7 @@ public bool ShouldStartAutoCompletion(string input, int caretPosition) public string GetToken(string input, int caretPosition) { - var match = k_Regex.Match(input, 0, caretPosition); + var match = m_Regex.Match(input, 0, caretPosition); if (!match.Success) return string.Empty; var type = match.Groups["componentType"]; @@ -38,7 +44,7 @@ public IEnumerable GetCompletionItems(string input, int caretPosition) public (string newInput, int caretPosition) OnCompletion(string completedToken, string input, int caretPosition) { - var match = k_Regex.Match(input, 0, caretPosition); + var match = m_Regex.Match(input, 0, caretPosition); var componentType = match.Groups["componentType"]; var indexOfNextSpace = input.IndexOf(' ', componentType.Index); diff --git a/Unity.Entities.Editor/Inspector/Aspects/TransformAspectInspector.cs b/Unity.Entities.Editor/Inspector/Aspects/TransformAspectInspector.cs deleted file mode 100644 index f84d0e00..00000000 --- a/Unity.Entities.Editor/Inspector/Aspects/TransformAspectInspector.cs +++ /dev/null @@ -1,130 +0,0 @@ -using JetBrains.Annotations; -using Unity.Entities.UI; -using Unity.Transforms; -using UnityEditor; -using UnityEngine.UIElements; - -namespace Unity.Entities.Editor -{ -#if ENABLE_TRANSFORM_V1 - [UsedImplicitly] - class TransformAspectInspector : PropertyInspector> - { - const string k_EditorPrefsTransformAspectInspectorBase = "com.unity.dots.editor.transform_aspect_inspector_"; - - public override VisualElement Build() - { - var root = new VisualElement(); - var toolHandleLocalName = EditorGUIUtility.isProSkin ? "d_ToolHandleLocal" : "ToolHandleLocal"; - var toolHandleGlobalName = EditorGUIUtility.isProSkin ? "d_ToolHandleGlobal" : "ToolHandleGlobal"; - - var localPosition = new Vector3Field {label = "Local Position", bindingPath = "LocalPosition"}.WithIconPrefix(toolHandleLocalName); - var globalPosition = new Vector3Field {label = "Global Position", bindingPath = "Position"}.WithIconPrefix(toolHandleGlobalName); - - var localRotation = new Vector3Field {label = "Local Rotation", bindingPath = "LocalRotation"}.WithIconPrefix(toolHandleLocalName); - var globalRotation = new Vector3Field {label = "Global Rotation", bindingPath = "Rotation"}.WithIconPrefix(toolHandleGlobalName); - - root.Add(new ContextualElement - ( - k_EditorPrefsTransformAspectInspectorBase + "position", - new ContextualElement.Item - { - Element = localPosition, - ContextMenuLabel = localPosition.label - }, - new ContextualElement.Item - { - Element = globalPosition, - ContextMenuLabel = globalPosition.label - } - )); - - root.Add(new ContextualElement - ( - k_EditorPrefsTransformAspectInspectorBase + "rotation", - new ContextualElement.Item - { - Element = localRotation, - ContextMenuLabel = localRotation.label - }, - new ContextualElement.Item - { - Element = globalRotation, - ContextMenuLabel = globalRotation.label - } - )); - - return root; - } - } -#else - [UsedImplicitly] - class TransformAspectInspector : PropertyInspector> - { - const string k_EditorPrefsTransformAspectInspectorBase = "com.unity.dots.editor.transform_aspect_inspector_"; - - public override VisualElement Build() - { - var root = new VisualElement(); - var toolHandleLocalName = EditorGUIUtility.isProSkin ? "d_ToolHandleLocal" : "ToolHandleLocal"; - var toolHandleGlobalName = EditorGUIUtility.isProSkin ? "d_ToolHandleGlobal" : "ToolHandleGlobal"; - - var localPosition = new Vector3Field {label = "Local Position", bindingPath = "LocalPosition"}.WithIconPrefix(toolHandleLocalName); - var globalPosition = new Vector3Field {label = "Global Position", bindingPath = "Position"}.WithIconPrefix(toolHandleGlobalName); - - var localRotation = new Vector3Field {label = "Local Rotation", bindingPath = "LocalRotation"}.WithIconPrefix(toolHandleLocalName); - var globalRotation = new Vector3Field {label = "Global Rotation", bindingPath = "Rotation"}.WithIconPrefix(toolHandleGlobalName); - - var localUniformScale = new FloatField {label = "Local Uniform Scale", bindingPath = "LocalTransformData.Scale"}.WithIconPrefix(toolHandleLocalName); - var globalUniformScale = new FloatField {label = "Global Uniform Scale", bindingPath = "WorldTransformData.Scale"}.WithIconPrefix(toolHandleGlobalName); - - root.Add(new ContextualElement - ( - k_EditorPrefsTransformAspectInspectorBase + "position", - new ContextualElement.Item - { - Element = localPosition, - ContextMenuLabel = localPosition.label - }, - new ContextualElement.Item - { - Element = globalPosition, - ContextMenuLabel = globalPosition.label - } - )); - - root.Add(new ContextualElement - ( - k_EditorPrefsTransformAspectInspectorBase + "rotation", - new ContextualElement.Item - { - Element = localRotation, - ContextMenuLabel = localRotation.label - }, - new ContextualElement.Item - { - Element = globalRotation, - ContextMenuLabel = globalRotation.label - } - )); - - root.Add(new ContextualElement - ( - k_EditorPrefsTransformAspectInspectorBase + "scale", - new ContextualElement.Item - { - Element = localUniformScale, - ContextMenuLabel = localUniformScale.label - }, - new ContextualElement.Item - { - Element = globalUniformScale, - ContextMenuLabel = globalUniformScale.label - } - )); - - return root; - } - } -#endif -} diff --git a/Unity.Entities.Editor/Inspector/CustomInspectors/TransformComponentInspectors.cs b/Unity.Entities.Editor/Inspector/CustomInspectors/TransformComponentInspectors.cs index b5665870..72b54a4b 100644 --- a/Unity.Entities.Editor/Inspector/CustomInspectors/TransformComponentInspectors.cs +++ b/Unity.Entities.Editor/Inspector/CustomInspectors/TransformComponentInspectors.cs @@ -35,42 +35,11 @@ public override VisualElement Build() } } - #if !ENABLE_TRANSFORM_V1 - #else - [UsedImplicitly] - sealed class LocalToParentInspector : Float4x4ValueInspector - { - } -#endif - [UsedImplicitly] sealed class LocalToWorldInspector : Float4x4ValueInspector { } -#if !ENABLE_TRANSFORM_V1 -#else - [UsedImplicitly] - sealed class CompositeRotationInspector : Float4x4ValueInspector - { - } - - [UsedImplicitly] - sealed class CompositeScaleInspector : Float4x4ValueInspector - { - } - - [UsedImplicitly] - sealed class ParentScaleInverseInspector : Float4x4ValueInspector - { - } - - [UsedImplicitly] - sealed class WorldToLocalInspector : Float4x4ValueInspector - { - } -#endif - abstract class Float3ValueInspector : PropertyInspector { static Float3ValueInspector() @@ -87,99 +56,6 @@ public override VisualElement Build() } } -#if !ENABLE_TRANSFORM_V1 -#else - [UsedImplicitly] - sealed class TranslationInspector : Float3ValueInspector - { - } - - [UsedImplicitly] - sealed class NonUniformScaleInspector : Float3ValueInspector - { - } - - [UsedImplicitly] - sealed class RotationEulerXYZInspector : Float4x4ValueInspector - { - } - - [UsedImplicitly] - sealed class RotationEulerXZYInspector : Float4x4ValueInspector - { - } - - [UsedImplicitly] - sealed class RotationEulerYXZInspector : Float4x4ValueInspector - { - } - - [UsedImplicitly] - sealed class RotationEulerYZXInspector : Float4x4ValueInspector - { - } - - [UsedImplicitly] - sealed class RotationEulerZXYInspector : Float4x4ValueInspector - { - } - - [UsedImplicitly] - sealed class RotationEulerZYXInspector : Float4x4ValueInspector - { - } - - [UsedImplicitly] - sealed class PostRotationEulerXYZInspector : Float4x4ValueInspector - { - } - - [UsedImplicitly] - sealed class PostRotationEulerXZYInspector : Float4x4ValueInspector - { - } - - [UsedImplicitly] - sealed class PostRotationEulerYXZInspector : Float4x4ValueInspector - { - } - - [UsedImplicitly] - sealed class PostRotationEulerYZXInspector : Float4x4ValueInspector - { - } - - [UsedImplicitly] - sealed class PostRotationEulerZXYInspector : Float4x4ValueInspector - { - } - - [UsedImplicitly] - sealed class PostRotationEulerZYXInspector : Float4x4ValueInspector - { - } - - [UsedImplicitly] - sealed class RotationPivotInspector : Float4x4ValueInspector - { - } - - [UsedImplicitly] - sealed class RotationPivotTranslationInspector : Float4x4ValueInspector - { - } - - [UsedImplicitly] - sealed class ScalePivotInspector : Float4x4ValueInspector - { - } - - [UsedImplicitly] - sealed class ScalePivotTranslationInspector : Float4x4ValueInspector - { - } -#endif - abstract class QuaternionValueInspector : PropertyInspector { static QuaternionValueInspector() @@ -196,19 +72,6 @@ public override VisualElement Build() } } -#if !ENABLE_TRANSFORM_V1 -#else - [UsedImplicitly] - sealed class RotationInspector : QuaternionValueInspector - { - } - - [UsedImplicitly] - sealed class PostRotationInspector : QuaternionValueInspector - { - } -#endif - abstract class DefaultValueInspector : PropertyInspector { public override VisualElement Build() diff --git a/Unity.Entities.Editor/Inspector/Utilities/InspectorDataModeSupport.cs b/Unity.Entities.Editor/Inspector/Utilities/InspectorDataModeSupport.cs index 41205f3b..5cc39c12 100644 --- a/Unity.Entities.Editor/Inspector/Utilities/InspectorDataModeSupport.cs +++ b/Unity.Entities.Editor/Inspector/Utilities/InspectorDataModeSupport.cs @@ -230,7 +230,7 @@ when EditorApplication.isPlaying && => typeof(InvalidEntityEditor), DataMode.Runtime - when context is null && (Selection.activeGameObject == null || Selection.activeGameObject.scene.isSubScene) + when context is null && (Selection.activeGameObject != null && Selection.activeGameObject.scene.isSubScene) => typeof(InvalidEntityEditor), // Anything else: show the default inspector. diff --git a/Unity.Entities.Editor/Inspector/Utilities/InspectorUtility.cs b/Unity.Entities.Editor/Inspector/Utilities/InspectorUtility.cs index 7f9c7397..86693a73 100644 --- a/Unity.Entities.Editor/Inspector/Utilities/InspectorUtility.cs +++ b/Unity.Entities.Editor/Inspector/Utilities/InspectorUtility.cs @@ -16,7 +16,6 @@ static class InspectorUtility static InspectorUtility() { - k_AspectIconsDict.Add("TransformAspect", EditorGUIUtility.IconContent(EditorGUIUtility.isProSkin ? "d_Transform Icon" : "Transform Icon").image as Texture2D); k_AspectIconsDict.Add("RigidbodyAspect", EditorGUIUtility.IconContent(EditorGUIUtility.isProSkin ? "d_Rigidbody Icon" : "Rigidbody Icon").image as Texture2D); k_AspectIconsDict.Add("CameraAspect", EditorGUIUtility.IconContent(EditorGUIUtility.isProSkin ? "d_Camera Icon" : "Camera Icon").image as Texture2D); k_AspectIconsDict.Add("RendererAspect", EditorGUIUtility.IconContent(EditorGUIUtility.isProSkin ? "d_MeshRenderer Icon" : "MeshRenderer Icon").image as Texture2D); diff --git a/Unity.Entities.Editor/Inspector/Visitors/Aspects/EntityInspectorAspectsComparer.cs b/Unity.Entities.Editor/Inspector/Visitors/Aspects/EntityInspectorAspectsComparer.cs index 2da23ddc..bc388628 100644 --- a/Unity.Entities.Editor/Inspector/Visitors/Aspects/EntityInspectorAspectsComparer.cs +++ b/Unity.Entities.Editor/Inspector/Visitors/Aspects/EntityInspectorAspectsComparer.cs @@ -8,7 +8,7 @@ class EntityInspectorAspectsComparer: IComparer { static readonly string[] k_TopAspects = { - nameof(TransformAspect) + // No built-in aspects currently need to be sorted at the top of the inspector }; public static EntityInspectorAspectsComparer Instance { get; } = new(); diff --git a/Unity.Entities.Editor/Inspector/Visitors/Components/EntityInspectorComponentsComparer.cs b/Unity.Entities.Editor/Inspector/Visitors/Components/EntityInspectorComponentsComparer.cs index 3acd1178..8696c81a 100644 --- a/Unity.Entities.Editor/Inspector/Visitors/Components/EntityInspectorComponentsComparer.cs +++ b/Unity.Entities.Editor/Inspector/Visitors/Components/EntityInspectorComponentsComparer.cs @@ -6,25 +6,13 @@ namespace Unity.Entities.Editor { class EntityInspectorComponentsComparer: IComparer { -#if !ENABLE_TRANSFORM_V1 static readonly string[] k_TopComponents = { - nameof(WorldTransform), nameof(LocalTransform), - nameof(PostTransformScale), + nameof(PostTransformMatrix), nameof(LocalToWorld), nameof(Parent), }; -#else - static readonly string[] k_TopComponents = - { - nameof(Translation), - nameof(Rotation), - nameof(NonUniformScale), - nameof(LocalToWorld), - nameof(LocalToParent) - }; -#endif public static EntityInspectorComponentsComparer Instance { get; } = new(); diff --git a/Unity.Entities.Editor/Journaling/EntitiesJournalingWindow+ReadOnlyRecordViewList.cs b/Unity.Entities.Editor/Journaling/EntitiesJournalingWindow+ReadOnlyRecordViewList.cs index b225e3c7..ed4585db 100644 --- a/Unity.Entities.Editor/Journaling/EntitiesJournalingWindow+ReadOnlyRecordViewList.cs +++ b/Unity.Entities.Editor/Journaling/EntitiesJournalingWindow+ReadOnlyRecordViewList.cs @@ -8,7 +8,7 @@ namespace Unity.Entities.Editor { partial class EntitiesJournalingWindow { - class ReadOnlyRecordViewList : IList, IList + internal class ReadOnlyRecordViewList : IList, IList { readonly RecordViewArray m_Records; diff --git a/Unity.Entities.Editor/Journaling/EntitiesJournalingWindow.cs b/Unity.Entities.Editor/Journaling/EntitiesJournalingWindow.cs index cece2159..7979e029 100644 --- a/Unity.Entities.Editor/Journaling/EntitiesJournalingWindow.cs +++ b/Unity.Entities.Editor/Journaling/EntitiesJournalingWindow.cs @@ -13,7 +13,7 @@ namespace Unity.Entities.Editor { partial class EntitiesJournalingWindow : DOTSEditorWindow { - class ComponentDataValuesVisitor : IPropertyBagVisitor, IPropertyVisitor + internal class ComponentDataValuesVisitor : IPropertyBagVisitor, IPropertyVisitor { readonly HashSet m_Values = new HashSet(); @@ -57,21 +57,21 @@ void IPropertyVisitor.Visit(Property pro } } - const string k_RecordIndexToken = "ri"; - const string k_RecordTypeToken = "rt"; - const string k_FrameIndexToken = "f"; - const string k_WorldNameToken = "w"; - const string k_WorldIndexToken = "wi"; - const string k_SystemToken = "s"; - const string k_ExecutingSystemToken = "es"; - const string k_OriginSystemToken = "os"; - const string k_EntityNameToken = "e"; - const string k_EntityIndexToken = "ei"; - const string k_EntityCountToken = "ec"; - const string k_ComponentTypeNameToken = "c"; - const string k_ComponentTypeIndexToken = "ci"; - const string k_ComponentCountToken = "cc"; - const string k_ComponentDataValueToken = "v"; + internal const string k_RecordIndexToken = "ri"; + internal const string k_RecordTypeToken = "rt"; + internal const string k_FrameIndexToken = "f"; + internal const string k_WorldNameToken = "w"; + internal const string k_WorldIndexToken = "wi"; + internal const string k_SystemToken = "s"; + internal const string k_ExecutingSystemToken = "es"; + internal const string k_OriginSystemToken = "os"; + internal const string k_EntityNameToken = "e"; + internal const string k_EntityIndexToken = "ei"; + internal const string k_EntityCountToken = "ec"; + internal const string k_ComponentTypeNameToken = "c"; + internal const string k_ComponentTypeIndexToken = "ci"; + internal const string k_ComponentCountToken = "cc"; + internal const string k_ComponentDataValueToken = "v"; const string k_RecordIndexColumn = "record-index"; const string k_RecordTypeColumn = "record-type"; @@ -208,6 +208,9 @@ void OnEnable() searchElement.AddSearchFilterCallbackWithPopupItem(k_ComponentCountToken, r => r.ComponentTypes.Length, s_ComponentCount, "", defaultSearchOptions, "="); searchElement.AddSearchFilterCallbackWithPopupItem>(k_ComponentDataValueToken, GetComponentDataText, s_ComponentDataValue); + var jump = toolbar.Q