Priyesh Wani
4 年前
当前提交
b2cc609e
共有 1834 个文件被更改,包括 11004 次插入 和 21 次删除
-
4TestProjects/PerceptionURP/Packages/manifest.json
-
66TestProjects/PerceptionURP/Packages/packages-lock.json
-
612TestProjects/PerceptionURP/Packages/com.unity.entities@0.8.0-preview.8/CHANGELOG.md
-
7TestProjects/PerceptionURP/Packages/com.unity.entities@0.8.0-preview.8/CHANGELOG.md.meta
-
8TestProjects/PerceptionURP/Packages/com.unity.entities@0.8.0-preview.8/DocCodeSamples.Tests.meta
-
211TestProjects/PerceptionURP/Packages/com.unity.entities@0.8.0-preview.8/DocCodeSamples.Tests/ChunkComponentExamples.cs
-
11TestProjects/PerceptionURP/Packages/com.unity.entities@0.8.0-preview.8/DocCodeSamples.Tests/ChunkComponentExamples.cs.meta
-
347TestProjects/PerceptionURP/Packages/com.unity.entities@0.8.0-preview.8/DocCodeSamples.Tests/ChunkIterationJob.cs
-
11TestProjects/PerceptionURP/Packages/com.unity.entities@0.8.0-preview.8/DocCodeSamples.Tests/ChunkIterationJob.cs.meta
-
2TestProjects/PerceptionURP/Packages/com.unity.entities@0.8.0-preview.8/DocCodeSamples.Tests/DisableAutoCreation.cs
-
11TestProjects/PerceptionURP/Packages/com.unity.entities@0.8.0-preview.8/DocCodeSamples.Tests/DisableAutoCreation.cs.meta
-
30TestProjects/PerceptionURP/Packages/com.unity.entities@0.8.0-preview.8/DocCodeSamples.Tests/DocCodeSamples.asmdef
-
7TestProjects/PerceptionURP/Packages/com.unity.entities@0.8.0-preview.8/DocCodeSamples.Tests/DocCodeSamples.asmdef.meta
-
534TestProjects/PerceptionURP/Packages/com.unity.entities@0.8.0-preview.8/DocCodeSamples.Tests/DynamicBufferExamples.cs
-
11TestProjects/PerceptionURP/Packages/com.unity.entities@0.8.0-preview.8/DocCodeSamples.Tests/DynamicBufferExamples.cs.meta
-
54TestProjects/PerceptionURP/Packages/com.unity.entities@0.8.0-preview.8/DocCodeSamples.Tests/EntityCommandBuffers.cs
-
11TestProjects/PerceptionURP/Packages/com.unity.entities@0.8.0-preview.8/DocCodeSamples.Tests/EntityCommandBuffers.cs.meta
-
59TestProjects/PerceptionURP/Packages/com.unity.entities@0.8.0-preview.8/DocCodeSamples.Tests/EntityQueryExamples.cs
-
11TestProjects/PerceptionURP/Packages/com.unity.entities@0.8.0-preview.8/DocCodeSamples.Tests/EntityQueryExamples.cs.meta
-
460TestProjects/PerceptionURP/Packages/com.unity.entities@0.8.0-preview.8/DocCodeSamples.Tests/LambdaJobExamples.cs
-
11TestProjects/PerceptionURP/Packages/com.unity.entities@0.8.0-preview.8/DocCodeSamples.Tests/LambdaJobExamples.cs.meta
-
250TestProjects/PerceptionURP/Packages/com.unity.entities@0.8.0-preview.8/DocCodeSamples.Tests/LookupDataExamples.cs
-
11TestProjects/PerceptionURP/Packages/com.unity.entities@0.8.0-preview.8/DocCodeSamples.Tests/LookupDataExamples.cs.meta
-
124TestProjects/PerceptionURP/Packages/com.unity.entities@0.8.0-preview.8/DocCodeSamples.Tests/StatefulSystem.cs
-
11TestProjects/PerceptionURP/Packages/com.unity.entities@0.8.0-preview.8/DocCodeSamples.Tests/StatefulSystem.cs.meta
-
222TestProjects/PerceptionURP/Packages/com.unity.entities@0.8.0-preview.8/DocCodeSamples.Tests/SystemBaseExamples.cs
-
11TestProjects/PerceptionURP/Packages/com.unity.entities@0.8.0-preview.8/DocCodeSamples.Tests/SystemBaseExamples.cs.meta
-
28TestProjects/PerceptionURP/Packages/com.unity.entities@0.8.0-preview.8/Documentation~/TableOfContents.md
-
35TestProjects/PerceptionURP/Packages/com.unity.entities@0.8.0-preview.8/Documentation~/chunk_iteration.md
-
122TestProjects/PerceptionURP/Packages/com.unity.entities@0.8.0-preview.8/Documentation~/chunk_iteration_job.md
-
46TestProjects/PerceptionURP/Packages/com.unity.entities@0.8.0-preview.8/Documentation~/component_data.md
-
382TestProjects/PerceptionURP/Packages/com.unity.entities@0.8.0-preview.8/Documentation~/custom_job_types.md
-
125TestProjects/PerceptionURP/Packages/com.unity.entities@0.8.0-preview.8/Documentation~/dynamic_buffers.md
-
7TestProjects/PerceptionURP/Packages/com.unity.entities@0.8.0-preview.8/Documentation~/ecs_building_projects.md
-
7TestProjects/PerceptionURP/Packages/com.unity.entities@0.8.0-preview.8/Documentation~/ecs_burst.md
-
156TestProjects/PerceptionURP/Packages/com.unity.entities@0.8.0-preview.8/Documentation~/ecs_chunk_component.md
-
21TestProjects/PerceptionURP/Packages/com.unity.entities@0.8.0-preview.8/Documentation~/ecs_components.md
-
71TestProjects/PerceptionURP/Packages/com.unity.entities@0.8.0-preview.8/Documentation~/ecs_core.md
-
61TestProjects/PerceptionURP/Packages/com.unity.entities@0.8.0-preview.8/Documentation~/ecs_creating_systems.md
-
59TestProjects/PerceptionURP/Packages/com.unity.entities@0.8.0-preview.8/Documentation~/ecs_debugging.md
-
50TestProjects/PerceptionURP/Packages/com.unity.entities@0.8.0-preview.8/Documentation~/ecs_entities.md
-
148TestProjects/PerceptionURP/Packages/com.unity.entities@0.8.0-preview.8/Documentation~/ecs_entities_foreach.md
-
234TestProjects/PerceptionURP/Packages/com.unity.entities@0.8.0-preview.8/Documentation~/ecs_entity_query.md
-
8TestProjects/PerceptionURP/Packages/com.unity.entities@0.8.0-preview.8/Documentation~/ecs_gameplay.md
-
36TestProjects/PerceptionURP/Packages/com.unity.entities@0.8.0-preview.8/Documentation~/ecs_job_dependencies.md
-
24TestProjects/PerceptionURP/Packages/com.unity.entities@0.8.0-preview.8/Documentation~/ecs_job_extensions.md
-
40TestProjects/PerceptionURP/Packages/com.unity.entities@0.8.0-preview.8/Documentation~/ecs_job_overview.md
-
61TestProjects/PerceptionURP/Packages/com.unity.entities@0.8.0-preview.8/Documentation~/ecs_job_withcode.md
-
67TestProjects/PerceptionURP/Packages/com.unity.entities@0.8.0-preview.8/Documentation~/ecs_lookup_data.md
-
7TestProjects/PerceptionURP/Packages/com.unity.entities@0.8.0-preview.8/Documentation~/ecs_profiling.md
-
58TestProjects/PerceptionURP/Packages/com.unity.entities@0.8.0-preview.8/Documentation~/ecs_systems.md
-
7TestProjects/PerceptionURP/Packages/com.unity.entities@0.8.0-preview.8/Documentation~/ecs_testing.md
-
208TestProjects/PerceptionURP/Packages/com.unity.entities@0.8.0-preview.8/Documentation~/ecs_write_groups.md
-
11TestProjects/PerceptionURP/Packages/com.unity.entities@0.8.0-preview.8/Documentation~/entities_job_foreach.md
-
22TestProjects/PerceptionURP/Packages/com.unity.entities@0.8.0-preview.8/Documentation~/entity_command_buffer.md
-
11TestProjects/PerceptionURP/Packages/com.unity.entities@0.8.0-preview.8/Documentation~/entity_iteration_foreach.md
-
7TestProjects/PerceptionURP/Packages/com.unity.entities@0.8.0-preview.8/Documentation~/entity_iteration_job.md
-
53TestProjects/PerceptionURP/Packages/com.unity.entities@0.8.0-preview.8/Documentation~/entity_manager.md
-
11TestProjects/PerceptionURP/Packages/com.unity.entities@0.8.0-preview.8/Documentation~/exclusive_entity_transaction.md
-
46TestProjects/PerceptionURP/Packages/com.unity.entities@0.8.0-preview.8/Documentation~/filter.yml
-
11TestProjects/PerceptionURP/Packages/com.unity.entities@0.8.0-preview.8/Documentation~/gp_ai.md
-
8TestProjects/PerceptionURP/Packages/com.unity.entities@0.8.0-preview.8/Documentation~/gp_animation.md
-
8TestProjects/PerceptionURP/Packages/com.unity.entities@0.8.0-preview.8/Documentation~/gp_assets.md
-
9TestProjects/PerceptionURP/Packages/com.unity.entities@0.8.0-preview.8/Documentation~/gp_audio.md
-
8TestProjects/PerceptionURP/Packages/com.unity.entities@0.8.0-preview.8/Documentation~/gp_ecs_interop.md
-
8TestProjects/PerceptionURP/Packages/com.unity.entities@0.8.0-preview.8/Documentation~/gp_input.md
-
8TestProjects/PerceptionURP/Packages/com.unity.entities@0.8.0-preview.8/Documentation~/gp_multiplayer.md
-
81TestProjects/PerceptionURP/Packages/com.unity.entities@0.8.0-preview.8/Documentation~/gp_overview.md
-
10TestProjects/PerceptionURP/Packages/com.unity.entities@0.8.0-preview.8/Documentation~/gp_physics.md
-
8TestProjects/PerceptionURP/Packages/com.unity.entities@0.8.0-preview.8/Documentation~/gp_prefabs.md
-
9TestProjects/PerceptionURP/Packages/com.unity.entities@0.8.0-preview.8/Documentation~/gp_rendering.md
-
8TestProjects/PerceptionURP/Packages/com.unity.entities@0.8.0-preview.8/Documentation~/gp_scenes.md
-
8TestProjects/PerceptionURP/Packages/com.unity.entities@0.8.0-preview.8/Documentation~/gp_text_handling.md
-
8TestProjects/PerceptionURP/Packages/com.unity.entities@0.8.0-preview.8/Documentation~/gp_ui.md
-
117TestProjects/PerceptionURP/Packages/com.unity.entities@0.8.0-preview.8/Documentation~/images/ArchetypeChunkDiagram.png
-
137TestProjects/PerceptionURP/Packages/com.unity.entities@0.8.0-preview.8/Documentation~/images/ArchetypeDiagram.png
-
51TestProjects/PerceptionURP/Packages/com.unity.entities@0.8.0-preview.8/Documentation~/images/BasicSystem.png
-
234TestProjects/PerceptionURP/Packages/com.unity.entities@0.8.0-preview.8/Documentation~/images/ConversionAndSubscenes.png
-
158TestProjects/PerceptionURP/Packages/com.unity.entities@0.8.0-preview.8/Documentation~/images/CreatingGameplay.png
-
206TestProjects/PerceptionURP/Packages/com.unity.entities@0.8.0-preview.8/Documentation~/images/ECSBlockDiagram.png
-
506TestProjects/PerceptionURP/Packages/com.unity.entities@0.8.0-preview.8/Documentation~/images/EntitiesSplash.png
-
35TestProjects/PerceptionURP/Packages/com.unity.entities@0.8.0-preview.8/Documentation~/images/SlideArrows.png
-
111TestProjects/PerceptionURP/Packages/com.unity.entities@0.8.0-preview.8/Documentation~/images/SystemEventOrder.png
-
813TestProjects/PerceptionURP/Packages/com.unity.entities@0.8.0-preview.8/Documentation~/images/WhatIsECSinfographic0000.png
-
643TestProjects/PerceptionURP/Packages/com.unity.entities@0.8.0-preview.8/Documentation~/images/WhatIsECSinfographic0001.png
-
843TestProjects/PerceptionURP/Packages/com.unity.entities@0.8.0-preview.8/Documentation~/images/WhatIsECSinfographic0002.png
-
803TestProjects/PerceptionURP/Packages/com.unity.entities@0.8.0-preview.8/Documentation~/images/WhatIsECSinfographic0003.png
-
635TestProjects/PerceptionURP/Packages/com.unity.entities@0.8.0-preview.8/Documentation~/images/WhatIsECSinfographic0004.png
-
32TestProjects/PerceptionURP/Packages/com.unity.entities@0.8.0-preview.8/Documentation~/images/infographic.js
-
4TestProjects/PerceptionURP/Packages/com.unity.entities@0.8.0-preview.8/Documentation~/images/pixel.png
-
40TestProjects/PerceptionURP/Packages/com.unity.entities@0.8.0-preview.8/Documentation~/images/sec1-1.png
-
50TestProjects/PerceptionURP/Packages/com.unity.entities@0.8.0-preview.8/Documentation~/images/sec1-2.png
-
79TestProjects/PerceptionURP/Packages/com.unity.entities@0.8.0-preview.8/Documentation~/images/sec2-1.png
|
|||
# Change log |
|||
|
|||
## [0.8.0] - 2020-03-13 |
|||
|
|||
### Added |
|||
|
|||
* Added missing dynamic component version API: `ArchetypeChunk.GetComponentVersion(ArchetypeChunkComponentTypeDynamic)` |
|||
* Added missing dynamic component has API: `ArchetypeChunk.Has(ArchetypeChunkComponentTypeDynamic)` |
|||
* `EntityArchetype` didn't expose whether it was Prefab or not. Added bool `EntityArchetype.Prefab`. This is needed for meta entity queries, because meta entity queries don't avoid Prefabs. |
|||
* Added Build Configurations and Build Pipelines for Linux |
|||
* LiveLink now gives an error if a LiveLink player attempts to connect to the wrong Editor, and advises the user on how to correct this. |
|||
|
|||
### Changed |
|||
|
|||
* Optimized `ArchetypeChunkComponentTypeDynamic` memory layout. 48->40 bytes. |
|||
* LiveLink: Editor no longer freezes when sending LiveLink assets to a LiveLinked player. |
|||
* 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 |
|||
|
|||
* Types that implement `IJobForEach` interfaces have been deprecated. Use `IJobChunk` and `Entities.ForEach` for these jobs. |
|||
|
|||
### Fixed |
|||
|
|||
* Fixed LiveLinking with SubScene Sections indices that were not contiguous (0, 1, 2..). Now works with whatever index you use. |
|||
* Fixed warning when live converting disabled GameObjects. |
|||
* Allow usage of `Entities.WithReadOnly`, `Entities.WithDeallocateOnJobCompletion`, `Entities.WithNativeDisableContainerSafetyRestriction`, and `Entities.WithNativeDisableParallelForRestriction` on types that contain valid NativeContainers. |
|||
|
|||
|
|||
## [0.7.0] - 2020-03-03 |
|||
|
|||
### Added |
|||
|
|||
* Added `HasComponent`/`GetComponent`/`SetComponent` methods that streamline access to components through entities when using the `SystemBase` class. These methods call through to `EntityManager` methods when in OnUpdate code and codegen access through `ComponentDataFromEntity` when inside of `Entities.ForEach`. |
|||
* `SubScene` support for hybrid components, allowing Editor LiveLink (Player LiveLink is not supported yet). |
|||
* Added `GameObjectConversionSettings.Systems` to allow users to explicitly specify what systems should be included in the conversion |
|||
|
|||
### Changed |
|||
|
|||
* Fixed an issue where shared component filtering could be broken until the shared component data is manually set/added when using a deserialized world. |
|||
* Users can control the update behaviour of a `ComponentSystemGroup` via an update callback. See the documentation for `ComponentSystemGroup.UpdateCallback`, as well as examples in `FixedRateUtils`. |
|||
* `IDisposable` and `ICloneable` are now supported on managed components. |
|||
* `World` now exposes a `Flags` field allowing the editor to improve how it filters world to show in various tooling windows. |
|||
* `World.Systems` is now a read only collection that does not allocate managed memory while being iterated over. |
|||
* Updated package `com.unity.platforms` to version `0.2.1-preview.4`. |
|||
|
|||
### Deprecated |
|||
|
|||
* Property `World.AllWorlds` is now replaced by `World.All` which now returns a read only collection that does not allocate managed memory while being iterated over. |
|||
|
|||
### Removed |
|||
|
|||
* Removed expired API `implicit operator GameObjectConversionSettings(World)` |
|||
* Removed expired API `implicit operator GameObjectConversionSettings(Hash128)` |
|||
* Removed expired API `implicit operator GameObjectConversionSettings(UnityEditor.GUID)` |
|||
* Removed expired API `TimeData.deltaTime` |
|||
* Removed expired API `TimeData.time` |
|||
* Removed expired API `TimeData.timeSinceLevelLoad` |
|||
* Removed expired API `TimeData.captureFramerate` |
|||
* Removed expired API `TimeData.fixedTime` |
|||
* Removed expired API `TimeData.frameCount` |
|||
* Removed expired API `TimeData.timeScale` |
|||
* Removed expired API `TimeData.unscaledTime` |
|||
* Removed expired API `TimeData.captureDeltaTime` |
|||
* Removed expired API `TimeData.fixedUnscaledTime` |
|||
* Removed expired API `TimeData.maximumDeltaTime` |
|||
* Removed expired API `TimeData.realtimeSinceStartup` |
|||
* Removed expired API `TimeData.renderedFrameCount` |
|||
* Removed expired API `TimeData.smoothDeltaTime` |
|||
* Removed expired API `TimeData.unscaledDeltaTime` |
|||
* Removed expired API `TimeData.fixedUnscaledDeltaTime` |
|||
* Removed expired API `TimeData.maximumParticleDeltaTime` |
|||
* Removed expired API `TimeData.inFixedTimeStep` |
|||
* Removed expired API `ComponentSystemBase.OnCreateManager()` |
|||
* Removed expired API `ComponentSystemBase.OnDestroyManager()` |
|||
* Removed expired API `ConverterVersionAttribute(int)` |
|||
|
|||
### Fixed |
|||
|
|||
* Non-moving children in transform hierarchies no longer trigger transform system updates. |
|||
* Fixed a bug where dynamic buffer components would sometimes leak during live link. |
|||
* Fixed crash that would occur if only method in a module was generated from a `[GenerateAuthoringComponent]` type. |
|||
* `Entities.ForEach` now throws a correct error message when it is used with a delegate stored in a variable, field or returned from a method. |
|||
* Fix IL2CPP compilation error with `Entities.ForEach` that uses a tag component and `WithStructuralChanges`. |
|||
* `Entities.ForEach` now marshals lambda parameters for DOTS Runtime when the lambda is burst compiled and has collection checks enabled. Previously using `EntityCommandBuffer` or other types with a `DisposeSentinel` field as part of your lambda function (when using DOTS Runtime) may have resulted in memory access violation. |
|||
* `.Run()` on `IJobChunk` may have dereferenced null or invalid chunk on filtered queries. |
|||
|
|||
|
|||
### Security |
|||
|
|||
* Throw correct error message if accessing `ToComponentDataArrayAsync` `CopyFromComponentDataArray` or `CopyFromComponentDataArrayAsync` from an unrelated query. |
|||
|
|||
|
|||
|
|||
## [0.6.0] - 2020-02-17 |
|||
|
|||
### Added |
|||
|
|||
* The `[GenerateAuthoringComponent]` attribute is now allowed on structs implementing `IBufferElementData`. An authoring component is automatically generated to support adding a `DynamicBuffer` of the type implementing `IBufferElementData` to an entity. |
|||
* Added new `SystemBase` base class for component systems. This new way of defining component systems manages dependencies for the user (manual dependency management is still possible by accessing the `SystemBase.Dependency` field directly). |
|||
* New `ScheduleParallel` methods in `IJobChunk` and `Entities.ForEach` (in `SystemBase`) to make parallel scheduling of jobs explicit. `ScheduleSingle` in `IJobChunk` indicates scheduling work to be done in a non-parallel manner. |
|||
* New editor workflow to quickly and easily build LiveLink player using the `BuildConfiguration` API. |
|||
* Adds Live Link support for `GameObject` scenes. |
|||
* The `SceneSystem` API now also loads `GameObject` scenes via `LoadSceneAsync` API. |
|||
* Added new build component for LiveLink settings in `Unity.Scenes.Editor` to control how initial scenes are handled (LiveLink all, embed all, embed first). |
|||
* Users can now inspect post-procssed IL code inside Unity Editor: `DOTS` -> `DOTS Compiler` -> `Open Inspector` |
|||
* `GetAssignableComponentTypes()` can now be called with or without a List<Type> argument to collect the data. When omitted, the list will be allocated, which is the same behavior as before. |
|||
|
|||
### Changed |
|||
|
|||
* The package `com.unity.build` has been merged into the package `com.unity.platforms`. As such, removed the dependency on `com.unity.build@0.1.0-preview` and replaced it with `com.unity.platforms@0.2.1-preview.1`. Please read the changelog of `com.unity.platforms` for more details. |
|||
* Managed components are now stored in a way that will generate less GC allocations when entities change archetype. |
|||
* Moved `Unity.Entities.ICustomBootstrap` from Unity.Entities.Hybrid to Unity.Entities. |
|||
* `World.Dispose()` now completes all reader/writer jobs on the `World`'s `EntityManager` before releasing any resources, to avoid use-after-free errors. |
|||
* Fix `AssemblyResolveException` when loading a project with dependent packages that are using Burst in static initializers or `InitializeOnLoad`. |
|||
* `.sceneWithBuildSettings` files that are stored in Assets/SceneDependencyCache are no longer rebuilt constantly. Because they are required for SubScene behaviour to work in the editor, if these are deleted they are recreated by OnValidate of the SubScene in the edited Scene. They should also be recreated on domain reload (restarting unity, entering/exiting playmode, etc). |
|||
* `EntityQuery.cs`: Overloads of `CreateArchetypeChunkArray`, `ToComponentDataArray`, `ToEntityArray`, and `CopyFromComponentDataArray` that return a JobHandle (allowing the work to be done asynchronously) have been renamed to add `Async` to the title (i.e. `ToComponentDataArrayAsync`). The old overloads have been deprecated and an API Updater clause has been added. |
|||
* `Entities.WithName` now only accepts names that use letters, digits, and underscores (not starting with a digit, no two consecutive underscores) |
|||
* Updated package `com.unity.properties` to version `0.10.4-preview`. |
|||
* Updated package `com.unity.serialization` to version `0.6.4-preview`. |
|||
* The entity debugger now remembers whether chunk info panel is visible |
|||
* The entity debugger now displays the full name for nested types in the system list |
|||
* The entity debugger now sorts previously used filter components to the top of the filter GUI |
|||
* Bumped burst version to include the new features and fixes including: |
|||
* Fix an issue with function pointers being corrupted after a domain reload that could lead to hard crashes. |
|||
* Fix potential deadlock between Burst and the AssetDatabase if burst is being used when building the database. |
|||
|
|||
### Deprecated |
|||
|
|||
* Method `GetBuildSettingsComponent` on class `GameObjectConversionSystem` has been renamed to `GetBuildConfigurationComponent`. |
|||
* Method `TryGetBuildSettingsComponent` on class `GameObjectConversionSystem` has been renamed to `TryGetBuildConfigurationComponent`. |
|||
* Member `BuildSettings` on class `GameObjectConversionSettings` has been renamed to `BuildConfiguration`. |
|||
* Member `BuildSettingsGUID` on class `SceneSystem` has been renamed to `BuildConfigurationGUID`. |
|||
|
|||
### Removed |
|||
|
|||
* Removed expired API `SceneSectionData.SharedComponentCount` |
|||
* Removed expired API `struct SceneData` |
|||
* Removed expired API `SubScene._SceneEntities` |
|||
* Removed expired API `World.Active` |
|||
|
|||
### Fixed |
|||
|
|||
* Ability to open and close SubScenes from the scene hierarchy window (Without having to move cursor to inspector window). |
|||
* Ability to create a new empty Sub Scene without first creating a game object. |
|||
* Improve performance of SubScene loading and change tracking in the editor. |
|||
* Fixed regression where `GetSingleton` would create a new query on every call. |
|||
* Fixed SubScenes trying to load an already loaded AssetBundle when loaded multiple times on the same player, but with different Worlds. |
|||
* Make it clear that SubScenes in Prefabs are not supported. |
|||
* Lambda job codegen tests now fail if the error message does not contain the expected contents. |
|||
* Improved performance of setting up the world required for game object conversion |
|||
* The `chunkIndex` parameter passed to `IJobChunk.Execute()` now has the correct value. |
|||
* Fixed an error which caused entities with `ISystemStateSharedComponentData` components to not be cleaned up correctly. |
|||
* Managed components containing `Entity` fields will now correctly serialize. |
|||
* Fixed issue where `BlobAssetVerifier` will throw error if it can't resolve a type. |
|||
* Exposed the Managed Component extensions for `EntityQuery`. |
|||
* `Entities.ForEach` now identifies when `this` of the enclosing system is captured due to calling an extension method on it when compilation fails since the lambda was emitted as a member function |
|||
* `Entities.ForEach` now reports when a field of the outer system is captured and used by reference when compilation fails since the lambda was emitted as a member function |
|||
* `Entities.ForEach` does not erronously point to calling static functions as the source of the error when compilation fails since the lambda was emitted as a member function |
|||
* Debugging inside of `Entities.ForEach` with Visual Studio 2017/2019 (some debugging features will need an upcoming update of the com.unity.ide.visualstudio package). |
|||
* `EntityQuery.ToComponentArray<T>` with `T` deriving from `UnityEngine.Component` now correctly collects all data in a chunk |
|||
* Fixed an issue with `ComponentSystemBase.GetEntityQuery` and `EntityManager.CreateEntityQuery` calls made with `EntityQueryDesc` not respecting read-only permissions. |
|||
|
|||
|
|||
## [0.5.1] - 2020-01-28 |
|||
|
|||
### Changed |
|||
|
|||
* Constructor-related exceptions thrown during `World.CreateSystem` will now included the inner exception details. |
|||
* `DefaultWorldInitialization.GetAllSystems` now returns `IReadOnlyList<Type>` instead of `List<Type>` |
|||
* `DefaultWorldInitialization.AddSystemsToRootLevelSystemGroups` now takes `IEnumerable<Type>` instead of `List<Type>` |
|||
|
|||
### Fixed |
|||
|
|||
* Fixed an issue where `BlobAssetReference` types was not guaranteed to be 8-byte aligned on all platforms which could result in failing to read Blob data in components correctly on 32-bit platforms. |
|||
* Fixed issue in `MinMaxAABB.Equals()` comparing `Min` to itself rather than `other`. |
|||
* `Entities.ForEach` now properly treats `in` parameters of `DynamicBuffer` type as read-only |
|||
* Fixed potential crash caused by a leaked job after an exception is thrown during a call to `IJobChunk.Schedule`. |
|||
* Fixed regression in `ComponentSystemBase.GetSingleton()` where a new query would be created every timee the function is called. |
|||
|
|||
|
|||
## [0.5.0] - 2020-01-16 |
|||
|
|||
### Added |
|||
|
|||
* Added AndroidHybrid.buildpipeline with RunStepAndroid |
|||
|
|||
### Changed |
|||
|
|||
* `Entities.WithReadOnly`, `Entities.WithNativeDisableParallelForRestriction`, `Entities.WithDeallocateOnJobCompletion`, `Entities.WithNativeDisableSafetyRestriction` and `Entities.WithNativeDisableUnsafePtrRestriction` now check their argument types for the proper attributes (`[NativeContainer]`, `[NativeContainerSupportsDeallocateOnJobCompletion]`) at compile time and throw an error when used on a field of a user defined type. |
|||
* Log entries emitted during subscene conversion without a context object are now displayed in the subscene inspector instead of discarded |
|||
|
|||
### Deprecated |
|||
|
|||
* Adding removal dates to the API that have been deprecated but did not have the date set. |
|||
* `BlobAssetReference<T>`: `Release()` was deprecated, use `Dispose()` instead. |
|||
|
|||
### Removed |
|||
|
|||
* Adding removal dates to the API that have been deprecated but did not have the date set. |
|||
* `BlobAssetReference<T>`: `Release()` was deprecated, use `Dispose()` instead. |
|||
* `EntityQuery.cs`: Removed expired API `CalculateLength()`, `SetFilter()` and `SetFilterChanged()`. |
|||
|
|||
### Fixed |
|||
* Fixed an issue where trying to perform EntityRemapping on Managed Components could throw if a component field was null. |
|||
* `EntityManager.MoveEntitiesFrom` with query was not bumping shared component versions, order versions or dirty versions correctly. Now it does. |
|||
* Fixed that adding a Sub Scene component from the Add Components dropdown was not reflected in the Hierarchy. |
|||
* Fixed so that Undo/Redo of changes to SceneAsset objectfield in the Sub Scene Inspector is reflected in the Hierarchy. |
|||
* Make it clear when Sub Scene duplicates are present: shown in Hierarchy and by showing a warning box in the Inspector. |
|||
* Support Undo for 'Create Sub Scene From Selection' context menu item. |
|||
* Better file name error handling for the 'New Sub Scene From Selection' context menu item. |
|||
* Keep sibling order for new Sub Scene when created using 'New Sub Scene From Selection' (prevents the new Sub Scene from ending as the last sibling). |
|||
* Handle if selection contains part of a Prefab instance when creating Sub Scene from Selection. |
|||
* Fix dangling loaded Sub Scenes not visualized in the Hierarchy when removing Scene Asset reference in Sub Scene component. |
|||
* Fixed an issue with invalid IL generated by `Entities.ForEach` when structs are captured as locals from two different scopes and their fields are accessed. |
|||
* Make it clear in the Hierarchy and Sub Scene Inspector that nesting Sub Scenes is not yet supported. |
|||
* Fixed an issue with `BinaryWriter` where serializing a `System.String[]` with a single element would throw an exception. |
|||
* Fixed an issue with `ComponentSystem.GetEntityQuery` and `JobComponentSystem.GetEntityQuery` which caused improper caching of queries when using "None" or "Any" fields. |
|||
|
|||
|
|||
## [0.4.0] - 2019-12-16 |
|||
|
|||
**This version requires Unity 2019.3.0f1+** |
|||
|
|||
### New Features |
|||
|
|||
* Two new methods added to the public API: |
|||
* `void EntityCommandBuffer.AddComponent<T>(EntityQuery entityQuery)` |
|||
* `void EntityCommandBuffer.RemoveComponent<T>(EntityQuery entityQuery)` |
|||
* BlobArray, BlobString & BlobPtr are not allowed to be copied by value since they carry offset pointers that aree relative to the location of the memory. This could easily result in programming mistakes. The compiler now prevents incorrect usage by enforcing any type attributed with [MayOnlyLiveInBlobStorage] to never be copied by value. |
|||
|
|||
### Changes |
|||
|
|||
* Deprecates `TypeManager.CreateTypeIndexForComponent` and it's other component type variants. Types can be dynamically added (in Editor builds) by instead passing the new unregistered types to `TypeManager.AddNewComponentTypes` instead. |
|||
* `RequireForUpdate(EntityQuery)` and `RequireSingletonForUpdate` on a system with `[AlwaysUpdate]` will now throw an exception instead of being ignored. |
|||
* ChangeVersionUtility.IncrementGlobalSystemVersion & ChangeVersionUtility.InitialGlobalSystemVersion is now internal. They were accidentally public previously. |
|||
* Entity inspector now shows entity names and allows to rename the selected entity |
|||
* Improved entity debugger UI |
|||
* Create WorldRenderBounds for prefabs and disabled entities with renderers during conversion, this make instantiation of those entities significantly faster. |
|||
* Reduced stack depth of System.Update / OnUpdate method (So it looks better in debugger) |
|||
* Assert when using EntityQuery from another world |
|||
* Using an EntityQuery created in one world on another world was resulting in memory corruption. We now detect it in the EntityManager API and throw an argument exception |
|||
* Structural changes now go through a bursted codepath and are significantly faster |
|||
* DynamicBuffer.Capacity is now settable |
|||
|
|||
### Fixes |
|||
|
|||
* Remove unnecessary & incorrect warning in DeclareReferencedPrefab when the referenced game object is a scene object |
|||
* GameObjects with ConvertAndInject won't get detached from a non-converted parent (fixes regression) |
|||
* Fixed a crash that could occur when destroying an entity with an empty LinkedEntityGroup. |
|||
* Updated performance package dependency to 1.3.2 which fixes an obsoletion warning |
|||
* The `EntityCommandBuffer` can be replayed repeatedly. |
|||
* Fixed exception in entity binary scene serialization when referencing a null UnityEngine.Object from a shared component |
|||
* Moving scripts between assemblies now triggers asset bundle rebuilds where necessary for live link |
|||
* Fixed LiveLink on Android |
|||
|
|||
|
|||
## [0.3.0] - 2019-12-03 |
|||
|
|||
### New Features |
|||
|
|||
* ENABLE_SIMPLE_SYSTEM_DEPENDENCIES define can now be used to replace the automatic dependency chaining with a much simplified strategy. With ENABLE_SIMPLE_SYSTEM_DEPENDENCIES it simply chains jobs in the order of the systems against previous jobs. Without ENABLE_SIMPLE_SYSTEM_DEPENDENCIES, dependencies are automatically chained based on read / write access of component data of each system. In cases when there game code is forced to very few cores or there are many systems, this can improve performance since it reduces overhead in calculating optimal dependencies. |
|||
* Added `DebuggerTypeProxy` for `MultiListEnumerator<T>` (e.g. this makes the results of `GameObjectConversionSystem.GetEntities` calls readable in the debugger) |
|||
* Two new methods added to the public API: |
|||
* EntityManager.CreateEntity(Archetype type, int count, Allocator allocator); |
|||
* EntityManager.Instantiate(Entity entity, int count, Allocator allocator); |
|||
Both methods return a `NativeArray<Entity>`. |
|||
|
|||
### Changes |
|||
|
|||
Removed the following deprecated API as announced in/before `0.1.1-preview`: |
|||
|
|||
* From GameObjectConversionUtility.cs: `ConvertIncrementalInitialize()` and `ConvertScene()`. |
|||
* From Translation.cs: `struct Position`. |
|||
* From EditorEntityScenes.cs: `WriteEntityScene()`. |
|||
* From GameObjectConversionSystem.cs: `AddReferencedPrefab()`, `AddDependency()`, `AddLinkedEntityGroup()`, `DstWorld`. |
|||
* From DefaultWorld.cs: `class EndPresentationEntityCommandBufferSystem`. |
|||
|
|||
### Fixes |
|||
|
|||
* ConvertAndInject won't destroy the root GameObject anymore (fixes regression introduced in 0.2.0) |
|||
* Fix Android/iOS build when using new build pipeline |
|||
* Provide correct application extension apk, aab or empty for project export when building to Android |
|||
|
|||
|
|||
## [0.2.0] - 2019-11-22 |
|||
|
|||
**This version requires Unity 2019.3 0b11+** |
|||
|
|||
### New Features |
|||
|
|||
* Automatically generate authoring components for IComponentData with IL post-processing. Any component data marked with a GenerateAuthoringComponent attribute will generate the corresponding authoring MonoBehaviour with a Convert method. |
|||
* BuildSettings assets are now used to define a single build recipe asset on disk. This gives full control over the build pipeline in a modular way from C# code. |
|||
* BuildSettings let you attach builtin or your own custom IBuildSettingsComponents for full configurability |
|||
* BuildPipelines let you define the exact IBuildStep that should be run and in which order |
|||
* IBuildStep is either builtin or your own custom build step |
|||
* BuildSettings files can be inherited so you can easily make base build settings with most configuration complete and then do minor adjustments per build setting |
|||
* Right now most player configuration is still in the existing PlayerSettings, our plan is to over time expose all Player Settings via BuildSettings as well to ease configuration of complex projects with many build recipes & artifacts |
|||
* SubScenes are now automatically converted to entity binary files & cached by the asset pipeline. The entity cache files previously present in the project folder should be removed. Conversion systems can use the ConverterVersion attribute to convert to trigger a reconversion if the conversion system has changed behaviour. The conversion happens asynchronously in another process. Thus on first open the subscenes might not show up immediately. |
|||
|
|||
* Live link builds can be built with the new BuildSettings pipeline. |
|||
Open sub scene |
|||
* Closed Entity scenes are built by the asset pipeline and loaded via livelink on demand |
|||
* Opened Entity scenes are send via live entity patcher with patches on a per component / entity basis based on what has changed |
|||
* Assets referenced by entity scenes are transferred via livelink when saving the asset |
|||
* Scenes loaded as game objects are currently not live linked (This is in progress) |
|||
by assigning the LiveLink build pipeline |
|||
|
|||
* `Entities.ForEach` syntax for supplying jobified code in a `JobComponentSystem`'s `OnUpdate` method directly by using a lambda (instead of supplying an additional `IJobForEach`). |
|||
|
|||
* `EntityQueryMask` has been added, which allows for quick confirmation of if an Entity would be returned by an `EntityQuery` without filters via `EntityQueryMask.Matches(Entity entity)`. An EntityQueryMask can be obtained by calling `EntityManager.GetEntityQueryMask(EntityQuery query).` |
|||
* Unity Entities now supports the _Fast Enter playmode_ which can be enabled in the project settings. It is recommended to be turned on for all dots projects. |
|||
* The UnityEngine component `StopConvertToEntity` can be used to interrupt `ConvertToEntity` recursion, and should be preferred over a `ConvertToEntity` set to "convert and inject" for that purpose. |
|||
* _EntityDebugger_ now shows IDs in a separate column, so you can still see them when entities have custom names |
|||
* Entity references in the Entity Inspector have a "Show" button which will select the referenced Entity in the Debugger. |
|||
* An `ArchetypeChunkIterator` can be created by calling `GetArchetypeChunkIterator` on an `EntityQuery`. You may run an `IJobChunk` while bypassing the Jobs API by passing an `ArchetypeChunkIterator` into `IJobChunk.RunWithoutJobs()`. |
|||
* The `[AlwaysSynchronizeSystem]` attribute has been added, which can be applied to a `JobComponentSystem` to force it to synchronize on all of its dependencies before every update. |
|||
* `BoneIndexOffset` has been added, which allows the Animation system to communicate a bone index offset to the Hybrid Renderer. |
|||
* Initial support for using Hybrid Components during conversion, see the HybridComponent sample in the StressTests folder. |
|||
* New `GameObjectConversionSystem.ForkSettings()` that provides a very specialized method for creating a fork of the current conversion settings with a different "EntityGuid namespace", which can be used for nested conversions. This is useful for example in net code where multiple root-level variants of the same authoring object need to be created in the destination world. |
|||
* `EntityManager` `LockChunkOrder` and `UnlockChunkOrder` are deprecated. |
|||
* Entity Scenes can be loaded synchronously (during the next streaming system update) by using `SceneLoadFlags.BlockOnStreamIn` in `SceneSystem.LoadParameters`. |
|||
* `EntityCommandBuffer` can now be played back on an `ExclusiveEntityTransaction` as well as an `EntityManager`. This allows ECB playback to be invoked from a job (though exclusive access to the EntityManager data is still required for the duration of playback). |
|||
|
|||
### Upgrade guide |
|||
* If you are using SubScenes you must use the new BuildSettings assets to make a build & run it. SubScenes are not supported from the File -> BuildSettings... & File -> Build and Run workflows. |
|||
* Entities requires AssetDatabase V2 for certain new features, we do not provide support for AssetDatabase V1. |
|||
|
|||
### Fixes |
|||
|
|||
* Setting `ComponentSystemGroup.Enabled` to `false` now calls `OnStopRunning()` recursively on the group's member systems, not just on the group itself. |
|||
* Updated Properties pacakge to `0.10.3-preview` to fix an exception when showing Physics ComponentData in the inspector as well as fix IL2CPP Ahead of Time linker errors for generic virtual function calls. |
|||
* The `LocalToParentSystem` will no longer write to the `LocalToWorld` component of entities that have a component with the `WriteGroup(typeof(LocalToWorld))`. |
|||
* Entity Debugger styling work better with Pro theme |
|||
* Entity Inspector no longer has runaway indentation |
|||
* Fixed issue where `AddSharedComponentData`, `SetSharedComponentData` did not always update `SharedComponentOrderVersion`. |
|||
* Fixes serialization issue when reading in managed `IComponentData` containing array types and `UnityEngine.Object` references. |
|||
* No exception is thrown when re-adding a tag component with `EntityQuery`. |
|||
* `AddComponent<T>(NativeArray<Entity>)` now reliably throws an `ArgumentException` if any of the target entities are invalid. |
|||
* Fixed an issue where the Entity Debugger would not repaint in edit mode |
|||
* Marking a system as `[UpdateInGroup(typeof(LateSimulationSystemGroup))]` no longer emits a warning about `[DisableAutoCreation]`. |
|||
* Fixed rendering of chunk info to be compatible with HDRP |
|||
* Fixed issue where `ToComponentDataArray` ignored the filter settings on the `EntityQuery` for managed component types. |
|||
|
|||
### Changes |
|||
|
|||
* Deprecated `DynamicBuffer.Reserve` and made `DynamicBuffer.Capacity` a settable property. `DynamicBuffer.Reserve(10)` should now be `DynamicBuffer.Capacity = 10`. |
|||
* Moved `NativeString` code from Unity.Entities to Unity.Collections. |
|||
* Updated dependencies for this package. |
|||
* Significantly improved `Entity` instantiation performance when running in-Editor. |
|||
* Added support for managed `IComponentData` types such as `class MyComponent : IComponentData {}` which allows managed types such as GameObjects or List<>s to be stored in components. Users should use managed components sparingly in production code when possible as these components cannot be used by the Job System or archetype chunk storage and thus will be significantly slower to work with. Refer to the documentation for [component data](Documentation~/component_data.md) for more details on managed component use, implications and prevention. |
|||
* 'SubSceneStreamingSystem' has been renamed to `SceneSectionStreamingSystem` and is now internal |
|||
* Deprecated `_SceneEntities` in `SubScene.cs`. Please use `SceneSystem.LoadAsync` / `Unload` with the respective SceneGUID instead. |
|||
* Updated `com.unity.serialization` to `0.6.3-preview`. |
|||
* The deprecated `GetComponentGroup()` APIs are now `protected` and can only be called from inside a System like their `GetEntityQuery()` successors. |
|||
* All GameObjects with a ConvertToEntity set to "Convert and Destroy" will all be processed within the same conversion pass, this allows cross-referencing. |
|||
* Duplicate component adds are always ignored |
|||
* When adding component to single entity via EntityQuery, entity is moved to matching chunk instead of chunk achetype changing. |
|||
* "Used by Systems" list skips queries with filters |
|||
* Managed `IComponentData` no longer require all fields to be non-null after default construction. |
|||
* `ISharedComponentData` is serialized inline with entity and managed `IComponentData`. If a shared component references a `UnityEngine.Object` type, that type is serialized separately in an "objrefs" resource asset. |
|||
* `EntityManager` calls `EntityComponentStore` via burst delegates for `Add`/`Remove` components. |
|||
* `EntityComponentStore` cannot throw exceptions (since called as burst delegate from main thread.) |
|||
* `bool ICustomBootstrap.Initialize(string defaultWorldName)` has changed API with no deprecated fallback. It now simply gives you a chance to completely replace the default world initialization by returning true. |
|||
* `ICustomBootstrap` & `DefaultWorldInitialization` is now composable like this: |
|||
``` |
|||
class MyCustomBootStrap : ICustomBootstrap |
|||
{ |
|||
public bool Initialize(string defaultWorldName) |
|||
{ |
|||
Debug.Log("Executing bootstrap"); |
|||
var world = new World("Custom world"); |
|||
World.DefaultGameObjectInjectionWorld = world; |
|||
var systems = DefaultWorldInitialization.GetAllSystems(WorldSystemFilterFlags.Default); |
|||
|
|||
DefaultWorldInitialization.AddSystemsToRootLevelSystemGroups(world, systems); |
|||
ScriptBehaviourUpdateOrder.UpdatePlayerLoop(world); |
|||
return true; |
|||
} |
|||
} |
|||
``` |
|||
* `ICustomBootstrap` can now be inherited and only the most deepest subclass bootstrap will be executed. |
|||
* `DefaultWorldInitialization.GetAllSystems` is not affected by bootstrap, it simply returns a list of systems based on the present dlls & attributes. |
|||
* `Time` is now available per-World, and is a property in a `ComponentSystem`. It is updated from the `UnityEngine.Time` during the `InitializationSystemGroup` of each world. If you need access to time in a sytem that runs in the `InitializationSystemGroup`, make sure you schedule your system after `UpdateWorldTimeSystem`. `Time` is also a limited `TimeData` struct; if you need access to any of the extended fields available in `UnityEngine.Time`, access `UnityEngine.Time` explicitly` |
|||
* Systems are no longer removed from a `ComponentSystemGroup` if they throw an exception from their `OnUpdate`. This behavior was more confusing than helpful. |
|||
* Managed IComponentData no longer require implementing the `IEquatable<>` interface and overriding `GetHashCode()`. If either function is provided it will be preferred, otherwise the component will be inspected generically for equality. |
|||
* `EntityGuid` is now constructed from an originating ID, a namespace ID, and a serial, which can be safely extracted from their packed form using new getters. Use `a` and `b` fields when wanting to treat this as an opaque struct (the packing may change again in the future, as there are still unused bits remaining). The a/b constructor has been removed, to avoid any ambiguity. |
|||
* Updated `com.unity.platforms` to `0.1.6-preview`. |
|||
* The default Api Compatibility Level should now be `.NET Standard 2.0` and a warning is generated when the project uses `.NET 4.x`. |
|||
* Added `[UnityEngine.ExecuteAlways]` to `LateSimulationSystemGroup`, so its systems run in Edit Mode. |
|||
|
|||
|
|||
## [0.1.1] - 2019-08-06 |
|||
|
|||
### New Features |
|||
* EntityManager.SetSharedComponentData(EntityQuery query, T componentData) has been added which lets you efficiently swap a shared component data for a whole query. (Without moving any component data) |
|||
|
|||
### Upgrade guide |
|||
|
|||
* The deprecated `OnCreateManager` and `OnDestroyManager` are now compilation errors in the `NET_DOTS` profile as overrides can not be detected reliably (without reflection). |
|||
To avoid the confusion of "why is that not being called", especially when there is no warning issued, this will now be a compilation error. Use `OnCreate` and `OnDestroy` instead. |
|||
|
|||
### Changes |
|||
|
|||
* Updated default version of burst to `1.1.2` |
|||
|
|||
### Fixes |
|||
|
|||
* Fixed potential memory corruption when calling RemoveComponent on a batch of entities that didn't have the component. |
|||
* Fixed an issue where an assert about chunk layout compatibility could be triggered when adding a shared component via EntityManager.AddSharedComponentData<T>(EntityQuery entityQuery, T componentData). |
|||
* Fixed an issue where Entities without any Components would cause UI errors in the Chunk Info view |
|||
* Fixed EntityManager.AddComponent(NativeArray<Entity> entities, ComponentType componentType) so that it handles duplicate entities in the input NativeArray. Duplicate entities are discarded and the component is added only once. Prior to this fix, an assert would be triggered when checking for chunk layout compatibility. |
|||
* Fixed invalid update path for `ComponentType.Create`. Auto-update is available in Unity `2019.3` and was removed for previous versions where it would fail (the fallback implementation will work as before). |
|||
|
|||
|
|||
## [0.1.0] - 2019-07-30 |
|||
|
|||
### New Features |
|||
|
|||
* Added the `#UNITY_DISABLE_AUTOMATIC_SYSTEM_BOOTSTRAP_RUNTIME_WORLD` and `#UNITY_DISABLE_AUTOMATIC_SYSTEM_BOOTSTRAP_EDITOR_WORLD` defines which respectively can be used to disable runtime and editor default world generation. Defining `#UNITY_DISABLE_AUTOMATIC_SYSTEM_BOOTSTRAP` will still disable all default world generation. |
|||
* Allow structural changes to entities (add/remove components, add/destroy entities, etc.) while inside of `ForEach` lambda functions. This negates the need for using `PostUpdateCommands` inside of ForEach. |
|||
* `EntityCommandBuffer` has some additional methods for adding components based on `ComponentType`, or for adding empty components of a certain type (`<T>`) |
|||
* EntityManagerDiffer & EntityManagerPatcher provides highly optimized diffing & patching functionality. It is used in the editor for providing scene conversion live link. |
|||
* Added support for `EntityManager.MoveEntitiesFrom` with managed arrays (Object Components). |
|||
* EntityManager.SetArchetype lets you change an entity to a specific archetype. Removing & adding the necessary components with default values. System state components are not allowed to be removed with this method, it throws an exception to avoid accidental system state removal. (Used in incremental live link conversion it made conversion from 100ms -> 40ms for 1000 changed game objects) |
|||
* Entity Debugger's system list now has a string filter field. This makes it easier to find a system by name when you have a lot of systems. |
|||
* Added IComponentData type `Asset` that will be used by Tiny to convert Editor assets to runtime assets |
|||
* Filled in some `<T>` holes in the overloads we provide in `EntityManager` |
|||
* New `Entities.WithIncludeAll()` that will include in matching all components that are normally ignored by default (currently `Prefab` and `Disabled`) |
|||
* EntityManager.CopyAndReplaceEntitiesFrom has been added it can be used to store & restore a backup of the world for the purposes of general purpose simulation rollback. |
|||
|
|||
### Upgrade guide |
|||
|
|||
* WorldDiff has been removed. It has been replaced by EntityManagerDiff & EntityManagerPatch. |
|||
* Renamed `EntityGroupManager` to `EntityQueryManager`. |
|||
|
|||
### Changes |
|||
|
|||
* EntityArchetype.GetComponentTypes no longer includes Entity in the list of components (it is implied). Behaviour now matches the EntityMangager.GetComponentTypes method. This matches the behavior of the corresponding `EntityManager` function. |
|||
* `EntityCommandBuffer.AddComponent(Entity, ComponentType)` no longer fails if the target entity already has the specified component. |
|||
* DestroyEntity(EntityQuery entityQuery) now uses burst internally. |
|||
|
|||
### Fixes |
|||
|
|||
* Entity Inspector now shows DynamicBuffer elements in pages of five at a time |
|||
* Resources folder renamed to Styles so as not to add editor assets to built player |
|||
* `EntityQueryBuilder.ShallowEquals` (used from `Entities.ForEach`) no longer boxes and allocs GC |
|||
* Improved error message for unnecessary/invalid `UpdateBefore` and `UpdateAfter` |
|||
* Fixed leak in BlobBuilder.CreateBlobAssetReference |
|||
* ComponentSystems are now properly preserved when running the UnityLinker. Note this requires 19.3a10 to work correctly. If your project is not yet using 19.3 you can workaround the issue using the link.xml file. https://docs.unity3d.com/Manual//IL2CPP-BytecodeStripping.html |
|||
* Types that trigger an exception in the TypeManager won't prevent other types from initializing properly. |
|||
|
|||
## [0.0.12-preview.33] - 2019-05-24 |
|||
|
|||
### New Features |
|||
|
|||
* `[DisableAutoCreation]` can now apply to entire assemblies, which will cause all systems contained within to be excluded from automatic system creation. Useful for test assemblies. |
|||
* Added `ComponentSystemGroup.RemoveSystemFromUpdateList()` |
|||
* `EntityCommandBuffer` has commands for adding/removing components, deleting entities and adding shared components based on an EntityQuery and its filter. Not available in the `Concurrent` version |
|||
|
|||
### Changes |
|||
|
|||
* Generic component data types must now be registered in advance. Use [RegisterGenericComponentType] attribute to register each concrete use. e.g. `[assembly: RegisterGenericComponentType(typeof(TypeManagerTests.GenericComponent<int>))]` |
|||
* Attempting to call `Playback()` more than once on the same EntityCommandBuffer will now throw an error. |
|||
* Improved error checking for `[UpdateInGroup]`, `[UpdateBefore]`, and `[UpdateAfter]` attributes |
|||
* TypeManager no longer imposes alignment requirements on components containing pointers. Instead, it now throws an exception if you try to serialize a blittable component containing an unmanaged pointer, which suggests different alternatives. |
|||
|
|||
### Fixes |
|||
|
|||
* Fixed regression where accessing and destroying a blob asset in a burst job caused an exception |
|||
* Fixed bug where entities with manually specified `CompositeScale` were not updated by `TRSLocalToWorldSystem`. |
|||
* Error message when passing in invalid parameters to CreateSystem() is improved. |
|||
* Fixed bug where an exception due to aggressive pointer restrictions could leave the `TypeManager` in an invalid state |
|||
* SceneBoundingVolume is now generated seperately for each subsection |
|||
* SceneBoundingVolume no longer throws exceptions in conversion flow |
|||
* Fixed regression where calling AddComponent(NativeArray<Entity> entities, ComponentType componentType) could cause a crash. |
|||
* Fixed bug causing error message to appear in Inspector header when `ConvertToEntity` component was added to a disabled GameObject. |
|||
|
|||
## [0.0.12-preview.32] - 2019-05-16 |
|||
|
|||
### New Features |
|||
|
|||
* Added BlobBuilder which is a new API to build Blob Assets that does not require preallocating one contiguous block of memory. The BlobAllocator is now marked obsolete. |
|||
* Added versions of `IJobForEach` that support `DynamicBuffer`s |
|||
* Due to C# language constraints, these overloads needed different names. The format for these overloads follows the following structure: |
|||
* All job names begin with either `IJobForEach` or `IJobForEachEntity` |
|||
* All jobs names are then followed by an underscore `_` and a combination of letter corresponding to the parameter types of the job |
|||
* `B` - `IBufferElementData` |
|||
* `C` - `IComponentData` |
|||
* `E` - `Entity` (`IJobForEachWithEntity` only) |
|||
* All suffixes for `WithEntity` jobs begin with `E` |
|||
* All data types in a suffix are in alphabetical order |
|||
* Here is the complete list of overloads: |
|||
* `IJobForEach_C`, `IJobForEach_CC`, `IJobForEach_CCC`, `IJobForEach_CCCC`, `IJobForEach_CCCCC`, `IJobForEach_CCCCCC` |
|||
* `IJobForEach_B`, `IJobForEach_BB`, `IJobForEach_BBB`, `IJobForEach_BBBB`, `IJobForEach_BBBBB`, `IJobForEach_BBBBBB` |
|||
* `IJobForEach_BC`, `IJobForEach_BCC`, `IJobForEach_BCCC`, `IJobForEach_BCCCC`, `IJobForEach_BCCCCC`, `IJobForEach_BBC`, `IJobForEach_BBCC`, `IJobForEach_BBCCC`, `IJobForEach_BBCCCC`, `IJobForEach_BBBC`, `IJobForEach_BBBCC`, `IJobForEach_BBBCCC`, `IJobForEach_BBBCCC`, `IJobForEach_BBBBC`, `IJobForEach_BBBBCC`, `IJobForEach_BBBBBC` |
|||
* `IJobForEachWithEntity_EB`, `IJobForEachWithEntity_EBB`, `IJobForEachWithEntity_EBBB`, `IJobForEachWithEntity_EBBBB`, `IJobForEachWithEntity_EBBBBB`, `IJobForEachWithEntity_EBBBBBB` |
|||
* `IJobForEachWithEntity_EC`, `IJobForEachWithEntity_ECC`, `IJobForEachWithEntity_ECCC`, `IJobForEachWithEntity_ECCCC`, `IJobForEachWithEntity_ECCCCC`, `IJobForEachWithEntity_ECCCCCC` |
|||
* `IJobForEachWithEntity_BC`, `IJobForEachWithEntity_BCC`, `IJobForEachWithEntity_BCCC`, `IJobForEachWithEntity_BCCCC`, `IJobForEachWithEntity_BCCCCC`, `IJobForEachWithEntity_BBC`, `IJobForEachWithEntity_BBCC`, `IJobForEachWithEntity_BBCCC`, `IJobForEachWithEntity_BBCCCC`, `IJobForEachWithEntity_BBBC`, `IJobForEachWithEntity_BBBCC`, `IJobForEachWithEntity_BBBCCC`, `IJobForEachWithEntity_BBBCCC`, `IJobForEachWithEntity_BBBBC`, `IJobForEachWithEntity_BBBBCC`, `IJobForEachWithEntity_BBBBBC` |
|||
* Note that you can still use `IJobForEach` and `IJobForEachWithEntity` as before if you're using only `IComponentData`. |
|||
* EntityManager.SetEnabled API automatically enables & disables an entity or set of entities. If LinkedEntityGroup is present the whole group is enabled / disabled. Inactive game objects automatically get a LinkedEntityGroup added so that EntityManager.SetEnabled works as expected out of the box. |
|||
* Add `WithAnyReadOnly` and `WithAllReadyOnly` methods to EntityQueryBuilder to specify queries that filter on components with access type ReadOnly. |
|||
* No longer throw when the same type is in a WithAll and ForEach delegate param for ForEach queries. |
|||
* `DynamicBuffer` CopyFrom method now supports another DynamicBuffer as a parameter. |
|||
* Fixed cases that would not be handled correctly by the api updater. |
|||
|
|||
### Upgrade guide |
|||
|
|||
* Usages of BlobAllocator will need to be changed to use BlobBuilder instead. The API is similar but Allocate now returns the data that can be populated: |
|||
|
|||
```csharp |
|||
ref var root = ref builder.ConstructRoot<MyData>(); |
|||
var floatArray = builder.Allocate(3, ref root.floatArray); |
|||
floatArray[0] = 0; // root.floatArray[0] can not be used and will throw on access |
|||
``` |
|||
|
|||
* ISharedComponentData with managed fields must implement IEquatable and GetHashCode |
|||
* IComponentData and ISharedComponentData implementing IEquatable must also override GetHashCode |
|||
|
|||
### Fixes |
|||
|
|||
* Comparisons of managed objects (e.g. in shared components) now work as expected |
|||
* Prefabs referencing other prefabs are now supported in game object entity conversion process |
|||
* Fixed a regression where ComponentDataProxy was not working correctly on Prefabs due to a ordering issue. |
|||
* Exposed GameObjectConversionDeclarePrefabsGroup for declaring prefab references. (Must happen before any conversion systems run) |
|||
* Inactive game objects are automatically converted to be Disabled entities |
|||
* Disabled components are ignored during conversion process. Behaviour.Enabled has no direct mapping in ECS. It is recommended to Disable whole entities instead |
|||
* Warnings are now issues when asking for a GetPrimaryEntity that is not a game object that is part of the converted group. HasPrimaryEntity can be used to check if the game object is part of the converted group in case that is necessary. |
|||
* Fixed a race condition in `EntityCommandBuffer.AddBuffer()` and `EntityCommandBuffer.SetBuffer()` |
|||
|
|||
## [0.0.12-preview.31] - 2019-05-01 |
|||
|
|||
### New Features |
|||
|
|||
### Upgrade guide |
|||
|
|||
* Serialized entities file format version has changed, Sub Scenes entity caches will require rebuilding. |
|||
|
|||
### Changes |
|||
|
|||
* Adding components to entities that already have them is now properly ignored in the cases where no data would be overwritten. That means the inspectable state does not change and thus determinism can still be guaranteed. |
|||
* Restored backwards compatibility for `ForEach` API directly on `ComponentSystem` to ease people upgrading to the latest Unity.Entities package on top of Megacity. |
|||
* Rebuilding the entity cache files for sub scenes will now properly request checkout from source control if required. |
|||
|
|||
### Fixes |
|||
|
|||
* `IJobForEach` will only create new entity queries when scheduled, and won't rely on injection anymore. This avoids the creation of useless queries when explicit ones are used to schedule those jobs. Those useless queries could cause systems to keep updating even though the actual queries were empty. |
|||
* APIs changed in the previous version now have better obsolete stubs and upgrade paths. All obsolete APIs requiring manual code changes will now soft warn and continue to work, instead of erroring at compile time. These respective APIs will be removed in a future release after that date. |
|||
* LODGroup conversion now handles renderers being present in a LOD Group in multipe LOD levels correctly |
|||
* Fixed potential memory leak when disposing an EntityCommandBuffer after certain types of playback errors |
|||
* Fixed an issue where chunk utilization histograms weren't properly clipped in EntityDebugger |
|||
* Fixed an issue where tag components were incorrectly shown as subtractive in EntityDebugger |
|||
* ComponentSystem.ShouldRunSystem() exception message now more accurately reports the most likely reason for the error when the system does not exist. |
|||
|
|||
### Known Issues |
|||
|
|||
* It might happen that shared component data with managed references is not compared for equality correctly with certain profiles. |
|||
|
|||
|
|||
## [0.0.12-preview.30] - 2019-04-05 |
|||
|
|||
### New Features |
|||
Script templates have been added to help you create new component types and systems, similar to Unity's built-in template for new MonoBehaviours. Use them via the Assets/Create/ECS menu. |
|||
|
|||
### Upgrade guide |
|||
|
|||
Some APIs have been deprecated in this release: |
|||
|
|||
[API Deprecation FAQ](https://forum.unity.com/threads/api-deprecation-faq-0-0-23.636994/) |
|||
|
|||
** Removed obsolete ComponentSystem.ForEach |
|||
** Removed obsolete [Inject] |
|||
** Removed obsolete ComponentDataArray |
|||
** Removed obsolete SharedComponentDataArray |
|||
** Removed obsolete BufferArray |
|||
** Removed obsolete EntityArray |
|||
** Removed obsolete ComponentGroupArray |
|||
|
|||
####ScriptBehaviourManager removal |
|||
* The ScriptBehaviourManager class has been removed. |
|||
* ComponentSystem and JobComponentSystem remain as system base classes (with a common ComponentSystemBase class) |
|||
* ComponentSystems have overridable methods OnCreateManager and OnDestroyManager. These have been renamed to OnCreate and OnDestroy. |
|||
* This is NOT handled by the obsolete API updater and will need to be done manually. |
|||
* The old OnCreateManager/OnDestroyManager will continue to work temporarily, but will print a warning if a system contains them. |
|||
* World APIs have been updated as follows: |
|||
* CreateManager, GetOrCreateManager, GetExistingManager, DestroyManager, BehaviourManagers have been renamed to CreateSystem, GetOrCreateSystem, GetExistingSystem, DestroySystem, Systems. |
|||
* These should be handled by the obsolete API updater. |
|||
* EntityManager is no longer accessed via GetExistingManager. There is now a property directly on World: World.EntityManager. |
|||
* This is NOT handled by the obsolete API updater and will need to be done manually. |
|||
* Searching and replacing Manager<EntityManager> should locate the right spots. For example, world.GetExistingManager<EntityManager>() should become just world.EntityManager. |
|||
|
|||
#### IJobProcessComponentData renamed to IJobForeach |
|||
This rename unfortunately cannot be handled by the obsolete API updater. |
|||
A global search and replace of IJobProcessComponentData to IJobForEach should be sufficient. |
|||
|
|||
#### ComponentGroup renamed to EntityQuery |
|||
ComponentGroup has been renamed to EntityQuery to better represent what it does. |
|||
All APIs that refer to ComponentGroup have been changed to refer to EntityQuery in their name, e.g. CreateEntityQuery, GetEntityQuery, etc. |
|||
|
|||
#### EntityArchetypeQuery renamed to EntityQueryDesc |
|||
EntityArchetypeQuery has been renamed to EntityQueryDesc |
|||
|
|||
### Changes |
|||
* Minimum required Unity version is now 2019.1.0b9 |
|||
* Adding components to entities that already have them is now properly ignored in the cases where no data would be overwritten. |
|||
* UNITY_CSHARP_TINY is now NET_DOTS to match our other NET_* defines |
|||
|
|||
### Fixes |
|||
* Fixed exception in inspector when Script is missing |
|||
* The presence of chunk components could lead to corruption of the entity remapping during deserialization of SubScene sections. |
|||
* Fix for an issue causing filtering with IJobForEachWithEntity to try to access entities outside of the range of the group it was scheduled with. |
|
|||
fileFormatVersion: 2 |
|||
guid: 77ec343c3667943c189e1bceeff600ce |
|||
TextScriptImporter: |
|||
externalObjects: {} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
|
|||
fileFormatVersion: 2 |
|||
guid: 2e32a04cf2814421a9c3a8b6caf49261 |
|||
folderAsset: yes |
|||
DefaultImporter: |
|||
externalObjects: {} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
|
|||
using Unity.Burst; |
|||
using Unity.Collections; |
|||
using Unity.Entities; |
|||
using Unity.Jobs; |
|||
using Unity.Mathematics; |
|||
using Unity.Transforms; |
|||
using static Unity.Mathematics.math; |
|||
|
|||
namespace Doc.CodeSamples.Tests |
|||
{ |
|||
#region declare-chunk-component
|
|||
public struct ChunkComponentA : IComponentData |
|||
{ |
|||
public float Value; |
|||
} |
|||
#endregion
|
|||
|
|||
#region full-chunk-example
|
|||
public class ChunkComponentExamples : SystemBase |
|||
{ |
|||
private EntityQuery ChunksWithChunkComponentA; |
|||
protected override void OnCreate() |
|||
{ |
|||
EntityQueryDesc ChunksWithComponentADesc = new EntityQueryDesc() |
|||
{ |
|||
All = new ComponentType[] { ComponentType.ChunkComponent<ChunkComponentA>() } |
|||
}; |
|||
ChunksWithChunkComponentA = GetEntityQuery(ChunksWithComponentADesc); |
|||
} |
|||
|
|||
[BurstCompile] |
|||
struct ChunkComponentCheckerJob : IJobChunk |
|||
{ |
|||
public ArchetypeChunkComponentType<ChunkComponentA> ChunkComponentATypeInfo; |
|||
public void Execute(ArchetypeChunk chunk, int chunkIndex, int firstEntityIndex) |
|||
{ |
|||
var compValue = chunk.GetChunkComponentData(ChunkComponentATypeInfo); |
|||
//...
|
|||
var squared = compValue.Value * compValue.Value; |
|||
chunk.SetChunkComponentData(ChunkComponentATypeInfo, |
|||
new ChunkComponentA() { Value = squared }); |
|||
} |
|||
} |
|||
|
|||
protected override void OnUpdate() |
|||
{ |
|||
var job = new ChunkComponentCheckerJob() |
|||
{ |
|||
ChunkComponentATypeInfo = GetArchetypeChunkComponentType<ChunkComponentA>() |
|||
}; |
|||
this.Dependency = job.Schedule(ChunksWithChunkComponentA, this.Dependency); |
|||
} |
|||
} |
|||
#endregion
|
|||
|
|||
#region aabb-chunk-component
|
|||
public struct ChunkAABB : IComponentData |
|||
{ |
|||
public AABB Value; |
|||
} |
|||
|
|||
[UpdateInGroup(typeof(PresentationSystemGroup))] |
|||
[UpdateBefore(typeof(UpdateAABBSystem))] |
|||
public class AddAABBSystem : SystemBase |
|||
{ |
|||
EntityQuery queryWithoutChunkComponent; |
|||
protected override void OnCreate() |
|||
{ |
|||
queryWithoutChunkComponent = GetEntityQuery(new EntityQueryDesc() |
|||
{ |
|||
All = new ComponentType[] { ComponentType.ReadOnly<LocalToWorld>() }, |
|||
None = new ComponentType[] { ComponentType.ChunkComponent<ChunkAABB>() } |
|||
}); |
|||
} |
|||
protected override void OnUpdate() |
|||
{ |
|||
// This is a structural change and a sync point
|
|||
EntityManager.AddChunkComponentData<ChunkAABB>(queryWithoutChunkComponent, new ChunkAABB()); |
|||
} |
|||
} |
|||
|
|||
[UpdateInGroup(typeof(PresentationSystemGroup))] |
|||
public class UpdateAABBSystem : SystemBase |
|||
{ |
|||
EntityQuery queryWithChunkComponent; |
|||
protected override void OnCreate() |
|||
{ |
|||
queryWithChunkComponent = GetEntityQuery(new EntityQueryDesc() |
|||
{ |
|||
All = new ComponentType[] { ComponentType.ReadOnly<LocalToWorld>(), |
|||
ComponentType.ChunkComponent<ChunkAABB>()} |
|||
}); |
|||
} |
|||
[BurstCompile] |
|||
struct AABBJob : IJobChunk |
|||
{ |
|||
[ReadOnly] public ArchetypeChunkComponentType<LocalToWorld> LocalToWorldTypeInfo; |
|||
public ArchetypeChunkComponentType<ChunkAABB> ChunkAABBTypeInfo; |
|||
public uint L2WChangeVersion; |
|||
public void Execute(ArchetypeChunk chunk, int chunkIndex, int firstEntityIndex) |
|||
{ |
|||
bool chunkHasChanges = chunk.DidChange(LocalToWorldTypeInfo, L2WChangeVersion); |
|||
|
|||
if (!chunkHasChanges) |
|||
return; // early out if the chunk transforms haven't changed
|
|||
|
|||
NativeArray<LocalToWorld> transforms = chunk.GetNativeArray<LocalToWorld>(LocalToWorldTypeInfo); |
|||
UnityEngine.Bounds bounds = new UnityEngine.Bounds(); |
|||
bounds.center = transforms[0].Position; |
|||
for (int i = 1; i < transforms.Length; i++) |
|||
{ |
|||
bounds.Encapsulate(transforms[i].Position); |
|||
} |
|||
chunk.SetChunkComponentData(ChunkAABBTypeInfo, new ChunkAABB() { Value = bounds.ToAABB() }); |
|||
} |
|||
} |
|||
|
|||
protected override void OnUpdate() |
|||
{ |
|||
var job = new AABBJob() |
|||
{ |
|||
LocalToWorldTypeInfo = GetArchetypeChunkComponentType<LocalToWorld>(true), |
|||
ChunkAABBTypeInfo = GetArchetypeChunkComponentType<ChunkAABB>(false), |
|||
L2WChangeVersion = this.LastSystemVersion |
|||
}; |
|||
this.Dependency = job.Schedule(queryWithChunkComponent, this.Dependency); |
|||
} |
|||
} |
|||
#endregion
|
|||
|
|||
//snippets
|
|||
public class ChunkComponentSnippets : SystemBase |
|||
{ |
|||
protected override void OnUpdate() |
|||
{ |
|||
throw new System.NotImplementedException(); |
|||
} |
|||
|
|||
private void snippets() |
|||
{ |
|||
#region component-list-chunk-component
|
|||
ComponentType[] compTypes = {ComponentType.ChunkComponent<ChunkComponentA>(), |
|||
ComponentType.ReadOnly<GeneralPurposeComponentA>()}; |
|||
Entity entity = EntityManager.CreateEntity(compTypes); |
|||
#endregion
|
|||
|
|||
#region em-snippet
|
|||
EntityManager.AddChunkComponentData<ChunkComponentA>(entity); |
|||
#endregion
|
|||
|
|||
#region desc-chunk-component
|
|||
EntityQueryDesc ChunksWithoutComponentADesc = new EntityQueryDesc() |
|||
{ |
|||
None = new ComponentType[] { ComponentType.ChunkComponent<ChunkComponentA>() } |
|||
}; |
|||
EntityQuery ChunksWithoutChunkComponentA = GetEntityQuery(ChunksWithoutComponentADesc); |
|||
|
|||
EntityManager.AddChunkComponentData<ChunkComponentA>(ChunksWithoutChunkComponentA, |
|||
new ChunkComponentA() { Value = 4 }); |
|||
#endregion
|
|||
|
|||
#region use-chunk-component
|
|||
EntityQueryDesc ChunksWithChunkComponentADesc = new EntityQueryDesc() |
|||
{ |
|||
All = new ComponentType[] { ComponentType.ChunkComponent<ChunkComponentA>() } |
|||
}; |
|||
#endregion
|
|||
|
|||
#region archetype-chunk-component
|
|||
EntityArchetype ArchetypeWithChunkComponent = EntityManager.CreateArchetype( |
|||
ComponentType.ChunkComponent(typeof(ChunkComponentA)), |
|||
ComponentType.ReadWrite<GeneralPurposeComponentA>()); |
|||
Entity newEntity = EntityManager.CreateEntity(ArchetypeWithChunkComponent); |
|||
#endregion
|
|||
{ |
|||
EntityQuery ChunksWithChunkComponentA = null; |
|||
#region read-chunk-component
|
|||
NativeArray<ArchetypeChunk> chunks = ChunksWithChunkComponentA.CreateArchetypeChunkArray(Allocator.TempJob); |
|||
foreach (var chunk in chunks) |
|||
{ |
|||
var compValue = EntityManager.GetChunkComponentData<ChunkComponentA>(chunk); |
|||
//..
|
|||
} |
|||
chunks.Dispose(); |
|||
#endregion
|
|||
} |
|||
|
|||
#region read-entity-chunk-component
|
|||
if (EntityManager.HasChunkComponent<ChunkComponentA>(entity)) |
|||
{ |
|||
ChunkComponentA chunkComponentValue = EntityManager.GetChunkComponentData<ChunkComponentA>(entity); |
|||
} |
|||
#endregion
|
|||
|
|||
{ |
|||
ArchetypeChunk chunk; |
|||
#region set-chunk-component
|
|||
EntityManager.SetChunkComponentData<ChunkComponentA>(chunk, |
|||
new ChunkComponentA() { Value = 7 }); |
|||
#endregion
|
|||
} |
|||
|
|||
#region set-entity-chunk-component
|
|||
var entityChunk = EntityManager.GetChunk(entity); |
|||
EntityManager.SetChunkComponentData<ChunkComponentA>(entityChunk, |
|||
new ChunkComponentA() { Value = 8 }); |
|||
#endregion
|
|||
|
|||
} |
|||
} |
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: 09a3d71eee8164e83be2e17e6616c9f6 |
|||
MonoImporter: |
|||
externalObjects: {} |
|||
serializedVersion: 2 |
|||
defaultReferences: [] |
|||
executionOrder: 0 |
|||
icon: {instanceID: 0} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
|
|||
using Unity.Burst; |
|||
using Unity.Collections; |
|||
using Unity.Entities; |
|||
using Unity.Jobs; |
|||
using Unity.Mathematics; |
|||
using Unity.Transforms; |
|||
using UnityEngine; |
|||
using NUnit.Framework; |
|||
|
|||
// The files in this namespace are used to compile/test the code samples in the documentation.
|
|||
namespace Doc.CodeSamples.Tests |
|||
{ |
|||
//Snippets used in chunk_iteration_job.md
|
|||
public struct Rotation : IComponentData |
|||
{ |
|||
public quaternion Value; |
|||
} |
|||
|
|||
public struct RotationSpeed : IComponentData |
|||
{ |
|||
public float RadiansPerSecond; |
|||
} |
|||
|
|||
#region rotationspeedsystem
|
|||
public class RotationSpeedSystem : SystemBase |
|||
{ |
|||
private EntityQuery m_Query; |
|||
|
|||
protected override void OnCreate() |
|||
{ |
|||
m_Query = GetEntityQuery(ComponentType.ReadOnly<Rotation>(), |
|||
ComponentType.ReadOnly<RotationSpeed>()); |
|||
//...
|
|||
} |
|||
#endregion
|
|||
|
|||
[BurstCompile] |
|||
struct RotationSpeedJob : IJobChunk |
|||
{ |
|||
public float DeltaTime; |
|||
public ArchetypeChunkComponentType<Rotation> RotationType; |
|||
[ReadOnly] public ArchetypeChunkComponentType<RotationSpeed> RotationSpeedType; |
|||
|
|||
public void Execute(ArchetypeChunk chunk, int chunkIndex, int firstEntityIndex) |
|||
{ |
|||
#region chunkiteration
|
|||
|
|||
var chunkRotations = chunk.GetNativeArray(RotationType); |
|||
var chunkRotationSpeeds = chunk.GetNativeArray(RotationSpeedType); |
|||
for (var i = 0; i < chunk.Count; i++) |
|||
{ |
|||
var rotation = chunkRotations[i]; |
|||
var rotationSpeed = chunkRotationSpeeds[i]; |
|||
|
|||
// Rotate something about its up vector at the speed given by RotationSpeed.
|
|||
chunkRotations[i] = new Rotation |
|||
{ |
|||
Value = math.mul(math.normalize(rotation.Value), |
|||
quaternion.AxisAngle(math.up(), rotationSpeed.RadiansPerSecond * DeltaTime)) |
|||
}; |
|||
} |
|||
|
|||
#endregion
|
|||
} |
|||
} |
|||
|
|||
#region schedulequery
|
|||
|
|||
protected override void OnUpdate() |
|||
{ |
|||
var job = new RotationSpeedJob() |
|||
{ |
|||
RotationType = GetArchetypeChunkComponentType<Rotation>(false), |
|||
RotationSpeedType = GetArchetypeChunkComponentType<RotationSpeed>(true), |
|||
DeltaTime = Time.DeltaTime |
|||
}; |
|||
this.Dependency = job.ScheduleParallel(m_Query, this.Dependency); |
|||
} |
|||
|
|||
#endregion
|
|||
} |
|||
|
|||
|
|||
public class RotationSpeedSystemExample2 : SystemBase |
|||
{ |
|||
private EntityQuery m_Query; |
|||
|
|||
#region oncreate2
|
|||
protected override void OnCreate() |
|||
{ |
|||
var queryDescription = new EntityQueryDesc() |
|||
{ |
|||
None = new ComponentType[] |
|||
{ |
|||
typeof(Static) |
|||
}, |
|||
All = new ComponentType[] |
|||
{ |
|||
ComponentType.ReadWrite<Rotation>(), |
|||
ComponentType.ReadOnly<RotationSpeed>() |
|||
} |
|||
}; |
|||
m_Query = GetEntityQuery(queryDescription); |
|||
} |
|||
#endregion
|
|||
|
|||
#region speedjob
|
|||
|
|||
[BurstCompile] |
|||
struct RotationSpeedJob : IJobChunk |
|||
{ |
|||
public float DeltaTime; |
|||
public ArchetypeChunkComponentType<Rotation> RotationType; |
|||
[ReadOnly] public ArchetypeChunkComponentType<RotationSpeed> RotationSpeedType; |
|||
|
|||
public void Execute(ArchetypeChunk chunk, int chunkIndex, int firstEntityIndex) |
|||
{ |
|||
// ...
|
|||
} |
|||
} |
|||
|
|||
#endregion
|
|||
|
|||
protected override void OnUpdate() |
|||
{ |
|||
var job = new RotationSpeedJob() |
|||
{ |
|||
RotationType = GetArchetypeChunkComponentType<Rotation>(false), |
|||
RotationSpeedType = GetArchetypeChunkComponentType<RotationSpeed>(true), |
|||
DeltaTime = Time.DeltaTime |
|||
}; |
|||
|
|||
this.Dependency = job.ScheduleParallel(m_Query, this.Dependency); |
|||
} |
|||
} |
|||
|
|||
public class RotationSpeedSystemExample3 : SystemBase |
|||
{ |
|||
private EntityQuery m_Query; |
|||
|
|||
#region oncreate3
|
|||
|
|||
protected override void OnCreate() |
|||
{ |
|||
var queryDescription0 = new EntityQueryDesc |
|||
{ |
|||
All = new ComponentType[] {typeof(Rotation)} |
|||
}; |
|||
|
|||
var queryDescription1 = new EntityQueryDesc |
|||
{ |
|||
All = new ComponentType[] {typeof(RotationSpeed)} |
|||
}; |
|||
|
|||
m_Query = GetEntityQuery(new EntityQueryDesc[] {queryDescription0, queryDescription1}); |
|||
} |
|||
|
|||
#endregion
|
|||
|
|||
[BurstCompile] |
|||
struct RotationSpeedJob : IJobChunk |
|||
{ |
|||
public float DeltaTime; |
|||
public ArchetypeChunkComponentType<Rotation> RotationType; |
|||
[ReadOnly] public ArchetypeChunkComponentType<RotationSpeed> RotationSpeedType; |
|||
|
|||
#region execsignature
|
|||
|
|||
public void Execute(ArchetypeChunk chunk, int chunkIndex, int firstEntityIndex) |
|||
|
|||
#endregion
|
|||
|
|||
{ |
|||
#region getcomponents
|
|||
|
|||
var chunkRotations = chunk.GetNativeArray(RotationType); |
|||
var chunkRotationSpeeds = chunk.GetNativeArray(RotationSpeedType); |
|||
|
|||
#endregion
|
|||
} |
|||
} |
|||
|
|||
protected override void OnUpdate() |
|||
{ |
|||
var job = new RotationSpeedJob() |
|||
{ |
|||
RotationType = GetArchetypeChunkComponentType<Rotation>(false), |
|||
RotationSpeedType = GetArchetypeChunkComponentType<RotationSpeed>(true), |
|||
DeltaTime = Time.DeltaTime |
|||
}; |
|||
|
|||
this.Dependency = job.ScheduleParallel(m_Query, this.Dependency); |
|||
} |
|||
} |
|||
|
|||
public struct Output : IComponentData |
|||
{ |
|||
public float Value; |
|||
} |
|||
|
|||
public struct InputA : IComponentData |
|||
{ |
|||
public float Value; |
|||
} |
|||
|
|||
public struct InputB : IComponentData |
|||
{ |
|||
public float Value; |
|||
} |
|||
|
|||
public class UpdateSystemExample : SystemBase |
|||
{ |
|||
#region changefilter
|
|||
|
|||
private EntityQuery m_Query; |
|||
|
|||
protected override void OnCreate() |
|||
{ |
|||
m_Query = GetEntityQuery( |
|||
ComponentType.ReadWrite<Output>(), |
|||
ComponentType.ReadOnly<InputA>(), |
|||
ComponentType.ReadOnly<InputB>()); |
|||
m_Query.SetChangedVersionFilter( |
|||
new ComponentType[] |
|||
{ |
|||
ComponentType.ReadWrite<InputA>(), |
|||
ComponentType.ReadWrite<InputB>() |
|||
}); |
|||
} |
|||
|
|||
#endregion
|
|||
|
|||
#region changefilterjobstruct
|
|||
|
|||
[BurstCompile] |
|||
struct UpdateJob : IJobChunk |
|||
{ |
|||
public ArchetypeChunkComponentType<InputA> InputAType; |
|||
public ArchetypeChunkComponentType<InputB> InputBType; |
|||
[ReadOnly] public ArchetypeChunkComponentType<Output> OutputType; |
|||
public uint LastSystemVersion; |
|||
|
|||
public void Execute(ArchetypeChunk chunk, int chunkIndex, int firstEntityIndex) |
|||
{ |
|||
var inputAChanged = chunk.DidChange(InputAType, LastSystemVersion); |
|||
var inputBChanged = chunk.DidChange(InputBType, LastSystemVersion); |
|||
|
|||
// If neither component changed, skip the current chunk
|
|||
if (!(inputAChanged || inputBChanged)) |
|||
return; |
|||
|
|||
var inputAs = chunk.GetNativeArray(InputAType); |
|||
var inputBs = chunk.GetNativeArray(InputBType); |
|||
var outputs = chunk.GetNativeArray(OutputType); |
|||
|
|||
for (var i = 0; i < outputs.Length; i++) |
|||
{ |
|||
outputs[i] = new Output{ Value = inputAs[i].Value + inputBs[i].Value }; |
|||
} |
|||
} |
|||
} |
|||
|
|||
#endregion
|
|||
|
|||
#region changefilteronupdate
|
|||
|
|||
protected override void OnUpdate() |
|||
{ |
|||
var job = new UpdateJob(); |
|||
|
|||
job.LastSystemVersion = this.LastSystemVersion; |
|||
|
|||
job.InputAType = GetArchetypeChunkComponentType<InputA>(true); |
|||
job.InputBType = GetArchetypeChunkComponentType<InputB>(true); |
|||
job.OutputType = GetArchetypeChunkComponentType<Output>(false); |
|||
|
|||
this.Dependency = job.ScheduleParallel(m_Query, this.Dependency); |
|||
} |
|||
|
|||
#endregion
|
|||
} |
|||
|
|||
#region basic-ijobchunk
|
|||
[GenerateAuthoringComponent] |
|||
public struct Target : IComponentData |
|||
{ |
|||
public Entity entity; |
|||
} |
|||
|
|||
public class ChaserSystem : SystemBase |
|||
{ |
|||
private EntityQuery query; // Initialized in Oncreate()
|
|||
|
|||
[BurstCompile] |
|||
private struct ChaserSystemJob : IJobChunk |
|||
{ |
|||
// Read-write data in the current chunk
|
|||
public ArchetypeChunkComponentType<Translation> PositionTypeAccessor; |
|||
|
|||
// Read-only data in the current chunk
|
|||
[ReadOnly] |
|||
public ArchetypeChunkComponentType<Target> TargetTypeAccessor; |
|||
|
|||
// Read-only data stored (potentially) in other chunks
|
|||
[ReadOnly] |
|||
//[NativeDisableParallelForRestriction]
|
|||
public ComponentDataFromEntity<LocalToWorld> EntityPositions; |
|||
|
|||
// Non-entity data
|
|||
public float deltaTime; |
|||
|
|||
public void Execute(ArchetypeChunk chunk, int chunkIndex, int firstEntityIndex) |
|||
{ |
|||
NativeArray<Translation> positions = chunk.GetNativeArray<Translation>(PositionTypeAccessor); |
|||
NativeArray<Target> targets = chunk.GetNativeArray<Target>(TargetTypeAccessor); |
|||
|
|||
for (int i = 0; i < positions.Length; i++) |
|||
{ |
|||
Entity targetEntity = targets[i].entity; |
|||
float3 targetPosition = EntityPositions[targetEntity].Position; |
|||
float3 chaserPosition = positions[i].Value; |
|||
|
|||
float3 displacement = (targetPosition - chaserPosition); |
|||
positions[i] = new Translation { Value = chaserPosition + displacement * deltaTime }; |
|||
} |
|||
} |
|||
} |
|||
|
|||
protected override void OnCreate() |
|||
{ |
|||
query = this.GetEntityQuery(typeof(Translation), ComponentType.ReadOnly<Target>()); |
|||
} |
|||
|
|||
protected override void OnUpdate() |
|||
{ |
|||
var job = new ChaserSystemJob(); |
|||
job.PositionTypeAccessor = this.GetArchetypeChunkComponentType<Translation>(false); |
|||
job.TargetTypeAccessor = this.GetArchetypeChunkComponentType<Target>(true); |
|||
|
|||
job.EntityPositions = this.GetComponentDataFromEntity<LocalToWorld>(true); |
|||
job.deltaTime = this.Time.DeltaTime; |
|||
|
|||
this.Dependency = job.Schedule(query, this.Dependency); |
|||
} |
|||
} |
|||
#endregion
|
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: 1be1511b73106412c9415ec5d838bcd1 |
|||
MonoImporter: |
|||
externalObjects: {} |
|||
serializedVersion: 2 |
|||
defaultReferences: [] |
|||
executionOrder: 0 |
|||
icon: {instanceID: 0} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
|
|||
[assembly: Unity.Entities.DisableAutoCreation] |
|||
//This declaration prevents the systems in the documentation sample source from being auto-created.
|
|
|||
fileFormatVersion: 2 |
|||
guid: dde99cc3e22634ce68972e0948627b30 |
|||
MonoImporter: |
|||
externalObjects: {} |
|||
serializedVersion: 2 |
|||
defaultReferences: [] |
|||
executionOrder: 0 |
|||
icon: {instanceID: 0} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
|
|||
{ |
|||
"name": "DocCodeSamples", |
|||
"references": [ |
|||
"Unity.Jobs", |
|||
"Unity.Entities", |
|||
"Unity.Burst", |
|||
"Unity.Mathematics", |
|||
"Unity.Collections", |
|||
"UnityEngine.TestRunner", |
|||
"UnityEditor.TestRunner", |
|||
"Unity.Transforms", |
|||
"Unity.Mathematics.Extensions", |
|||
"Unity.Mathematics.Extensions.Hybrid" |
|||
], |
|||
"includePlatforms": [ |
|||
"Editor" |
|||
], |
|||
"excludePlatforms": [], |
|||
"allowUnsafeCode": true, |
|||
"overrideReferences": true, |
|||
"precompiledReferences": [ |
|||
"nunit.framework.dll" |
|||
], |
|||
"autoReferenced": false, |
|||
"defineConstraints": [ |
|||
"UNITY_INCLUDE_TESTS" |
|||
], |
|||
"versionDefines": [], |
|||
"noEngineReferences": false |
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: b8bd0640a304b40c29d635a23f8f4b5e |
|||
AssemblyDefinitionImporter: |
|||
externalObjects: {} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
|
|||
using System.Collections; |
|||
using System.Collections.Generic; |
|||
using UnityEngine; |
|||
using Unity.Entities; |
|||
using Unity.Jobs; |
|||
using Unity.Collections; |
|||
|
|||
namespace Doc.CodeSamples.Tests |
|||
{ |
|||
public class DynamicBuffersManual : MonoBehaviour |
|||
{ |
|||
} |
|||
|
|||
#region declare-element
|
|||
|
|||
public struct IntBufferElement : IBufferElementData |
|||
{ |
|||
public int Value; |
|||
} |
|||
|
|||
#endregion
|
|||
|
|||
#region declare-element-full
|
|||
|
|||
// InternalBufferCapacity specifies how many elements a buffer can have before
|
|||
// the buffer storage is moved outside the chunk.
|
|||
[InternalBufferCapacity(8)] |
|||
public struct MyBufferElement : IBufferElementData |
|||
{ |
|||
// Actual value each buffer element will store.
|
|||
public int Value; |
|||
|
|||
// The following implicit conversions are optional, but can be convenient.
|
|||
public static implicit operator int(MyBufferElement e) |
|||
{ |
|||
return e.Value; |
|||
} |
|||
|
|||
public static implicit operator MyBufferElement(int e) |
|||
{ |
|||
return new MyBufferElement { Value = e }; |
|||
} |
|||
} |
|||
|
|||
#endregion
|
|||
|
|||
public struct DataToSpawn : IComponentData |
|||
{ |
|||
public int EntityCount; |
|||
public int ElementCount; |
|||
} |
|||
|
|||
public class AddBufferSnippets : SystemBase |
|||
{ |
|||
protected override void OnCreate() |
|||
{ |
|||
var entity = EntityManager.CreateEntity(); |
|||
|
|||
#region add-with-manager
|
|||
|
|||
EntityManager.AddBuffer<MyBufferElement>(entity); |
|||
|
|||
#endregion
|
|||
|
|||
#region add-with-archetype
|
|||
|
|||
Entity e = EntityManager.CreateEntity(typeof(MyBufferElement)); |
|||
|
|||
#endregion
|
|||
|
|||
#region reinterpret-snippet
|
|||
|
|||
DynamicBuffer<int> intBuffer |
|||
= EntityManager.GetBuffer<MyBufferElement>(entity).Reinterpret<int>(); |
|||
|
|||
#endregion
|
|||
|
|||
#region access-manager
|
|||
|
|||
DynamicBuffer<MyBufferElement> dynamicBuffer |
|||
= EntityManager.GetBuffer<MyBufferElement>(entity); |
|||
|
|||
#endregion
|
|||
|
|||
#region lookup-snippet
|
|||
|
|||
BufferFromEntity<MyBufferElement> lookup = GetBufferFromEntity<MyBufferElement>(); |
|||
var buffer = lookup[entity]; |
|||
buffer.Add(17); |
|||
buffer.RemoveAt(0); |
|||
|
|||
#endregion
|
|||
|
|||
#region invalidation
|
|||
|
|||
var entity1 = EntityManager.CreateEntity(); |
|||
var entity2 = EntityManager.CreateEntity(); |
|||
|
|||
DynamicBuffer<MyBufferElement> buffer1 |
|||
= EntityManager.AddBuffer<MyBufferElement>(entity1); |
|||
// This line causes a structural change and invalidates
|
|||
// the previously acquired dynamic buffer
|
|||
DynamicBuffer<MyBufferElement> buffer2 |
|||
= EntityManager.AddBuffer<MyBufferElement>(entity1); |
|||
// This line will cause an error:
|
|||
buffer1.Add(17); |
|||
|
|||
#endregion
|
|||
} |
|||
|
|||
#region add-in-job
|
|||
|
|||
#pragma warning disable 618
|
|||
struct DataSpawnJob : IJobForEachWithEntity<DataToSpawn> |
|||
{ |
|||
// A command buffer marshals structural changes to the data
|
|||
public EntityCommandBuffer.Concurrent CommandBuffer; |
|||
|
|||
//The DataToSpawn component tells us how many entities with buffers to create
|
|||
public void Execute(Entity spawnEntity, int index, [ReadOnly] ref DataToSpawn data) |
|||
{ |
|||
for (int e = 0; e < data.EntityCount; e++) |
|||
{ |
|||
//Create a new entity for the command buffer
|
|||
Entity newEntity = CommandBuffer.CreateEntity(index); |
|||
|
|||
//Create the dynamic buffer and add it to the new entity
|
|||
DynamicBuffer<MyBufferElement> buffer = |
|||
CommandBuffer.AddBuffer<MyBufferElement>(index, newEntity); |
|||
|
|||
//Reinterpret to plain int buffer
|
|||
DynamicBuffer<int> intBuffer = buffer.Reinterpret<int>(); |
|||
|
|||
//Optionally, populate the dynamic buffer
|
|||
for (int j = 0; j < data.ElementCount; j++) |
|||
{ |
|||
intBuffer.Add(j); |
|||
} |
|||
} |
|||
|
|||
//Destroy the DataToSpawn entity since it has done its job
|
|||
CommandBuffer.DestroyEntity(index, spawnEntity); |
|||
} |
|||
} |
|||
#pragma warning restore 618
|
|||
|
|||
#endregion
|
|||
|
|||
protected override void OnUpdate() |
|||
{ |
|||
throw new System.NotImplementedException(); |
|||
} |
|||
} |
|||
|
|||
#region access-buffer-system
|
|||
|
|||
public class DynamicBufferSystem : SystemBase |
|||
{ |
|||
protected override void OnUpdate() |
|||
{ |
|||
var sum = 0; |
|||
|
|||
Entities.ForEach((DynamicBuffer<MyBufferElement> buffer) => |
|||
{ |
|||
foreach (var integer in buffer.Reinterpret<int>()) |
|||
{ |
|||
sum += integer; |
|||
} |
|||
}).Run(); |
|||
|
|||
Debug.Log("Sum of all buffers: " + sum); |
|||
} |
|||
} |
|||
|
|||
#endregion
|
|||
|
|||
#region access-ijfe
|
|||
|
|||
public class DynamicBufferForEachSystem : SystemBase |
|||
{ |
|||
private EntityQuery query; |
|||
|
|||
//Sums the intermediate results into the final total
|
|||
public struct SumResult : IJob |
|||
{ |
|||
[DeallocateOnJobCompletion] public NativeArray<int> sums; |
|||
|
|||
public void Execute() |
|||
{ |
|||
int sum = 0; |
|||
foreach (int integer in sums) |
|||
{ |
|||
sum += integer; |
|||
} |
|||
|
|||
//Note: Debug.Log is not burst-compatible
|
|||
Debug.Log("Sum of all buffers: " + sum); |
|||
} |
|||
} |
|||
|
|||
//Schedules the two jobs with a dependency between them
|
|||
protected override void OnUpdate() |
|||
{ |
|||
//Create a native array to hold the intermediate sums
|
|||
int entitiesInQuery = query.CalculateEntityCount(); |
|||
NativeArray<int> intermediateSums |
|||
= new NativeArray<int>(entitiesInQuery, Allocator.TempJob); |
|||
|
|||
//Schedule the first job to add all the buffer elements
|
|||
Entities |
|||
.WithStoreEntityQueryInField(ref query) |
|||
.ForEach((int entityInQueryIndex, Entity entity, in DynamicBuffer<MyBufferElement> buffer) => { |
|||
foreach (int integer in buffer.Reinterpret<int>()) |
|||
{ |
|||
intermediateSums[entityInQueryIndex] += integer; |
|||
} |
|||
}) |
|||
.ScheduleParallel(); |
|||
|
|||
// Must use a NativeArray to get data out of a Job.WithCode when scheduled to run on a background thread
|
|||
NativeArray<int> sum = new NativeArray<int>(1, Allocator.TempJob); |
|||
|
|||
//Schedule the second job, which depends on the first
|
|||
Job |
|||
.WithDeallocateOnJobCompletion(intermediateSums) |
|||
.WithCode(() => |
|||
{ |
|||
foreach (int integer in intermediateSums) |
|||
{ |
|||
sum[0] += integer; |
|||
} |
|||
//if we do not specify dependencies, the job depends on the Dependency property
|
|||
}).Schedule(); |
|||
// Likewise, if we don't return a JobHandle, the system adds the job to its Dependency property
|
|||
|
|||
//sum[0] will contain the result after all the jobs have finished
|
|||
this.CompleteDependency(); // Wait for the results now
|
|||
|
|||
Debug.Log(sum[0]); |
|||
sum.Dispose(); |
|||
} |
|||
} |
|||
|
|||
#endregion
|
|||
|
|||
#region access-chunk-job
|
|||
|
|||
public class DynamicBufferJobSystem : SystemBase |
|||
{ |
|||
private EntityQuery query; |
|||
|
|||
protected override void OnCreate() |
|||
{ |
|||
//Create a query to find all entities with a dynamic buffer
|
|||
// containing MyBufferElement
|
|||
EntityQueryDesc queryDescription = new EntityQueryDesc(); |
|||
queryDescription.All = new[] {ComponentType.ReadOnly<MyBufferElement>()}; |
|||
query = GetEntityQuery(queryDescription); |
|||
} |
|||
|
|||
public struct BuffersInChunks : IJobChunk |
|||
{ |
|||
//The data type and safety object
|
|||
public ArchetypeChunkBufferType<MyBufferElement> BufferType; |
|||
|
|||
//An array to hold the output, intermediate sums
|
|||
public NativeArray<int> sums; |
|||
|
|||
public void Execute(ArchetypeChunk chunk, |
|||
int chunkIndex, |
|||
int firstEntityIndex) |
|||
{ |
|||
//A buffer accessor is a list of all the buffers in the chunk
|
|||
BufferAccessor<MyBufferElement> buffers |
|||
= chunk.GetBufferAccessor(BufferType); |
|||
|
|||
for (int c = 0; c < chunk.Count; c++) |
|||
{ |
|||
//An individual dynamic buffer for a specific entity
|
|||
DynamicBuffer<MyBufferElement> buffer = buffers[c]; |
|||
foreach (MyBufferElement element in buffer) |
|||
{ |
|||
sums[chunkIndex] += element.Value; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
//Sums the intermediate results into the final total
|
|||
public struct SumResult : IJob |
|||
{ |
|||
[DeallocateOnJobCompletion] public NativeArray<int> sums; |
|||
public NativeArray<int> result; |
|||
public void Execute() |
|||
{ |
|||
foreach (int integer in sums) |
|||
{ |
|||
result[0] += integer; |
|||
} |
|||
} |
|||
} |
|||
|
|||
protected override void OnUpdate() |
|||
{ |
|||
//Create a native array to hold the intermediate sums
|
|||
int chunksInQuery = query.CalculateChunkCount(); |
|||
NativeArray<int> intermediateSums |
|||
= new NativeArray<int>(chunksInQuery, Allocator.TempJob); |
|||
|
|||
//Schedule the first job to add all the buffer elements
|
|||
BuffersInChunks bufferJob = new BuffersInChunks(); |
|||
bufferJob.BufferType = GetArchetypeChunkBufferType<MyBufferElement>(); |
|||
bufferJob.sums = intermediateSums; |
|||
this.Dependency = bufferJob.ScheduleParallel(query, this.Dependency); |
|||
|
|||
//Schedule the second job, which depends on the first
|
|||
SumResult finalSumJob = new SumResult(); |
|||
finalSumJob.sums = intermediateSums; |
|||
NativeArray<int> finalSum = new NativeArray<int>(1, Allocator.Temp); |
|||
finalSumJob.result = finalSum; |
|||
this.Dependency = finalSumJob.Schedule(this.Dependency); |
|||
|
|||
this.CompleteDependency(); |
|||
Debug.Log("Sum of all buffers: " + finalSum[0]); |
|||
finalSum.Dispose(); |
|||
} |
|||
} |
|||
|
|||
#endregion
|
|||
|
|||
#region dynamicbuffer.class
|
|||
|
|||
[InternalBufferCapacity(8)] |
|||
public struct FloatBufferElement : IBufferElementData |
|||
{ |
|||
// Actual value each buffer element will store.
|
|||
public float Value; |
|||
|
|||
// The following implicit conversions are optional, but can be convenient.
|
|||
public static implicit operator float(FloatBufferElement e) |
|||
{ |
|||
return e.Value; |
|||
} |
|||
|
|||
public static implicit operator FloatBufferElement(float e) |
|||
{ |
|||
return new FloatBufferElement {Value = e}; |
|||
} |
|||
} |
|||
|
|||
public class DynamicBufferExample : ComponentSystem |
|||
{ |
|||
protected override void OnUpdate() |
|||
{ |
|||
float sum = 0; |
|||
|
|||
Entities.ForEach((DynamicBuffer<FloatBufferElement> buffer) => |
|||
{ |
|||
foreach (var element in buffer.Reinterpret<float>()) |
|||
{ |
|||
sum += element; |
|||
} |
|||
}); |
|||
|
|||
Debug.Log("Sum of all buffers: " + sum); |
|||
} |
|||
} |
|||
|
|||
#endregion
|
|||
|
|||
class DynamicBufferSnippets |
|||
{ |
|||
private void ShowAdd() |
|||
{ |
|||
DynamicBuffer<int> buffer = new DynamicBuffer<int>(); |
|||
DynamicBuffer<int> secondBuffer = new DynamicBuffer<int>(); |
|||
|
|||
#region dynamicbuffer.add
|
|||
|
|||
buffer.Add(5); |
|||
|
|||
#endregion
|
|||
|
|||
#region dynamicbuffer.addrange
|
|||
|
|||
int[] source = {1, 2, 3, 4, 5}; |
|||
NativeArray<int> newElements = new NativeArray<int>(source, Allocator.Persistent); |
|||
buffer.AddRange(newElements); |
|||
|
|||
#endregion
|
|||
|
|||
#region dynamicbuffer.asnativearray
|
|||
|
|||
int[] intArray = {1, 2, 3, 4, 5}; |
|||
NativeArray<int>.Copy(intArray, buffer.AsNativeArray()); |
|||
|
|||
#endregion
|
|||
|
|||
#region dynamicbuffer.capacity
|
|||
|
|||
#endregion
|
|||
|
|||
#region dynamicbuffer.clear
|
|||
|
|||
buffer.Clear(); |
|||
|
|||
#endregion
|
|||
|
|||
#region dynamicbuffer.copyfrom.dynamicbuffer
|
|||
|
|||
buffer.CopyFrom(secondBuffer); |
|||
|
|||
#endregion
|
|||
|
|||
#region dynamicbuffer.copyfrom.nativearray
|
|||
|
|||
int[] sourceArray = {1, 2, 3, 4, 5}; |
|||
NativeArray<int> nativeArray = new NativeArray<int>(source, Allocator.Persistent); |
|||
buffer.CopyFrom(nativeArray); |
|||
|
|||
#endregion
|
|||
|
|||
#region dynamicbuffer.copyfrom.array
|
|||
|
|||
int[] integerArray = {1, 2, 3, 4, 5}; |
|||
buffer.CopyFrom(integerArray); |
|||
|
|||
#endregion
|
|||
|
|||
#region dynamicbuffer.getenumerator
|
|||
|
|||
foreach (var element in buffer) |
|||
{ |
|||
//Use element...
|
|||
} |
|||
|
|||
#endregion
|
|||
|
|||
#region dynamicbuffer.getunsafeptr
|
|||
|
|||
#endregion
|
|||
|
|||
int insertionIndex = 2; |
|||
|
|||
#region dynamicbuffer.insert
|
|||
|
|||
if (insertionIndex < buffer.Length) |
|||
buffer.Insert(insertionIndex, 6); |
|||
|
|||
#endregion
|
|||
|
|||
#region dynamicbuffer.iscreated
|
|||
|
|||
#endregion
|
|||
|
|||
#region dynamicbuffer.length
|
|||
|
|||
for (int i = 0; i < buffer.Length; i++) |
|||
{ |
|||
buffer[i] = i * i; |
|||
} |
|||
|
|||
#endregion
|
|||
|
|||
#region dynamicbuffer.removeat
|
|||
|
|||
if (insertionIndex < buffer.Length) |
|||
buffer.RemoveAt(insertionIndex); |
|||
|
|||
#endregion
|
|||
|
|||
int start = 1; |
|||
|
|||
#region dynamicbuffer.removerange
|
|||
|
|||
buffer.RemoveRange(start, 5); |
|||
|
|||
#endregion
|
|||
|
|||
#region dynamicbuffer.reserve
|
|||
|
|||
buffer.EnsureCapacity(buffer.Capacity + 10); |
|||
|
|||
#endregion
|
|||
|
|||
#region dynamicbuffer.resizeuninitialized
|
|||
|
|||
buffer.ResizeUninitialized(buffer.Length + 10); |
|||
|
|||
#endregion
|
|||
|
|||
#region dynamicbuffer.indexoperator
|
|||
|
|||
for (int i = 0; i < buffer.Length; i++) |
|||
{ |
|||
buffer[i] = i * i; |
|||
} |
|||
|
|||
#endregion
|
|||
|
|||
#region dynamicbuffer.tonativearray
|
|||
|
|||
NativeArray<int> copy = buffer.ToNativeArray(Allocator.Persistent); |
|||
|
|||
#endregion
|
|||
|
|||
#region dynamicbuffer.trimexcess
|
|||
|
|||
if (buffer.Capacity > buffer.Length) |
|||
buffer.TrimExcess(); |
|||
|
|||
#endregion
|
|||
} |
|||
} |
|||
|
|||
public class ReinterpretExample : SystemBase |
|||
{ |
|||
protected override void OnUpdate() |
|||
{ |
|||
#region dynamicbuffer.reinterpret
|
|||
|
|||
Entities.ForEach((DynamicBuffer<FloatBufferElement> buffer) => |
|||
{ |
|||
DynamicBuffer<float> floatBuffer = buffer.Reinterpret<float>(); |
|||
for (int i = 0; i < floatBuffer.Length; i++) |
|||
{ |
|||
floatBuffer[i] = i * 1.2f; |
|||
} |
|||
}).ScheduleParallel(); |
|||
|
|||
#endregion
|
|||
} |
|||
} |
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: 9377b3c4b1cd34b53adf01e4b73ebe6e |
|||
MonoImporter: |
|||
externalObjects: {} |
|||
serializedVersion: 2 |
|||
defaultReferences: [] |
|||
executionOrder: 0 |
|||
icon: {instanceID: 0} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
|
|||
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 |
|||
{ |
|||
#region ecb_concurrent
|
|||
struct Lifetime : IComponentData |
|||
{ |
|||
public byte Value; |
|||
} |
|||
|
|||
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 |
|||
.GetOrCreateSystem<EndSimulationEntityCommandBufferSystem>(); |
|||
} |
|||
|
|||
protected override void OnUpdate() |
|||
{ |
|||
// Acquire an ECB and convert it to a concurrent one to be able
|
|||
// to use it from a parallel job.
|
|||
var ecb = m_EndSimulationEcbSystem.CreateCommandBuffer().ToConcurrent(); |
|||
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(); |
|||
|
|||
// Make sure that the ECB system knows about our job
|
|||
m_EndSimulationEcbSystem.AddJobHandleForProducer(this.Dependency); |
|||
} |
|||
} |
|||
#endregion
|
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: 6a21d06d3f358b94db9f72a17ac078d9 |
|||
MonoImporter: |
|||
externalObjects: {} |
|||
serializedVersion: 2 |
|||
defaultReferences: [] |
|||
executionOrder: 0 |
|||
icon: {instanceID: 0} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
|
|||
using System; |
|||
using Unity.Collections; |
|||
using Unity.Entities; |
|||
using Unity.Mathematics; |
|||
using Unity.Transforms; |
|||
|
|||
namespace Doc.CodeSamples.Tests |
|||
{ |
|||
#region singleton-type-example
|
|||
public struct Singlet : IComponentData |
|||
{ |
|||
public int Value; |
|||
} |
|||
#endregion
|
|||
|
|||
public class EntityQueryExamples : SystemBase |
|||
{ |
|||
void queryFromList() |
|||
{ |
|||
#region query-from-list
|
|||
EntityQuery query = GetEntityQuery(typeof(Rotation), |
|||
ComponentType.ReadOnly<RotationSpeed>()); |
|||
#endregion
|
|||
} |
|||
|
|||
void queryFromDescription() |
|||
{ |
|||
#region query-from-description
|
|||
EntityQueryDesc description = new EntityQueryDesc |
|||
{ |
|||
None = new ComponentType[] |
|||
{ |
|||
typeof(Frozen) |
|||
}, |
|||
All = new ComponentType[] |
|||
{ |
|||
typeof(Rotation), |
|||
ComponentType.ReadOnly<RotationSpeed>() |
|||
} |
|||
}; |
|||
EntityQuery query = GetEntityQuery(description); |
|||
#endregion
|
|||
} |
|||
|
|||
protected override void OnUpdate() |
|||
{ |
|||
var queryForSingleton = EntityManager.CreateEntityQuery(typeof(Singlet)); |
|||
var entityManager = EntityManager; |
|||
#region create-singleton
|
|||
Entity singletonEntity = entityManager.CreateEntity(typeof(Singlet)); |
|||
entityManager.SetComponentData(singletonEntity, new Singlet { Value = 1 }); |
|||
#endregion
|
|||
|
|||
#region set-singleton
|
|||
queryForSingleton.SetSingleton<Singlet>(new Singlet {Value = 1}); |
|||
#endregion
|
|||
} |
|||
} |
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: 9e54548f2375641e28c985b9b09c0fe6 |
|||
MonoImporter: |
|||
externalObjects: {} |
|||
serializedVersion: 2 |
|||
defaultReferences: [] |
|||
executionOrder: 0 |
|||
icon: {instanceID: 0} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
|
|||
|
|||
using Unity.Entities.UniversalDelegates; |
|||
|
|||
namespace Doc.CodeSamples.Tests |
|||
{ |
|||
using Unity.Entities; |
|||
using Unity.Jobs; |
|||
using Unity.Transforms; |
|||
using Unity.Collections; |
|||
using System.Collections.Generic; |
|||
using UnityEngine; |
|||
using Unity.Mathematics; |
|||
using Random = Unity.Mathematics.Random; |
|||
|
|||
#region entities-foreach-example
|
|||
class ApplyVelocitySystem : SystemBase |
|||
{ |
|||
protected override void OnUpdate() |
|||
{ |
|||
Entities |
|||
.ForEach((ref Translation translation, |
|||
in Velocity velocity) => |
|||
{ |
|||
translation.Value += velocity.Value; |
|||
}) |
|||
.Schedule(); |
|||
} |
|||
} |
|||
#endregion
|
|||
#region job-with-code-example
|
|||
public class RandomSumJob : SystemBase |
|||
{ |
|||
private uint seed = 1; |
|||
|
|||
protected override void OnUpdate() |
|||
{ |
|||
Random randomGen = new Random(seed++); |
|||
NativeArray<float> randomNumbers |
|||
= new NativeArray<float>(500, Allocator.TempJob); |
|||
|
|||
Job.WithCode(() => |
|||
{ |
|||
for (int i = 0; i < randomNumbers.Length; i++) |
|||
{ |
|||
randomNumbers[i] = randomGen.NextFloat(); |
|||
} |
|||
}).Schedule(); |
|||
|
|||
// To get data out of a job, you must use a NativeArray
|
|||
// even if there is only one value
|
|||
NativeArray<float> result |
|||
= new NativeArray<float>(1, Allocator.TempJob); |
|||
|
|||
Job.WithCode(() => |
|||
{ |
|||
for (int i = 0; i < randomNumbers.Length; i++) |
|||
{ |
|||
result[0] += randomNumbers[i]; |
|||
} |
|||
}).Schedule(); |
|||
|
|||
// This completes the scheduled jobs to get the result immediately, but for
|
|||
// better efficiency you should schedule jobs early in the frame with one
|
|||
// system and get the results late in the frame with a different system.
|
|||
this.CompleteDependency(); |
|||
UnityEngine.Debug.Log("The sum of " |
|||
+ randomNumbers.Length + " numbers is " + result[0]); |
|||
|
|||
randomNumbers.Dispose(); |
|||
result.Dispose(); |
|||
} |
|||
} |
|||
|
|||
#endregion
|
|||
|
|||
//Used to verify the BuffersByEntity example (not shown in docs)
|
|||
public class MakeData : SystemBase |
|||
{ |
|||
protected override void OnCreate() |
|||
{ |
|||
var sum = 0; |
|||
for (int i = 0; i < 100; i++) |
|||
{ |
|||
var ent = EntityManager.CreateEntity(typeof(IntBufferElement)); |
|||
var buff = EntityManager.GetBuffer<IntBufferElement>(ent).Reinterpret<int>(); |
|||
for (int j = 0; j < 5; j++) |
|||
{ |
|||
buff.Add(j); |
|||
sum += j; |
|||
} |
|||
} |
|||
|
|||
UnityEngine.Debug.Log("Sum should equal " + sum); |
|||
} |
|||
|
|||
protected override void OnUpdate() |
|||
{ |
|||
} |
|||
} |
|||
|
|||
public struct IntBufferData : IBufferElementData |
|||
{ |
|||
public int Value; |
|||
} |
|||
|
|||
#region dynamicbuffer
|
|||
public class BufferSum : SystemBase |
|||
{ |
|||
private EntityQuery query; |
|||
|
|||
//Schedules the two jobs with a dependency between them
|
|||
protected override void OnUpdate() |
|||
{ |
|||
//The query variable can be accessed here because we are
|
|||
//using WithStoreEntityQueryInField(query) in the entities.ForEach below
|
|||
int entitiesInQuery = query.CalculateEntityCount(); |
|||
|
|||
//Create a native array to hold the intermediate sums
|
|||
//(one element per entity)
|
|||
NativeArray<int> intermediateSums |
|||
= new NativeArray<int>(entitiesInQuery, Allocator.TempJob); |
|||
|
|||
//Schedule the first job to add all the buffer elements
|
|||
Entities |
|||
.ForEach((int entityInQueryIndex, in DynamicBuffer<IntBufferData> buffer) => |
|||
{ |
|||
for (int i = 0; i < buffer.Length; i++) |
|||
{ |
|||
intermediateSums[entityInQueryIndex] += buffer[i].Value; |
|||
} |
|||
}) |
|||
.WithStoreEntityQueryInField(ref query) |
|||
.WithName("IntermediateSums") |
|||
.ScheduleParallel(); // Execute in parallel for each chunk of entities
|
|||
|
|||
//Schedule the second job, which depends on the first
|
|||
Job |
|||
.WithCode(() => |
|||
{ |
|||
int result = 0; |
|||
for (int i = 0; i < intermediateSums.Length; i++) |
|||
{ |
|||
result += intermediateSums[i]; |
|||
} |
|||
//Not burst compatible:
|
|||
Debug.Log("Final sum is " + result); |
|||
}) |
|||
.WithDeallocateOnJobCompletion(intermediateSums) |
|||
.WithoutBurst() |
|||
.WithName("FinalSum") |
|||
.Schedule(); // Execute on a single, background thread
|
|||
} |
|||
} |
|||
#endregion
|
|||
|
|||
public struct Source : IComponentData |
|||
{ |
|||
public int Value; |
|||
} |
|||
public struct Destination : IComponentData |
|||
{ |
|||
public int Value; |
|||
} |
|||
|
|||
public class WithAllExampleSystem : SystemBase |
|||
{ |
|||
protected override void OnUpdate() |
|||
{ |
|||
#region entity-query
|
|||
Entities.WithAll<LocalToWorld>() |
|||
.WithAny<Rotation, Translation, Scale>() |
|||
.WithNone<LocalToParent>() |
|||
.ForEach((ref Destination outputData, in Source inputData) => |
|||
{ |
|||
/* do some work */ |
|||
}) |
|||
.Schedule(); |
|||
#endregion
|
|||
} |
|||
} |
|||
|
|||
public struct Data : IComponentData |
|||
{ |
|||
public float Value; |
|||
} |
|||
public class WithStoreQuerySystem : SystemBase |
|||
{ |
|||
#region store-query
|
|||
private EntityQuery query; |
|||
protected override void OnUpdate() |
|||
{ |
|||
int dataCount = query.CalculateEntityCount(); |
|||
NativeArray<float> dataSquared |
|||
= new NativeArray<float>(dataCount, Allocator.Temp); |
|||
Entities |
|||
.WithStoreEntityQueryInField(ref query) |
|||
.ForEach((int entityInQueryIndex, in Data data) => |
|||
{ |
|||
dataSquared[entityInQueryIndex] = data.Value * data.Value; |
|||
}) |
|||
.ScheduleParallel(); |
|||
|
|||
Job |
|||
.WithCode(() => |
|||
{ |
|||
//Use dataSquared array...
|
|||
var v = dataSquared[dataSquared.Length -1]; |
|||
}) |
|||
.WithDeallocateOnJobCompletion(dataSquared) |
|||
.Schedule(); |
|||
} |
|||
#endregion
|
|||
} |
|||
|
|||
public class WithChangeExampleSystem : SystemBase |
|||
{ |
|||
protected override void OnUpdate() |
|||
{ |
|||
#region with-change-filter
|
|||
Entities |
|||
.WithChangeFilter<Source>() |
|||
.ForEach((ref Destination outputData, |
|||
in Source inputData) => |
|||
{ |
|||
/* Do work */ |
|||
}) |
|||
.ScheduleParallel(); |
|||
#endregion
|
|||
} |
|||
} |
|||
|
|||
public struct Cohort : ISharedComponentData |
|||
{ |
|||
public int Value; |
|||
} |
|||
public struct DisplayColor : IComponentData |
|||
{ |
|||
public int Value; |
|||
} |
|||
|
|||
public class ColorTable |
|||
{ |
|||
public static DisplayColor GetNextColor(int current){return new DisplayColor();} |
|||
} |
|||
|
|||
#region with-shared-component
|
|||
public class ColorCycleJob : SystemBase |
|||
{ |
|||
protected override void OnUpdate() |
|||
{ |
|||
List<Cohort> cohorts = new List<Cohort>(); |
|||
EntityManager.GetAllUniqueSharedComponentData<Cohort>(cohorts); |
|||
foreach (Cohort cohort in cohorts) |
|||
{ |
|||
DisplayColor newColor = ColorTable.GetNextColor(cohort.Value); |
|||
Entities.WithSharedComponentFilter(cohort) |
|||
.ForEach((ref DisplayColor color) => { color = newColor; }) |
|||
.ScheduleParallel(); |
|||
} |
|||
} |
|||
} |
|||
#endregion
|
|||
|
|||
public class ReadWriteModExample : SystemBase |
|||
{ |
|||
protected override void OnUpdate() |
|||
{ |
|||
#region read-write-modifiers
|
|||
Entities.ForEach( |
|||
(ref Destination outputData, |
|||
in Source inputData) => |
|||
{ |
|||
outputData.Value = inputData.Value; |
|||
}) |
|||
.ScheduleParallel(); |
|||
#endregion
|
|||
} |
|||
} |
|||
|
|||
#region basic-ecb
|
|||
public class MyJobSystem : SystemBase |
|||
{ |
|||
private EndSimulationEntityCommandBufferSystem commandBufferSystem; |
|||
|
|||
protected override void OnCreate() |
|||
{ |
|||
commandBufferSystem = World |
|||
.DefaultGameObjectInjectionWorld |
|||
.GetOrCreateSystem<EndSimulationEntityCommandBufferSystem>(); |
|||
} |
|||
|
|||
protected override void OnUpdate() |
|||
{ |
|||
EntityCommandBuffer.Concurrent commandBuffer |
|||
= commandBufferSystem.CreateCommandBuffer().ToConcurrent(); |
|||
|
|||
//.. The rest of the job system code
|
|||
} |
|||
} |
|||
#endregion
|
|||
|
|||
public struct Movement:IComponentData |
|||
{ |
|||
public float3 Value; |
|||
} |
|||
|
|||
public class EFESystem : SystemBase |
|||
{ |
|||
protected override void OnUpdate() |
|||
{ |
|||
|
|||
#region lambda-params
|
|||
Entities.ForEach( |
|||
(Entity entity, |
|||
int entityInQueryIndex, |
|||
ref Translation translation, |
|||
in Movement move) => {/* .. */}) |
|||
#endregion
|
|||
.Run(); |
|||
} |
|||
|
|||
|
|||
} |
|||
} |
|||
|
|||
namespace Doc.CodeSamples.Tests |
|||
{ |
|||
#region full-ecb-pt-one
|
|||
// ParticleSpawner.cs
|
|||
using Unity.Entities; |
|||
using Unity.Jobs; |
|||
using Unity.Mathematics; |
|||
using Unity.Transforms; |
|||
|
|||
public struct Velocity : IComponentData |
|||
{ |
|||
public float3 Value; |
|||
} |
|||
|
|||
public struct TimeToLive : IComponentData |
|||
{ |
|||
public float LifeLeft; |
|||
} |
|||
|
|||
public class ParticleSpawner : SystemBase |
|||
{ |
|||
private EndSimulationEntityCommandBufferSystem commandBufferSystem; |
|||
|
|||
protected override void OnCreate() |
|||
{ |
|||
commandBufferSystem = World |
|||
.DefaultGameObjectInjectionWorld |
|||
.GetOrCreateSystem<EndSimulationEntityCommandBufferSystem>(); |
|||
} |
|||
|
|||
protected override void OnUpdate() |
|||
{ |
|||
EntityCommandBuffer.Concurrent commandBufferCreate |
|||
= commandBufferSystem.CreateCommandBuffer().ToConcurrent(); |
|||
EntityCommandBuffer.Concurrent commandBufferCull |
|||
= commandBufferSystem.CreateCommandBuffer().ToConcurrent(); |
|||
|
|||
float dt = Time.DeltaTime; |
|||
Random rnd = new Random(); |
|||
rnd.InitState((uint) (dt * 100000)); |
|||
|
|||
|
|||
JobHandle spawnJobHandle = Entities |
|||
.ForEach((int entityInQueryIndex, |
|||
in SpawnParticles spawn, |
|||
in LocalToWorld center) => |
|||
{ |
|||
int spawnCount = spawn.Rate; |
|||
for (int i = 0; i < spawnCount; i++) |
|||
{ |
|||
Entity spawnedEntity = commandBufferCreate |
|||
.Instantiate(entityInQueryIndex, |
|||
spawn.ParticlePrefab); |
|||
|
|||
LocalToWorld spawnedCenter = center; |
|||
Translation spawnedOffset = new Translation() |
|||
{ |
|||
Value = center.Position + |
|||
rnd.NextFloat3(-spawn.Offset, spawn.Offset) |
|||
}; |
|||
Velocity spawnedVelocity = new Velocity() |
|||
{ |
|||
Value = rnd.NextFloat3(-spawn.MaxVelocity, spawn.MaxVelocity) |
|||
}; |
|||
TimeToLive spawnedLife = new TimeToLive() |
|||
{ |
|||
LifeLeft = spawn.Lifetime |
|||
}; |
|||
|
|||
commandBufferCreate.SetComponent(entityInQueryIndex, |
|||
spawnedEntity, |
|||
spawnedCenter); |
|||
commandBufferCreate.SetComponent(entityInQueryIndex, |
|||
spawnedEntity, |
|||
spawnedOffset); |
|||
commandBufferCreate.AddComponent(entityInQueryIndex, |
|||
spawnedEntity, |
|||
spawnedVelocity); |
|||
commandBufferCreate.AddComponent(entityInQueryIndex, |
|||
spawnedEntity, |
|||
spawnedLife); |
|||
} |
|||
}) |
|||
.WithName("ParticleSpawning") |
|||
.Schedule(this.Dependency); |
|||
|
|||
JobHandle MoveJobHandle = Entities |
|||
.ForEach((ref Translation translation, in Velocity velocity) => |
|||
{ |
|||
translation = new Translation() |
|||
{ |
|||
Value = translation.Value + velocity.Value * dt |
|||
}; |
|||
}) |
|||
.WithName("MoveParticles") |
|||
.Schedule(spawnJobHandle); |
|||
|
|||
JobHandle cullJobHandle = Entities |
|||
.ForEach((Entity entity, int entityInQueryIndex, ref TimeToLive life) => |
|||
{ |
|||
life.LifeLeft -= dt; |
|||
if (life.LifeLeft < 0) |
|||
commandBufferCull.DestroyEntity(entityInQueryIndex, entity); |
|||
}) |
|||
.WithName("CullOldEntities") |
|||
.Schedule(this.Dependency); |
|||
|
|||
this.Dependency |
|||
= JobHandle.CombineDependencies(MoveJobHandle, cullJobHandle); |
|||
|
|||
commandBufferSystem.AddJobHandleForProducer(spawnJobHandle); |
|||
commandBufferSystem.AddJobHandleForProducer(cullJobHandle); |
|||
} |
|||
} |
|||
#endregion
|
|||
} |
|||
|
|||
namespace Doc.CodeSamples.Tests |
|||
{ |
|||
#region full-ecb-pt-two
|
|||
// SpawnParticles.cs
|
|||
using Unity.Entities; |
|||
using Unity.Mathematics; |
|||
|
|||
[GenerateAuthoringComponent] |
|||
public struct SpawnParticles : IComponentData |
|||
{ |
|||
public Entity ParticlePrefab; |
|||
public int Rate; |
|||
public float3 Offset; |
|||
public float3 MaxVelocity; |
|||
public float Lifetime; |
|||
} |
|||
#endregion
|
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: a263dbbca0d0044c095b173c3a1a39b8 |
|||
MonoImporter: |
|||
externalObjects: {} |
|||
serializedVersion: 2 |
|||
defaultReferences: [] |
|||
executionOrder: 0 |
|||
icon: {instanceID: 0} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
|
|||
using Unity.Burst; |
|||
using Unity.Collections; |
|||
using Unity.Entities; |
|||
using Unity.Jobs; |
|||
using Unity.Mathematics; |
|||
using Unity.Transforms; |
|||
using UnityEngine; |
|||
using NUnit.Framework; |
|||
|
|||
// The files in this namespace are used to compile/test the code samples in the documentation.
|
|||
namespace Doc.CodeSamples.Tests |
|||
{ |
|||
|
|||
#region lookup-foreach
|
|||
public class TrackingSystem : SystemBase |
|||
{ |
|||
protected override void OnUpdate() |
|||
{ |
|||
float deltaTime = this.Time.DeltaTime; |
|||
|
|||
Entities |
|||
.ForEach((ref Rotation orientation, |
|||
in LocalToWorld transform, |
|||
in Target target) => |
|||
{ |
|||
// Check to make sure the target Entity still exists and has
|
|||
// the needed component
|
|||
if (!HasComponent<LocalToWorld>(target.entity)) |
|||
return; |
|||
|
|||
// Look up the entity data
|
|||
LocalToWorld targetTransform |
|||
= GetComponent<LocalToWorld>(target.entity); |
|||
float3 targetPosition = targetTransform.Position; |
|||
|
|||
// Calculate the rotation
|
|||
float3 displacement = targetPosition - transform.Position; |
|||
float3 upReference = new float3(0, 1, 0); |
|||
quaternion lookRotation = |
|||
quaternion.LookRotationSafe(displacement, upReference); |
|||
|
|||
orientation.Value = |
|||
math.slerp(orientation.Value, lookRotation, deltaTime); |
|||
}) |
|||
.ScheduleParallel(); |
|||
} |
|||
} |
|||
#endregion
|
|||
#region lookup-foreach-buffer
|
|||
public struct BufferData : IBufferElementData |
|||
{ |
|||
public float Value; |
|||
} |
|||
public class BufferLookupSystem : SystemBase |
|||
{ |
|||
protected override void OnUpdate() |
|||
{ |
|||
BufferFromEntity<BufferData> buffersOfAllEntities |
|||
= this.GetBufferFromEntity<BufferData>(true); |
|||
|
|||
Entities |
|||
.ForEach((ref Rotation orientation, |
|||
in LocalToWorld transform, |
|||
in Target target) => |
|||
{ |
|||
// Check to make sure the target Entity still exists
|
|||
if (!buffersOfAllEntities.Exists(target.entity)) |
|||
return; |
|||
|
|||
// Get a reference to the buffer
|
|||
DynamicBuffer<BufferData> bufferOfOneEntity = |
|||
buffersOfAllEntities[target.entity]; |
|||
|
|||
// Use the data in the buffer
|
|||
float avg = 0; |
|||
for (var i = 0; i < bufferOfOneEntity.Length; i++) |
|||
{ |
|||
avg += bufferOfOneEntity[i].Value; |
|||
} |
|||
if (bufferOfOneEntity.Length > 0) |
|||
avg /= bufferOfOneEntity.Length; |
|||
}) |
|||
.ScheduleParallel(); |
|||
} |
|||
} |
|||
#endregion
|
|||
#region lookup-ijobchunk
|
|||
public class MoveTowardsEntitySystem : SystemBase |
|||
{ |
|||
private EntityQuery query; |
|||
|
|||
[BurstCompile] |
|||
private struct MoveTowardsJob : IJobChunk |
|||
{ |
|||
// Read-write data in the current chunk
|
|||
public ArchetypeChunkComponentType<Translation> PositionTypeAccessor; |
|||
|
|||
// Read-only data in the current chunk
|
|||
[ReadOnly] |
|||
public ArchetypeChunkComponentType<Target> TargetTypeAccessor; |
|||
|
|||
// Read-only data stored (potentially) in other chunks
|
|||
[ReadOnly] |
|||
public ComponentDataFromEntity<LocalToWorld> EntityPositions; |
|||
|
|||
// Non-entity data
|
|||
public float deltaTime; |
|||
|
|||
public void Execute(ArchetypeChunk chunk, |
|||
int chunkIndex, |
|||
int firstEntityIndex) |
|||
{ |
|||
// Get arrays of the components in chunk
|
|||
NativeArray<Translation> positions |
|||
= chunk.GetNativeArray<Translation>(PositionTypeAccessor); |
|||
NativeArray<Target> targets |
|||
= chunk.GetNativeArray<Target>(TargetTypeAccessor); |
|||
|
|||
for (int i = 0; i < positions.Length; i++) |
|||
{ |
|||
// Get the target Entity object
|
|||
Entity targetEntity = targets[i].entity; |
|||
|
|||
// Check that the target still exists
|
|||
if (!EntityPositions.Exists(targetEntity)) |
|||
continue; |
|||
|
|||
// Update translation to move the chasing enitity toward the target
|
|||
float3 targetPosition = EntityPositions[targetEntity].Position; |
|||
float3 chaserPosition = positions[i].Value; |
|||
|
|||
float3 displacement = targetPosition - chaserPosition; |
|||
positions[i] = new Translation |
|||
{ |
|||
Value = chaserPosition + displacement * deltaTime |
|||
}; |
|||
} |
|||
} |
|||
} |
|||
|
|||
protected override void OnCreate() |
|||
{ |
|||
// Select all entities that have Translation and Target Componentx
|
|||
query = this.GetEntityQuery |
|||
( |
|||
typeof(Translation), |
|||
ComponentType.ReadOnly<Target>() |
|||
); |
|||
} |
|||
|
|||
protected override void OnUpdate() |
|||
{ |
|||
// Create the job
|
|||
var job = new MoveTowardsJob(); |
|||
|
|||
// Set the chunk data accessors
|
|||
job.PositionTypeAccessor = |
|||
this.GetArchetypeChunkComponentType<Translation>(false); |
|||
job.TargetTypeAccessor = |
|||
this.GetArchetypeChunkComponentType<Target>(true); |
|||
|
|||
// Set the component data lookup field
|
|||
job.EntityPositions = this.GetComponentDataFromEntity<LocalToWorld>(true); |
|||
|
|||
// Set non-ECS data fields
|
|||
job.deltaTime = this.Time.DeltaTime; |
|||
|
|||
// Schedule the job using Dependency property
|
|||
this.Dependency = job.Schedule(query, this.Dependency); |
|||
} |
|||
} |
|||
#endregion
|
|||
|
|||
public class Snippets : SystemBase |
|||
{ |
|||
private EntityQuery query; |
|||
protected override void OnCreate() |
|||
{ |
|||
// Select all entities that have Translation and Target Componentx
|
|||
query = this.GetEntityQuery(typeof(Translation), ComponentType.ReadOnly<Target>()); |
|||
} |
|||
|
|||
[BurstCompile] |
|||
private struct ChaserSystemJob : IJobChunk |
|||
{ |
|||
// Read-write data in the current chunk
|
|||
public ArchetypeChunkComponentType<Translation> PositionTypeAccessor; |
|||
|
|||
// Read-only data in the current chunk
|
|||
[ReadOnly] |
|||
public ArchetypeChunkComponentType<Target> TargetTypeAccessor; |
|||
|
|||
// Read-only data stored (potentially) in other chunks
|
|||
#region lookup-ijobchunk-declare
|
|||
[ReadOnly] |
|||
public ComponentDataFromEntity<LocalToWorld> EntityPositions; |
|||
#endregion
|
|||
|
|||
// Non-entity data
|
|||
public float deltaTime; |
|||
|
|||
public void Execute(ArchetypeChunk chunk, int chunkIndex, int firstEntityIndex) |
|||
{ |
|||
// Get arrays of the components in chunk
|
|||
NativeArray<Translation> positions |
|||
= chunk.GetNativeArray<Translation>(PositionTypeAccessor); |
|||
NativeArray<Target> targets |
|||
= chunk.GetNativeArray<Target>(TargetTypeAccessor); |
|||
|
|||
for (int i = 0; i < positions.Length; i++) |
|||
{ |
|||
// Get the target Entity object
|
|||
Entity targetEntity = targets[i].entity; |
|||
|
|||
// Check that the target still exists
|
|||
if (!EntityPositions.Exists(targetEntity)) |
|||
continue; |
|||
|
|||
// Update translation to move the chasing enitity toward the target
|
|||
#region lookup-ijobchunk-read
|
|||
float3 targetPosition = EntityPositions[targetEntity].Position; |
|||
#endregion
|
|||
float3 chaserPosition = positions[i].Value; |
|||
|
|||
float3 displacement = targetPosition - chaserPosition; |
|||
positions[i] = new Translation { Value = chaserPosition + displacement * deltaTime }; |
|||
} |
|||
} |
|||
} |
|||
|
|||
protected override void OnUpdate() |
|||
{ |
|||
// Create the job
|
|||
#region lookup-ijobchunk-set
|
|||
var job = new ChaserSystemJob(); |
|||
job.EntityPositions = this.GetComponentDataFromEntity<LocalToWorld>(true); |
|||
#endregion
|
|||
// Set the chunk data accessors
|
|||
job.PositionTypeAccessor = this.GetArchetypeChunkComponentType<Translation>(false); |
|||
job.TargetTypeAccessor = this.GetArchetypeChunkComponentType<Target>(true); |
|||
|
|||
|
|||
// Set non-ECS data fields
|
|||
job.deltaTime = this.Time.DeltaTime; |
|||
|
|||
// Schedule the job using Dependency property
|
|||
this.Dependency = job.Schedule(query, this.Dependency); |
|||
} |
|||
} |
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: 8357f5ee6daa44877997e0632b1e5326 |
|||
MonoImporter: |
|||
externalObjects: {} |
|||
serializedVersion: 2 |
|||
defaultReferences: [] |
|||
executionOrder: 0 |
|||
icon: {instanceID: 0} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
|
|||
#region stateful-example
|
|||
using Unity.Collections; |
|||
using Unity.Entities; |
|||
using Unity.Jobs; |
|||
using UnityEngine; |
|||
|
|||
|
|||
public struct GeneralPurposeComponentA : IComponentData |
|||
{ |
|||
public bool IsAlive; |
|||
} |
|||
|
|||
public struct StateComponentB : ISystemStateComponentData |
|||
{ |
|||
public int State; |
|||
} |
|||
|
|||
public class StatefulSystem : JobComponentSystem |
|||
{ |
|||
private EntityQuery m_newEntities; |
|||
private EntityQuery m_activeEntities; |
|||
private EntityQuery m_destroyedEntities; |
|||
private EntityCommandBufferSystem m_ECBSource; |
|||
|
|||
protected override void OnCreate() |
|||
{ |
|||
// Entities with GeneralPurposeComponentA but not StateComponentB
|
|||
m_newEntities = GetEntityQuery(new EntityQueryDesc() |
|||
{ |
|||
All = new ComponentType[] {ComponentType.ReadOnly<GeneralPurposeComponentA>()}, |
|||
None = new ComponentType[] {ComponentType.ReadWrite<StateComponentB>()} |
|||
}); |
|||
|
|||
// Entities with both GeneralPurposeComponentA and StateComponentB
|
|||
m_activeEntities = GetEntityQuery(new EntityQueryDesc() |
|||
{ |
|||
All = new ComponentType[] |
|||
{ |
|||
ComponentType.ReadWrite<GeneralPurposeComponentA>(), |
|||
ComponentType.ReadOnly<StateComponentB>() |
|||
} |
|||
}); |
|||
|
|||
// Entities with StateComponentB but not GeneralPurposeComponentA
|
|||
m_destroyedEntities = GetEntityQuery(new EntityQueryDesc() |
|||
{ |
|||
All = new ComponentType[] {ComponentType.ReadWrite<StateComponentB>()}, |
|||
None = new ComponentType[] {ComponentType.ReadOnly<GeneralPurposeComponentA>()} |
|||
}); |
|||
|
|||
m_ECBSource = World.GetOrCreateSystem<EndSimulationEntityCommandBufferSystem>(); |
|||
} |
|||
|
|||
#pragma warning disable 618
|
|||
struct NewEntityJob : IJobForEachWithEntity<GeneralPurposeComponentA> |
|||
{ |
|||
public EntityCommandBuffer.Concurrent ConcurrentECB; |
|||
|
|||
public void Execute(Entity entity, int jobIndex, [ReadOnly] ref GeneralPurposeComponentA gpA) |
|||
{ |
|||
// Add an ISystemStateComponentData instance
|
|||
ConcurrentECB.AddComponent<StateComponentB>(jobIndex, entity, new StateComponentB() {State = 1}); |
|||
} |
|||
} |
|||
|
|||
struct ProcessEntityJob : IJobForEachWithEntity<GeneralPurposeComponentA> |
|||
{ |
|||
public EntityCommandBuffer.Concurrent ConcurrentECB; |
|||
|
|||
public void Execute(Entity entity, int jobIndex, ref GeneralPurposeComponentA gpA) |
|||
{ |
|||
// Process entity, possibly setting IsAlive false --
|
|||
// In which case, destroy the entity
|
|||
if (!gpA.IsAlive) |
|||
{ |
|||
ConcurrentECB.DestroyEntity(jobIndex, entity); |
|||
} |
|||
} |
|||
} |
|||
|
|||
struct CleanupEntityJob : IJobForEachWithEntity<StateComponentB> |
|||
{ |
|||
public EntityCommandBuffer.Concurrent ConcurrentECB; |
|||
|
|||
public void Execute(Entity entity, int jobIndex, [ReadOnly] ref StateComponentB state) |
|||
{ |
|||
// This system is responsible for removing any ISystemStateComponentData instances it adds
|
|||
// Otherwise, the entity is never truly destroyed.
|
|||
ConcurrentECB.RemoveComponent<StateComponentB>(jobIndex, entity); |
|||
} |
|||
} |
|||
#pragma warning restore 618
|
|||
|
|||
protected override JobHandle OnUpdate(JobHandle inputDependencies) |
|||
{ |
|||
var newEntityJob = new NewEntityJob() |
|||
{ |
|||
ConcurrentECB = m_ECBSource.CreateCommandBuffer().ToConcurrent() |
|||
}; |
|||
var newJobHandle = newEntityJob.ScheduleSingle(m_newEntities, inputDependencies); |
|||
m_ECBSource.AddJobHandleForProducer(newJobHandle); |
|||
|
|||
var processEntityJob = new ProcessEntityJob() |
|||
{ConcurrentECB = m_ECBSource.CreateCommandBuffer().ToConcurrent()}; |
|||
var processJobHandle = processEntityJob.Schedule(m_activeEntities, newJobHandle); |
|||
m_ECBSource.AddJobHandleForProducer(processJobHandle); |
|||
|
|||
var cleanupEntityJob = new CleanupEntityJob() |
|||
{ |
|||
ConcurrentECB = m_ECBSource.CreateCommandBuffer().ToConcurrent() |
|||
}; |
|||
var cleanupJobHandle = cleanupEntityJob.ScheduleSingle(m_destroyedEntities, processJobHandle); |
|||
m_ECBSource.AddJobHandleForProducer(cleanupJobHandle); |
|||
|
|||
return cleanupJobHandle; |
|||
} |
|||
|
|||
protected override void OnDestroy() |
|||
{ |
|||
// Implement OnDestroy to cleanup any resources allocated by this system.
|
|||
// (This simplified example does not allocate any resources.)
|
|||
} |
|||
} |
|||
#endregion
|
|
|||
fileFormatVersion: 2 |
|||
guid: bd95ce1a50eba41a3a6d9018621ae975 |
|||
MonoImporter: |
|||
externalObjects: {} |
|||
serializedVersion: 2 |
|||
defaultReferences: [] |
|||
executionOrder: 0 |
|||
icon: {instanceID: 0} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
|
|||
using Unity.Entities.UniversalDelegates; |
|||
|
|||
namespace Doc.CodeSamples.SyBase.Tests |
|||
{ |
|||
using Unity.Entities; |
|||
using Unity.Jobs; |
|||
using Unity.Transforms; |
|||
using Unity.Collections; |
|||
using System.Collections.Generic; |
|||
using UnityEngine; |
|||
using Unity.Mathematics; |
|||
|
|||
#region basic-system
|
|||
|
|||
public struct Position : IComponentData |
|||
{ |
|||
public float3 Value; |
|||
} |
|||
|
|||
public struct Velocity : IComponentData |
|||
{ |
|||
public float3 Value; |
|||
} |
|||
|
|||
public class ECSSystem : SystemBase |
|||
{ |
|||
protected override void OnUpdate() |
|||
{ |
|||
// Local variable captured in ForEach
|
|||
float dT = Time.DeltaTime; |
|||
|
|||
Entities |
|||
.WithName("Update_Displacement") |
|||
.ForEach( |
|||
(ref Position position, in Velocity velocity) => |
|||
{ |
|||
position = new Position() |
|||
{ |
|||
Value = position.Value + velocity.Value * dT |
|||
}; |
|||
} |
|||
) |
|||
.ScheduleParallel(); |
|||
} |
|||
} |
|||
|
|||
#endregion
|
|||
|
|||
public class EntitiesBasicExample : SystemBase |
|||
{ |
|||
protected override void OnUpdate() |
|||
{ |
|||
float dT = Time.DeltaTime; // Captured variable
|
|||
|
|||
#region entities-foreach-basic
|
|||
|
|||
Entities |
|||
.WithName("Update_Position") // Shown in error messages and profiler
|
|||
.WithAll<LocalToWorld>() // Require the LocalToWorld component
|
|||
.ForEach( |
|||
// Write to Displacement (ref), read Velocity (in)
|
|||
(ref Position position, in Velocity velocity) => |
|||
{ |
|||
//Execute for each selected entity
|
|||
position = new Position() |
|||
{ |
|||
// dT is a captured variable
|
|||
Value = position.Value + velocity.Value * dT |
|||
}; |
|||
} |
|||
) |
|||
.ScheduleParallel(); // Schedule as a parallel job
|
|||
|
|||
#endregion
|
|||
} |
|||
} |
|||
|
|||
#region basic-job
|
|||
|
|||
public class JobSystem : SystemBase |
|||
{ |
|||
NativeArray<int> EndlessSequence; |
|||
|
|||
protected override void OnUpdate() |
|||
{ |
|||
NativeArray<int> sequence = EndlessSequence; // Can only capture local variables
|
|||
if (!sequence.IsCreated) |
|||
{ |
|||
EndlessSequence = new NativeArray<int>(1000, Allocator.Persistent); |
|||
sequence[sequence.Length - 2] = 1; |
|||
sequence[sequence.Length - 1] = 1; |
|||
} |
|||
Job |
|||
.WithName("Fibonacci_Job") |
|||
.WithCode(() => |
|||
{ |
|||
sequence[0] = sequence[sequence.Length - 2]; |
|||
sequence[1] = sequence[sequence.Length - 1]; |
|||
for (int i = 3; i < sequence.Length; i++) |
|||
{ |
|||
sequence[i] = sequence[i - 1] + sequence[i - 2]; |
|||
} |
|||
}) |
|||
.Schedule(); |
|||
} |
|||
|
|||
protected override void OnDestroy() |
|||
{ |
|||
if (EndlessSequence.IsCreated) |
|||
EndlessSequence.Dispose(); |
|||
} |
|||
} |
|||
|
|||
#endregion
|
|||
|
|||
public struct WritableComponent : IComponentData |
|||
{ |
|||
} |
|||
|
|||
public struct ReadonlyComponent : IComponentData |
|||
{ |
|||
} |
|||
|
|||
public class LambdaParamsEx : SystemBase |
|||
{ |
|||
protected override void OnUpdate() |
|||
{ |
|||
Entities |
|||
#region lambda-params
|
|||
.ForEach((Entity entity, |
|||
int entityInQueryIndex, |
|||
ref WritableComponent aReadwriteComponent, |
|||
in ReadonlyComponent aReadonlyComponent) => |
|||
{ |
|||
/*..*/ |
|||
}) |
|||
#endregion
|
|||
.ScheduleParallel(); |
|||
} |
|||
} |
|||
|
|||
public struct AComponent : IComponentData |
|||
{ |
|||
} |
|||
|
|||
public struct AnotherComponent : IComponentData |
|||
{ |
|||
} |
|||
|
|||
public class SimpleDependencyManagement : SystemBase |
|||
{ |
|||
#region simple-dependency
|
|||
|
|||
protected override void OnUpdate() |
|||
{ |
|||
Entities |
|||
.WithName("ForEach_Job_One") |
|||
.ForEach((ref AComponent c) => |
|||
{ |
|||
/*...*/ |
|||
}) |
|||
.ScheduleParallel(); |
|||
|
|||
Entities |
|||
.WithName("ForEach_Job_Two") |
|||
.ForEach((ref AnotherComponent c) => |
|||
{ |
|||
/*...*/ |
|||
}) |
|||
.ScheduleParallel(); |
|||
|
|||
Job |
|||
.WithName("Job_Three") |
|||
.WithCode(() => |
|||
{ |
|||
/*...*/ |
|||
}) |
|||
.Schedule(); |
|||
} |
|||
|
|||
#endregion
|
|||
} |
|||
|
|||
public class ManualDependencyManagement : SystemBase |
|||
{ |
|||
#region manual-dependency
|
|||
|
|||
protected override void OnUpdate() |
|||
{ |
|||
JobHandle One = Entities |
|||
.WithName("ForEach_Job_One") |
|||
.ForEach((ref AComponent c) => |
|||
{ |
|||
/*...*/ |
|||
}) |
|||
.ScheduleParallel(this.Dependency); |
|||
|
|||
JobHandle Two = Entities |
|||
.WithName("ForEach_Job_Two") |
|||
.ForEach((ref AnotherComponent c) => |
|||
{ |
|||
/*...*/ |
|||
}) |
|||
.ScheduleParallel(this.Dependency); |
|||
|
|||
JobHandle intermediateDependencies = |
|||
JobHandle.CombineDependencies(One, Two); |
|||
|
|||
JobHandle finalDependency = Job |
|||
.WithName("Job_Three") |
|||
.WithCode(() => |
|||
{ |
|||
/*...*/ |
|||
}) |
|||
.Schedule(intermediateDependencies); |
|||
|
|||
this.Dependency = finalDependency; |
|||
} |
|||
|
|||
#endregion
|
|||
} |
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: d99ce6d333db341f89827e85af7f0169 |
|||
MonoImporter: |
|||
externalObjects: {} |
|||
serializedVersion: 2 |
|||
defaultReferences: [] |
|||
executionOrder: 0 |
|||
icon: {instanceID: 0} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
|
|||
* [Overview](index.md) |
|||
* [Core ECS](ecs_core.md) |
|||
* [Entities](ecs_entities.md) |
|||
* [Entity Queries](ecs_entity_query.md) |
|||
* [Worlds](world.md) |
|||
* [Components](ecs_components.md) |
|||
* [General purpose components](component_data.md) |
|||
* [Shared components](shared_component_data.md) |
|||
* [System state components](system_state_components.md) |
|||
* [Dynamic buffer components](dynamic_buffers.md) |
|||
* [Chunk components](ecs_chunk_component.md) |
|||
* [Systems](ecs_systems.md) |
|||
* [Creating systems](ecs_creating_systems.md) |
|||
* [Using Entities.ForEach](ecs_entities_foreach.md) |
|||
* [Using Job.WithCode](ecs_job_withcode.md) |
|||
* [Using IJobChunk](chunk_iteration_job.md) |
|||
* [Manual iteration](manual_iteration.md) |
|||
* [System Update Order](system_update_order.md) |
|||
* [ECS Job dependencies](ecs_job_dependencies.md) |
|||
* [Looking up data](ecs_lookup_data.md) |
|||
* [Entity Command Buffers](entity_command_buffer.md) |
|||
* [Sync points and structural changes](sync_points.md) |
|||
* [Component WriteGroups](ecs_write_groups.md) |
|||
* [Versions and Generations](version_numbers.md) |
|||
* [C# Job System extensions](ecs_job_extensions.md) |
|||
* [Creating Gameplay](gp_overview.md) |
|||
* [Transforms](transform_system.md) |
|||
* [Rendering](gp_rendering.md) |
|
|||
--- |
|||
uid: ecs-iteration |
|||
--- |
|||
# Accessing entity data |
|||
|
|||
Iterating over your data is one of the most common tasks you need to perform when you implement ECS systems. The ECS systems typically process a set of entities, reads data from one or more components, performs a calculation, and then writes the result to another component. |
|||
|
|||
The most efficient way to iterate over entities and components is in a parallelizable job that processes the components in order. This takes advantage of the processing power from all available cores and data locality to avoid CPU cache misses. |
|||
|
|||
The ECS API provides a number of ways to accomplish iteration, each with its own performance implications and restrictions. You can iterate over ECS data in the following ways: |
|||
|
|||
* [SystemBase.Entities.ForEach] — the simplest efficient way to process component data entity by entity. |
|||
|
|||
* [IJobChunk] — iterates over the eligible blocks of memory (called a **[chunk]**) that contain matching entities. The job `Execute()` function can use a for loop to iterate over the elements inside each chunk. You can use [IJobChunk] for more complex situations than [Entities.ForEach] supports, while maintaining maximum efficiency. |
|||
|
|||
* [Manual iteration] — if the previous methods are insufficient, you can manually iterate over entities or Chunks. For example, you can use a job such as `IJobParallelFor` to get a `NativeArray` that contains entities or the Chunks of the entities that you want to process and iterate over. |
|||
|
|||
The [EntityQuery] class provides a way to construct a view of your data that contains only the specific data you need for a given algorithm or process. Many of the iteration methods in the list above use an [EntityQuery], either explicitly or internally. |
|||
|
|||
**Important:** The following iteration types should not be used in new code: |
|||
|
|||
* IJobForEach |
|||
* IJobForEachWithEntity |
|||
* ComponentSystem |
|||
* JobComponentSystem |
|||
|
|||
These types are being phased out in preference to [SystemBase] and will become obsolete once they have gone through a deprecation cycle. Use [SystemBase] and [SystemBase.Entities.ForEach] or [IJobChunk] to replace them. |
|||
|
|||
|
|||
[SystemBase]: entities_job_foreach.md |
|||
[Entities.ForEach]: entities_job_foreach.md |
|||
[SystemBase.Entities.ForEach]: entities_job_foreach.md |
|||
[IJobChunk]: chunk_iteration_job.md |
|||
[EntityQuery]: ecs_entity_query.md |
|||
[Chunk]: xref:Unity.Entities.ArchetypeChunk |
|
|||
--- |
|||
uid: ecs-ijobchunk |
|||
--- |
|||
|
|||
# Using IJobChunk jobs |
|||
|
|||
You can implement [IJobChunk](xref:Unity.Entities.IJobChunk) inside a system to iterate through your data by chunk. When you schedule an IJobChunk job in the `OnUpdate()` function of a system, the job invokes your `Execute()` function once for each chunk that matches the entity query passed to the job's `Schedule()` method. You can then iterate over the data inside each chunk, entity by entity. |
|||
|
|||
Iterating with IJobChunk requires more code setup than does Entities.ForEach, but is also more explicit and represents the most direct access to the data, as it is actually stored. |
|||
|
|||
Another benefit of iterating by chunks is that you can check whether an optional component is present in each chunk with `Archetype.Has<T>()`, and then process all of the entities in the chunk accordingly. |
|||
|
|||
To implement an IJobChunk job, use the following steps: |
|||
|
|||
1. Create an `EntityQuery` to identify the entities that you want to process. |
|||
2. Define the job struct, and include fields for `ArchetypeChunkComponentType` objects that identify the types of components the job must directly access. Also, specify whether the job reads or writes to those components. |
|||
3. Instantiate the job struct and schedule the job in the system `OnUpdate()` function. |
|||
4. In the `Execute()` function, get the `NativeArray` instances for the components the job reads or writes and then iterate over the current chunk to perform the desired work. |
|||
|
|||
For more information, the [ECS samples repository](https://github.com/Unity-Technologies/EntityComponentSystemSamples) contains a simple HelloCube example that demonstrates how to use `IJobChunk`. |
|||
|
|||
<a name="query"></a> |
|||
|
|||
## Query for data with a EntityQuery |
|||
|
|||
An EntityQuery defines the set of component types that an archetype must contain for the system to process its associated chunks and entities. An archetype can have additional components, but it must have at least those that the EntityQuery defines. You can also exclude archetypes that contain specific types of components. |
|||
|
|||
For simple queries, you can use the `SystemBase.GetEntityQuery()` function and pass in the component types as follows: |
|||
|
|||
[!code-cs[system](../package/DocCodeSamples.Tests/ChunkIterationJob.cs#rotationspeedsystem)] |
|||
|
|||
For more complex situations, you can use an `EntityQueryDesc`. An `EntityQueryDesc` provides a flexible query mechanism to specify the component types: |
|||
|
|||
* `All`: All component types in this array must exist in the archetype |
|||
* `Any`: At least one of the component types in this array must exist in the archetype |
|||
* `None`: None of the component types in this array can exist in the archetype |
|||
|
|||
For example, the following query includes archetypes that contain the `RotationQuaternion` and `RotationSpeed` components, but excludes any archetypes that contain the `Frozen` component: |
|||
|
|||
[!code-cs[oncreate2](../package/DocCodeSamples.Tests/ChunkIterationJob.cs#oncreate2)] |
|||
|
|||
The query uses `ComponentType.ReadOnly<T>` instead of the simpler `typeof` expression to designate that the system does not write to `RotationSpeed`. |
|||
|
|||
You can also combine multiple queries. To do this, pass an array of `EntityQueryDesc` objects rather than a single instance. ECS uses a logical OR operation to combine each query. The following example selects any archetypes that contain a `RotationQuaternion` component or a `RotationSpeed` component (or both): |
|||
|
|||
[!code-cs[oncreate3](../package/DocCodeSamples.Tests/ChunkIterationJob.cs#oncreate3)] |
|||
|
|||
**Note:** Do not include completely optional components in the `EntityQueryDesc`. To handle optional components, use the `chunk.Has<T>()` method inside `IJobChunk.Execute()` to determine whether the current ArchetypeChunk has the optional component or not. Because all entities in the same chunk have the same components, you only need to check whether an optional component exists once per chunk: not once per entity. |
|||
|
|||
For efficiency and to avoid needless creation of garbage-collected reference types, you should create the `EntityQueries` for a system in the system’s `OnCreate()` function and store the result in an instance variable. (In the above examples, the `m_Query` variable is used for this purpose.) |
|||
|
|||
<a name="define-job-struct"></a> |
|||
|
|||
## Define the IJobChunk struct |
|||
|
|||
The IJobChunk struct defines fields for the data the job needs when it runs, as well as the job’s `Execute()` method. |
|||
|
|||
To access the component arrays inside of the chunks that the system passes to your `Execute()` method, you must create an `ArchetypeChunkComponentType<T>` object for each type of component that the job reads or writes to. You can use these objects to get instances of the `NativeArray`s that provide access to the components of an entity. Include all of the components referenced in the job’s EntityQuery that the `Execute()` method reads or writes. You can also provide `ArchetypeChunkComponentType` variables for optional component types that you do not include in the EntityQuery. |
|||
|
|||
You must check to make sure that the current chunk has an optional component before you try to access it. For example, the HelloCube IJobChunk example declares a job struct that defines `ArchetypeChunkComponentType<T>` variables for two components; `RotationQuaternion` and `RotationSpeed`: |
|||
|
|||
[!code-cs[speedjob](../package/DocCodeSamples.Tests/ChunkIterationJob.cs#speedjob)] |
|||
|
|||
The system assigns values to these variables in the `OnUpdate()` function. ECS uses the variables inside the `Execute()` method when it runs the job. |
|||
|
|||
The job also uses the Unity delta time to animate the rotation of a 3D object. The example uses a struct field to pass this value to the `Execute()` method. |
|||
|
|||
<a name="execute"></a> |
|||
|
|||
## Writing the Execute method |
|||
|
|||
The signature of the IJobChunk `Execute()` method is: |
|||
|
|||
[!code-cs[speedjob](../package/DocCodeSamples.Tests/ChunkIterationJob.cs#execsignature)] |
|||
|
|||
The `chunk` parameter is a handle to the block of memory that contains the entities and components that this iteration of the job has to process. Because a chunk can only contain a single archetype, all of the entities in a chunk have the same set of components. |
|||
|
|||
Use the `chunk` parameter to get the NativeArray instances for components: |
|||
|
|||
[!code-cs[getcomponents](../package/DocCodeSamples.Tests/ChunkIterationJob.cs#getcomponents)] |
|||
|
|||
These arrays are aligned so that an entity has the same index in all of them. You can then use a normal for loop to iterate through the component arrays. Use `chunk.Count` to get the number of entities stored in the current chunk: |
|||
|
|||
[!code-cs[chunkiteration](../package/DocCodeSamples.Tests/ChunkIterationJob.cs#chunkiteration)] |
|||
|
|||
If you have the `Any` filter in your EntityQueryDesc or have completely optional components that don’t appear in the query at all, you can use the `ArchetypeChunk.Has<T>()` function to test whether the current chunk contains one of those components before you use it: |
|||
|
|||
if (chunk.Has<OptionalComp>(OptionalCompType)) |
|||
{//...} |
|||
|
|||
__Note:__ If you use a concurrent entity command buffer, pass the `chunkIndex` argument as the `jobIndex` parameter to the command buffer functions. |
|||
|
|||
<a name="filtering"></a> |
|||
|
|||
## Skipping chunks with unchanged entities |
|||
|
|||
If you only need to update entities when a component value has changed, you can add that component type to the change filter of the EntityQuery that selects the entities and chunks for the job. For example, if you have a system that reads two components and only needs to update a third when one of the first two has changed, you can use a EntityQuery as follows: |
|||
|
|||
[!code-cs[changefilter](../package/DocCodeSamples.Tests/ChunkIterationJob.cs#changefilter)] |
|||
|
|||
The EntityQuery change filter supports up to two components. If you want to check more or you aren't using a EntityQuery, you can make the check manually. To make this check, use the `ArchetypeChunk.DidChange()` function to compare the chunk’s change version for the component to the system's `LastSystemVersion`. If this function returns false, you can skip the current chunk altogether because none of the components of that type have changed since the last time the system ran. |
|||
|
|||
You must use a struct field to pass the `LastSystemVersion` from the system into the job, as follows: |
|||
|
|||
[!code-cs[changefilterjobstruct](../package/DocCodeSamples.Tests/ChunkIterationJob.cs#changefilterjobstruct)] |
|||
|
|||
As with all the job struct fields, you must assign its value before you schedule the job: |
|||
|
|||
[!code-cs[changefilteronupdate](../package/DocCodeSamples.Tests/ChunkIterationJob.cs#changefilteronupdate)] |
|||
|
|||
**Note:** For efficiency, the change version applies to whole chunks not individual entities. If another job which has the ability to write to that type of component accesses a chunk, then ECS increments the change version for that component and the `DidChange()` function returns true. ECS increments the change version even if the job that declares write access to a component does not actually change the component value. |
|||
|
|||
<a name="schedule"></a> |
|||
## Instantiate and schedule the job |
|||
|
|||
To run an IJobChunk job, you must create an instance of your job struct, setting the struct fields, and then schedule the job. When you do this in the `OnUpdate()` function of a SystemBase implementation, the system schedules the job to run every frame. |
|||
|
|||
[!code-cs[schedulequery](../package/DocCodeSamples.Tests/ChunkIterationJob.cs#schedulequery)] |
|||
|
|||
When you call the `GetArchetypeChunkComponentType<T>()` function to set your component type variables, make sure that you set the `isReadOnly` parameter to true for components that the job reads, but doesn’t write. Setting these parameters correctly can have a significant impact on how efficiently the ECS framework can schedule your jobs. These access mode settings must match their equivalents in both the struct definition, and the EntityQuery. |
|||
|
|||
Do not cache the return value of `GetArchetypeChunkComponentType<T>()` in a system class variable. You must call the function every time the system runs, and pass the updated value to the job. |
|
|||
--- |
|||
uid: ecs-component-data |
|||
--- |
|||
|
|||
# General purpose components |
|||
|
|||
`ComponentData` in Unity (also known as a component in standard ECS terms) is a struct that contains only the instance data for an [entity](entities.md). `ComponentData` should not contain methods beyond utility functions to access the data in the struct. You should implement all of your game logic and behavior in systems. To put this in terms of the object-oriented Unity system, this is somewhat similar to a Component class, but one that **only contains variables**. |
|||
|
|||
The Unity ECS API provides an interface called [IComponentData](xref:Unity.Entities.IComponentData) that you can implement in your code to declare a general-purpose component type. |
|||
|
|||
## IComponentData |
|||
|
|||
Traditional Unity components (including `MonoBehaviour`) are [object-oriented](https://en.wikipedia.org/wiki/Object-oriented_programming) classes that contain data and methods for behavior. `IComponentData` is a pure ECS-style component, which means that it defines no behavior, only data. You should implement `IComponentData` as struct rather than a class, which means that it is copied [by value instead of by reference](https://stackoverflow.com/questions/373419/whats-the-difference-between-passing-by-reference-vs-passing-by-value?answertab=votes#tab-top) by default. You usually need to use the following pattern to modify data: |
|||
|
|||
```c# |
|||
var transform = group.transform[index]; // Read |
|||
|
|||
transform.heading = playerInput.move; // Modify |
|||
transform.position += deltaTime * playerInput.move * settings.playerMoveSpeed; |
|||
|
|||
group.transform[index] = transform; // Write |
|||
``` |
|||
|
|||
`IComponentData` structs must not contain references to managed objects. This is because `ComponentData` lives in simple non-garbage-collected tracked [Chunk memory](chunk_iteration.md), which has many performance advantages. |
|||
|
|||
### Managed IComponentData |
|||
|
|||
It is helpful to use a managed `IComponentData` (that is, `IComponentData` declared using a `class` rather than `struct`) to help port existing code over to ECS in a piecemeal fashion, interoperate with managed data not suitable in `ISharedComponentData`, or to prototype a data layout. |
|||
|
|||
These components are used the same way as value type `IComponentData`. However, ECS handles them internally in a much different (and slower) way. If you don't need managed component support, define `UNITY_DISABLE_MANAGED_COMPONENTS` in your application's __Player Settings__ (menu: __Edit > Project Settings > Player > Scripting Define Symbols__) to prevent accidental usage. |
|||
|
|||
Because managed `IComponentData` is a managed type, it has the following performance drawbacks compared to valuetype `IComponentData`: |
|||
* It cannot be used with the Burst Compiler |
|||
* It cannot be used in job structs |
|||
* It cannot use [Chunk memory](chunk_iteration.md) |
|||
* It requires garbage collection |
|||
|
|||
You should try to limit the number of managed components, and use blittable types as much as possible. |
|||
|
|||
Managed `IComponentData` must implement the `IEquatable<T>` interface and override for `Object.GetHashCode()`. Additionally, for serialization purposes, managed components must be default constructible. |
|||
|
|||
You must set the value of the component on the main thread. To do this, use either the `EntityManager` or `EntityCommandBuffer`. Because a component is a reference type, you can change the value of the component without moving entities across Chunks, unlike [ISharedComponentData](xref:Unity.Entities.ISharedComponentData). This does not create a sync-point. |
|||
|
|||
However, while managed components are logically stored separate from value-type components, they still contribute to an entity's `EntityArchetype` definition. As such, adding a new managed component to an entity still causes ECS to create a new archetype (if a matching archetype doesn't exist already) and it moves the entity to a new Chunk. |
|||
|
|||
For an example, see the file: `/Packages/com.unity.entities/Unity.Entities/IComponentData.cs`. |
|
|||
--- |
|||
uid: ecs-custom-job-types |
|||
--- |
|||
# Custom job types |
|||
|
|||
On the lowest level of the job system, jobs are scheduled by calling one of the `Schedule` functions in [JobsUtility](https://docs.unity3d.com/ScriptReference/Unity.Jobs.LowLevel.Unsafe.JobsUtility.html). The currently existing [job types](https://docs.unity3d.com/ScriptReference/Unity.Jobs.LowLevel.Unsafe.JobType.html) all use these functions, but it is also possible to create specialized job types using the same APIs. |
|||
|
|||
These APIs use unsafe code and have to be crafted carefully, since they can easily introduce unwanted race conditions. If you add your own job types, we strongly recommend to aim for full test coverage. |
|||
|
|||
As an example we have a custom job type `IJobParallelForBatch` (see file: _/Packages/com.unity.jobs/Unity.Jobs/IJobParallelForBatch.cs_). |
|||
|
|||
It works like __IJobParallelFor__, but instead of calling a single execute function per index it calls one execute function per batch being executed. This is useful if you need to do something on more than one item at a time, but still want to do it in parallel. A common scenario for this job type is if you need to create a temporary array and you want to avoid creating each item in the array one at a time. By using IJobParallelFor you can instead create one temporary array per batch. |
|||
|
|||
In the IJobParallelForBatch example, the entry point where the job is actually scheduled looks like this: |
|||
|
|||
```c# |
|||
unsafe static public JobHandle ScheduleBatch<T>(this T jobData, int arrayLength, int minIndicesPerJobCount, JobHandle dependsOn = new JobHandle()) where T : struct, IJobParallelForBatch |
|||
{ |
|||
var scheduleParams = new JobsUtility.JobScheduleParameters(UnsafeUtility.AddressOf(ref jobData), ParallelForBatchJobStruct<T>.Initialize(), dependsOn, ScheduleMode.Batched); |
|||
return JobsUtility.ScheduleParallelFor(ref scheduleParams, arrayLength, minIndicesPerJobCount); |
|||
} |
|||
``` |
|||
|
|||
The first line creates a struct containing the scheduling parameters. When creating it you need to set a pointer to the data which will be copied to the jobs. The reason this is a pointer is that the native code which uses it does not know about the type. |
|||
You also need to pass it a pointer to the __JobReflectionData__ created by calling: |
|||
|
|||
```c# |
|||
JobsUtility.CreateJobReflectionData(typeof(T), JobType.ParallelFor, (ExecuteJobFunction)Execute); |
|||
``` |
|||
|
|||
JobReflection stores information about the struct with the data for the job, such as which __NativeContainers__ it has and how they need to be patched when scheduling a job. It lives on the native side of the engine and the managed code only has access to it though pointers without any information about what the type is. When creating JobReflectionData you need to specify the type of the struct implementing the job, the __JobType__ and the method which will be called to execute the job. The JobReflectionData does not depend on the data in the struct you schedule, only its type, so it should only be created once for all jobs implementing the same interface. There are currently only two job types, __Single__ and __ParallelFor__. Single means the job will only get a single call, ParallelFor means there will be multiple calls to process it; where each call is restricted to a subset of the range of indices to process. Which job type you choose affects which schedule function you are allowed to call. |
|||
|
|||
The third parameter of __JobsUtility.JobScheduleParameters__ is the __JobHandle__ that the scheduled job should depend on. |
|||
|
|||
The final parameter is the schedule mode. There are two scheduling modes to choose from, __Run__ and __Batched__. Batched means one or more jobs will be scheduled to do the processing, while Run means the processing will be done on the main thread before Schedule returns. |
|||
|
|||
Once the schedule parameters are created we actually schedule the job. There are three ways to schedule jobs depending on their type: |
|||
|
|||
```c# |
|||
JobHandle Schedule(ref JobScheduleParameters parameters); |
|||
JobHandle ScheduleParallelFor(ref JobScheduleParameters parameters, int arrayLength, int innerLoopBatchCount); |
|||
JobHandle ScheduleParallelForTransform(ref JobScheduleParameters parameters, IntPtr transfromAccessArray); |
|||
|
|||
``` |
|||
|
|||
Schedule can only be used if the __ScheduleParameters__ are created with __JobType.Single__, the other two schedule functions require __JobType.ParallelFor__. |
|||
The __arrayLength__ and __innerLoopBatchCount__ parameter passed to __ScheduleParallelFor__ are used to determine how many indices the jobs should process and how many indices it should handle in the inner loop (see the section on [Execution and JobRanges](#execution-and-jobranges) for more information on the inner loop count). |
|||
__ScheduleParallelForTransform__ is similar to ScheduleParallelFor, but it also has access to a __TransformAccessArray__ that allows you to modify __Transform__ components on __GameObjects__. The number of indices and batch size is inferred from the TransformAccessArray. |
|||
|
|||
## Execution and JobRanges |
|||
|
|||
After scheduling the job, Unity will call the entry point you specified directly from the native side. It works in a similar way to how __Update__ is called on MonoBehaviours, but from inside a job instead. You only get one call per job and there is either one job, or one job per worker thread; in the case of ParallelFor. |
|||
|
|||
The signature used for Execute is |
|||
|
|||
```c# |
|||
public delegate void ExecuteJobFunction(ref T data, IntPtr additionalPtr, IntPtr bufferRangePatchData, ref JobRanges ranges, int jobIndex); |
|||
``` |
|||
For Single jobs, only the data is needed and you can just do your processing right away, but for ParallelFor jobs it requires some more work before you can start processing indices. We need to split up the indices into a number of sequential sub-sets that each job will process in parallel. This way we do not process the same thing twice and we are sure that everything gets covered. The memory layout will determine the order of indices. |
|||
|
|||
The JobRanges contain the batches and indices a ParallelFor job is supposed to process. The indices are split into batches based on the batch size, the batches are evenly distributed between the jobs doing the execution in such a way that each job can iterate over a continuous section of memory. The ParallelFor job should call: |
|||
|
|||
```c# |
|||
JobsUtility.GetWorkStealingRange(ref ranges, jobIndex, out begin, out end) |
|||
``` |
|||
|
|||
This continues until it returns `false`, and after calling it process all items with index between __begin__ and __end__. |
|||
The reason you get batches of items, rather than the full set of items the job should process, is that Unity will apply [work stealing](https://en.wikipedia.org/wiki/Work_stealing) if one job completes before the others. Work stealing in this context means that when one job is done it will look at the other jobs running and see if any of them still have a lot of work left. If it finds a job which is not complete it will steal some of the batches that it has not yet started; to dynamically redistribute the work. |
|||
|
|||
Before a ParallelFor job starts processing items it also needs to limit the write access to NativeContainers on the range of items which the job is processing. If it does not do this several jobs can potentially write to the same index which leads to race conditions. The NativeContainers that need to be limited is passed to the job and there is a function to patch them; so they cannot access items outside the correct range. The code to do it looks like this: |
|||
|
|||
```c# |
|||
#if ENABLE_UNITY_COLLECTIONS_CHECKS |
|||
JobsUtility.PatchBufferMinMaxRanges(bufferRangePatchData, UnsafeUtility.AddressOf(ref jobData), begin, end - begin); |
|||
#endif |
|||
``` |
|||
|
|||
# Custom NativeContainers |
|||
|
|||
When writing jobs, the data communication between jobs is one of the hardest parts to get right. Just using __NativeArray__ is very limiting. Using __NativeQueue__, __NativeHashMap__ and __NativeMultiHashMap__ and their __Concurrent__ versions solves most scenarios. |
|||
|
|||
For the remaining scenarios it is possible to write your own custom NativeContainers. |
|||
When writing custom containers for [thread synchronization](https://en.wikipedia.org/wiki/Synchronization_(computer_science)#Thread_or_process_synchronization) it is very important to write correct code. We strongly recommend full test coverage for any new containers you add. |
|||
|
|||
As a very simple example of this we will create a __NativeCounter__ that can be incremented in a ParallelFor job through __NativeCounter.Concurrent__ and read in a later job or on the main thread. |
|||
|
|||
Let's start with the basic container type: |
|||
|
|||
```c# |
|||
// Mark this struct as a NativeContainer, usually this would be a generic struct for containers, but a counter does not need to be generic |
|||
// TODO - why does a counter not need to be generic? - explain the argument for this reasoning please. |
|||
[StructLayout(LayoutKind.Sequential)] |
|||
[NativeContainer] |
|||
unsafe public struct NativeCounter |
|||
{ |
|||
// The actual pointer to the allocated count needs to have restrictions relaxed so jobs can be schedled with this container |
|||
[NativeDisableUnsafePtrRestriction] |
|||
int* m_Counter; |
|||
|
|||
#if ENABLE_UNITY_COLLECTIONS_CHECKS |
|||
AtomicSafetyHandle m_Safety; |
|||
// The dispose sentinel tracks memory leaks. It is a managed type so it is cleared to null when scheduling a job |
|||
// The job cannot dispose the container, and no one else can dispose it until the job has run, so it is ok to not pass it along |
|||
// This attribute is required, without it this NativeContainer cannot be passed to a job; since that would give the job access to a managed object |
|||
[NativeSetClassTypeToNullOnSchedule] |
|||
DisposeSentinel m_DisposeSentinel; |
|||
#endif |
|||
|
|||
// Keep track of where the memory for this was allocated |
|||
Allocator m_AllocatorLabel; |
|||
|
|||
public NativeCounter(Allocator label) |
|||
{ |
|||
// This check is redundant since we always use an int that is blittable. |
|||
// It is here as an example of how to check for type correctness for generic types. |
|||
#if ENABLE_UNITY_COLLECTIONS_CHECKS |
|||
if (!UnsafeUtility.IsBlittable<int>()) |
|||
throw new ArgumentException(string.Format("{0} used in NativeQueue<{0}> must be blittable", typeof(int))); |
|||
#endif |
|||
m_AllocatorLabel = label; |
|||
|
|||
// Allocate native memory for a single integer |
|||
m_Counter = (int*)UnsafeUtility.Malloc(UnsafeUtility.SizeOf<int>(), 4, label); |
|||
|
|||
// Create a dispose sentinel to track memory leaks. This also creates the AtomicSafetyHandle |
|||
#if ENABLE_UNITY_COLLECTIONS_CHECKS |
|||
DisposeSentinel.Create(out m_Safety, out m_DisposeSentinel, 0); |
|||
#endif |
|||
// Initialize the count to 0 to avoid uninitialized data |
|||
Count = 0; |
|||
} |
|||
|
|||
public void Increment() |
|||
{ |
|||
// Verify that the caller has write permission on this data. |
|||
// This is the race condition protection, without these checks the AtomicSafetyHandle is useless |
|||
#if ENABLE_UNITY_COLLECTIONS_CHECKS |
|||
AtomicSafetyHandle.CheckWriteAndThrow(m_Safety); |
|||
#endif |
|||
(*m_Counter)++; |
|||
} |
|||
|
|||
public int Count |
|||
{ |
|||
get |
|||
{ |
|||
// Verify that the caller has read permission on this data. |
|||
// This is the race condition protection, without these checks the AtomicSafetyHandle is useless |
|||
#if ENABLE_UNITY_COLLECTIONS_CHECKS |
|||
AtomicSafetyHandle.CheckReadAndThrow(m_Safety); |
|||
#endif |
|||
return *m_Counter; |
|||
} |
|||
set |
|||
{ |
|||
// Verify that the caller has write permission on this data. This is the race condition protection, without these checks the AtomicSafetyHandle is useless |
|||
#if ENABLE_UNITY_COLLECTIONS_CHECKS |
|||
AtomicSafetyHandle.CheckWriteAndThrow(m_Safety); |
|||
#endif |
|||
*m_Counter = value; |
|||
} |
|||
} |
|||
|
|||
public bool IsCreated |
|||
{ |
|||
get { return m_Counter != null; } |
|||
} |
|||
|
|||
public void Dispose() |
|||
{ |
|||
// Let the dispose sentinel know that the data has been freed so it does not report any memory leaks |
|||
#if ENABLE_UNITY_COLLECTIONS_CHECKS |
|||
DisposeSentinel.Dispose(m_Safety, ref m_DisposeSentinel); |
|||
#endif |
|||
|
|||
UnsafeUtility.Free(m_Counter, m_AllocatorLabel); |
|||
m_Counter = null; |
|||
} |
|||
} |
|||
``` |
|||
|
|||
With this we have a simple NativeContainer where we can get, set, and increment the count. This container can be passed to a job, but it has the same restrictions as NativeArray, which means it cannot be passed to a ParallelFor job with write access. |
|||
|
|||
The next step is to make it usable in ParallelFor. In order to avoid race conditions we want to make sure no-one else is reading it while the ParallelFor is writing to it. To achieve this we create a separate inner struct called Concurrent that can handle multiple writers, but no readers. We make sure NativeCounter.Concurrent can be assigned to from within a normal NativeCounter, since it is not possible for it to live separately outside a NativeCounter. TODO - why is that? |
|||
|
|||
```c# |
|||
[NativeContainer] |
|||
// This attribute is what makes it possible to use NativeCounter.Concurrent in a ParallelFor job |
|||
[NativeContainerIsAtomicWriteOnly] |
|||
unsafe public struct Concurrent |
|||
{ |
|||
// Copy of the pointer from the full NativeCounter |
|||
[NativeDisableUnsafePtrRestriction] |
|||
int* m_Counter; |
|||
|
|||
// Copy of the AtomicSafetyHandle from the full NativeCounter. The dispose sentinel is not copied since this inner struct does not own the memory and is not responsible for freeing it. |
|||
#if ENABLE_UNITY_COLLECTIONS_CHECKS |
|||
AtomicSafetyHandle m_Safety; |
|||
#endif |
|||
|
|||
// This is what makes it possible to assign to NativeCounter.Concurrent from NativeCounter |
|||
public static implicit operator NativeCounter.Concurrent (NativeCounter cnt) |
|||
{ |
|||
NativeCounter.Concurrent concurrent; |
|||
#if ENABLE_UNITY_COLLECTIONS_CHECKS |
|||
AtomicSafetyHandle.CheckWriteAndThrow(cnt.m_Safety); |
|||
concurrent.m_Safety = cnt.m_Safety; |
|||
AtomicSafetyHandle.UseSecondaryVersion(ref concurrent.m_Safety); |
|||
#endif |
|||
|
|||
concurrent.m_Counter = cnt.m_Counter; |
|||
return concurrent; |
|||
} |
|||
|
|||
public void Increment() |
|||
{ |
|||
// Increment still needs to check for write permissions |
|||
#if ENABLE_UNITY_COLLECTIONS_CHECKS |
|||
AtomicSafetyHandle.CheckWriteAndThrow(m_Safety); |
|||
#endif |
|||
// The actual increment is implemented with an atomic, since it can be incremented by multiple threads at the same time |
|||
Interlocked.Increment(ref *m_Counter); |
|||
} |
|||
} |
|||
``` |
|||
|
|||
With this setup we can schedule ParallelFor with write access to a NativeCounter through the inner Concurrent struct, like this: |
|||
|
|||
```c# |
|||
struct CountZeros : IJobParallelFor |
|||
{ |
|||
[ReadOnly] |
|||
public NativeArray<int> input; |
|||
public NativeCounter.Concurrent counter; |
|||
public void Execute(int i) |
|||
{ |
|||
if (input[i] == 0) |
|||
{ |
|||
counter.Increment(); |
|||
} |
|||
} |
|||
} |
|||
``` |
|||
|
|||
```c# |
|||
var counter = new NativeCounter(Allocator.Temp); |
|||
var jobData = new CountZeros(); |
|||
jobData.input = input; |
|||
jobData.counter = counter; |
|||
counter.Count = 0; |
|||
|
|||
var handle = jobData.Schedule(input.Length, 8); |
|||
handle.Complete(); |
|||
|
|||
Debug.Log("The array countains " + counter.Count + " zeros"); |
|||
counter.Dispose(); |
|||
``` |
|||
|
|||
## Better cache usage |
|||
|
|||
The NativeCounter from the previous section is a working implementation of a counter, but all jobs in the ParallelFor will access the same atomic to increment the value. This is not optimal as it means the same cache line is used by all threads. |
|||
The way this is generally solved in NativeContainers is to have a local cache per worker thread, which is stored on its own cache line. |
|||
|
|||
The __[NativeSetThreadIndex]__ attribute can inject a worker thread index, the index is guaranteed to be unique while accessing the NativeContainer from the ParallelFor jobs. |
|||
|
|||
In order to make such an optimization here we need to change a few things. The first thing we need to change is the data layout. For performance reasons we need one full cache line per worker thread, rather than a single int to avoid [false sharing](https://en.wikipedia.org/wiki/False_sharing). |
|||
|
|||
We start by adding a constant for the number of ints on a cache line. |
|||
|
|||
```c# |
|||
public const int IntsPerCacheLine = JobsUtility.CacheLineSize / sizeof(int); |
|||
``` |
|||
|
|||
Next we change the amount of memory allocated. |
|||
|
|||
```c# |
|||
// One full cache line (integers per cacheline * size of integer) for each potential worker index, JobsUtility.MaxJobThreadCount |
|||
m_Counter = (int*)UnsafeUtility.Malloc(UnsafeUtility.SizeOf<int>()*IntsPerCacheLine*JobsUtility.MaxJobThreadCount, 4, label); |
|||
``` |
|||
|
|||
TODO: I'm not sure which example you are referring to when you say: main, non-concurrent, version below (is this an example you used on this page or what you would do if you were not using jobified code/ECS etc. It has potential for confusion.) |
|||
|
|||
When accessing the counter from the main, non-concurrent, version there can only be one writer so the increment function is fine with the new memory layout. |
|||
For `get` and `set` of the `count` we need to loop over all potential worker indices. |
|||
|
|||
```c# |
|||
public int Count |
|||
{ |
|||
get |
|||
{ |
|||
// Verify that the caller has read permission on this data. |
|||
// This is the race condition protection, without these checks the AtomicSafetyHandle is useless |
|||
#if ENABLE_UNITY_COLLECTIONS_CHECKS |
|||
AtomicSafetyHandle.CheckReadAndThrow(m_Safety); |
|||
#endif |
|||
int count = 0; |
|||
for (int i = 0; i < JobsUtility.MaxJobThreadCount; ++i) |
|||
count += m_Counter[IntsPerCacheLine * i]; |
|||
return count; |
|||
} |
|||
set |
|||
{ |
|||
// Verify that the caller has write permission on this data. |
|||
// This is the race condition protection, without these checks the AtomicSafetyHandle is useless |
|||
#if ENABLE_UNITY_COLLECTIONS_CHECKS |
|||
AtomicSafetyHandle.CheckWriteAndThrow(m_Safety); |
|||
#endif |
|||
// Clear all locally cached counts, |
|||
// set the first one to the required value |
|||
for (int i = 1; i < JobsUtility.MaxJobThreadCount; ++i) |
|||
m_Counter[IntsPerCacheLine * i] = 0; |
|||
*m_Counter = value; |
|||
} |
|||
} |
|||
``` |
|||
|
|||
The final change is the inner Concurrent struct that needs to get the worker index injected into it. Since each worker only runs one job at a time, there is no longer any need to use atomics when only accessing the local count. |
|||
|
|||
```c# |
|||
[NativeContainer] |
|||
[NativeContainerIsAtomicWriteOnly] |
|||
// Let the job system know that it should inject the current worker index into this container |
|||
unsafe public struct Concurrent |
|||
{ |
|||
[NativeDisableUnsafePtrRestriction] |
|||
int* m_Counter; |
|||
|
|||
#if ENABLE_UNITY_COLLECTIONS_CHECKS |
|||
AtomicSafetyHandle m_Safety; |
|||
#endif |
|||
|
|||
// The current worker thread index; it must use this exact name since it is injected |
|||
[NativeSetThreadIndex] |
|||
int m_ThreadIndex; |
|||
|
|||
public static implicit operator NativeCacheCounter.Concurrent (NativeCacheCounter cnt) |
|||
{ |
|||
NativeCacheCounter.Concurrent concurrent; |
|||
#if ENABLE_UNITY_COLLECTIONS_CHECKS |
|||
AtomicSafetyHandle.CheckWriteAndThrow(cnt.m_Safety); |
|||
concurrent.m_Safety = cnt.m_Safety; |
|||
AtomicSafetyHandle.UseSecondaryVersion(ref concurrent.m_Safety); |
|||
#endif |
|||
|
|||
concurrent.m_Counter = cnt.m_Counter; |
|||
concurrent.m_ThreadIndex = 0; |
|||
return concurrent; |
|||
} |
|||
|
|||
public void Increment() |
|||
{ |
|||
#if ENABLE_UNITY_COLLECTIONS_CHECKS |
|||
AtomicSafetyHandle.CheckWriteAndThrow(m_Safety); |
|||
#endif |
|||
// No need for atomics any more since we are just incrementing the local count |
|||
++m_Counter[IntsPerCacheLine*m_ThreadIndex]; |
|||
} |
|||
} |
|||
``` |
|||
|
|||
Writing the NativeCounter this way significantly reduces the overhead of having multiple threads writing to it. It does, however, come at a price. The cost of getting the count on the main thread has increased significantly since it now needs to check all local caches and sum them up. If you are aware of this and make sure to cache the return values it is usually worth it, but you need to know the limitations of your data structures. So we strongly recommend documenting the performance characteristics. |
|||
|
|||
## Tests |
|||
|
|||
The NativeCounter is not complete, the only thing left is to add tests for it to make sure it is correct and that it does not break in the future. When writing tests you should try to cover as many unusual scenarios as possible. It is also a good idea to add some kind of stress test using jobs to detect race conditions, even if it is unlikely to find all of them. The NativeCounter API is very small so the number of tests required is not huge. |
|||
|
|||
* Both versions of the counter examples above are available at: _/Assets/NativeCounterDemo_. |
|||
* The tests for them can be found at: _/Assets/NativeCounterDemo/Editor/NativeCounterTests.cs_. |
|||
|
|||
## Available attributes |
|||
|
|||
The NativeCounter uses many attributes, but there are a few more available for other types of containers. Here is a list of the available attributes you can use on the NativeContainer struct. |
|||
* [NativeContainer](https://docs.unity3d.com/2018.1/Documentation/ScriptReference/Unity.Collections.LowLevel.Unsafe.NativeContainerAttribute.html) - marks a struct as a NativeContainer.Required for all native containers. |
|||
* [NativeContainerSupportsMinMaxWriteRestriction](https://docs.unity3d.com/2018.1/Documentation/ScriptReference/Unity.Collections.LowLevel.Unsafe.NativeContainerSupportsMinMaxWriteRestrictionAttribute.html) - signals that the NativeContainer can restrict its writable ranges to be between a min and max index. This is used when passing the container to an IJobParallelFor to make sure that the job does not write to indices it is not supposed to process. In order to use this the NativeContainer must have the members int __m_Length__, int __m_MinIndex__ and int __m_MaxIndex__ in that order with no other members between them. The container must also throw an exception for writes outside the min/max range. |
|||
* [NativeContainerIsAtomicWriteOnly](https://docs.unity3d.com/2018.1/Documentation/ScriptReference/Unity.Collections.LowLevel.Unsafe.NativeContainerIsAtomicWriteOnlyAttribute.html) - signals that the NativeContainer uses atomic writes and no reads. By adding this is is possible to pass the NativeContainer to an IJobParallelFor as writable without restrictions on which indices can be written to. |
|||
* [NativeContainerSupportsDeallocateOnJobCompletion](https://docs.unity3d.com/2018.1/Documentation/ScriptReference/Unity.Collections.LowLevel.Unsafe.NativeContainerSupportsDeallocateOnJobCompletionAttribute.html) - makes the NativeContainer usable with [DeallocateOnJobCompletion](https://docs.unity3d.com/2018.1/Documentation/ScriptReference/Unity.Collections.DeallocateOnJobCompletionAttribute.html). In order to use this the NativeContainer must have a single allocation in __m_Buffer__, an allocator label in __m_AllocatorLabel__ and a dispose sentinel in __m_DisposeSentinel__. |
|||
* [NativeSetThreadIndex](https://docs.unity3d.com/2018.1/Documentation/ScriptReference/Unity.Collections.LowLevel.Unsafe.NativeSetThreadIndexAttribute.html) - Patches an int with the thread index of the job. |
|||
|
|||
In addition to these attributes on the native container struct itself there are a few attributes which can be used on members of the native container. |
|||
* [NativeDisableUnsafePtrRestriction](https://docs.unity3d.com/2018.1/Documentation/ScriptReference/Unity.Collections.LowLevel.Unsafe.NativeDisableUnsafePtrRestrictionAttribute.html) - allows the NativeContainer to be passed to a job even though it contains a pointer, which is usually not allowed. |
|||
* [NativeSetClassTypeToNullOnSchedule](https://docs.unity3d.com/2018.1/Documentation/ScriptReference/Unity.Collections.LowLevel.Unsafe.NativeSetClassTypeToNullOnScheduleAttribute.html) - allows the NativeContainer to be passed to a job even though it contains a managed object. The managed object will be set to `null` on the copy passed to the job. |
|||
|
|
|||
--- |
|||
uid: ecs-dynamic-buffers |
|||
--- |
|||
|
|||
# Dynamic buffer components |
|||
|
|||
Use dynamic buffer components to associate array-like data with an entity. Dynamic buffers are ECS components that can hold a variable number of elements, and automatically resize as necessary. |
|||
|
|||
To create a dynamic buffer, first declare a struct that implements [IBufferElementData](xref:Unity.Entities.IBufferElementData) and defines the elements stored in the buffer. For example, you can use the following struct for a buffer component that stores integers: |
|||
|
|||
[!code-cs[declare-element](../package/DocCodeSamples.Tests/DynamicBufferExamples.cs#declare-element)] |
|||
|
|||
To associate a dynamic buffer with an entity, add an [IBufferElementData](xref:Unity.Entities.IBufferElementData) component directly to the entity rather than adding the [dynamic buffer container](xref:Unity.Entities.DynamicBuffer`1) itself. |
|||
|
|||
ECS manages the container. For most purposes, you can use a declared `IBufferElementData` type to treat a dynamic buffer the same as any other ECS component. For example, you can use the `IBufferElementData` type in [entity queries](xref:Unity.Entities.EntityQuery) as well as when you add or remove the buffer component. However, you must use different functions to access a buffer component and those functions provide the [DynamicBuffer](xref:Unity.Entities.DynamicBuffer`1) instance, which gives an array-like interface to the buffer data. |
|||
|
|||
To specify an “internal capacity" for a dynamic buffer component, use the [InternalBufferCapacity attribute](xref:Unity.Entities.InternalBufferCapacityAttribute). The internal capacity defines the number of elements the dynamic buffer stores in the [ArchetypeChunk](xref:Unity.Entities.ArchetypeChunk) along with the other components of an entity. If you increase the size of a buffer beyond the internal capacity, the buffer allocates a heap memory block outside the current chunk and moves all existing elements. ECS manages this external buffer memory automatically, and frees the memory when the buffer component is removed. |
|||
|
|||
**Note:** If the data in a buffer is not dynamic, you can use a [blob asset](xref:Unity.Entities.BlobBuilder) instead of a dynamic buffer. Blob assets can store structured data, including arrays. Multiple entities can share blob assets. |
|||
|
|||
## Declaring buffer element types |
|||
|
|||
To declare a buffer, declare a struct that defines the type of element that you want to put into the buffer. The struct must implement [IBufferElementData](xref:Unity.Entities.IBufferElementData), like so: |
|||
|
|||
[!code-cs[declare-element-full](../package/DocCodeSamples.Tests/DynamicBufferExamples.cs#declare-element-full)] |
|||
|
|||
|
|||
## Adding buffer types to entities |
|||
|
|||
To add a buffer to an entity, add the `IBufferElementData` struct that defines the data type of the buffer element, and then add that type directly to an entity or to an [archetype](xref:Unity.Entities.EntityArchetype): |
|||
|
|||
### Using EntityManager.AddBuffer() |
|||
|
|||
For more information, see the documentation on [EntityManager.AddBuffer()](xref:Unity.Enities.EntityManager.AddBuffer`1(Unity.Entities.Entity)). |
|||
|
|||
[!code-cs[declare](../package/DocCodeSamples.Tests/DynamicBufferExamples.cs#add-with-manager)] |
|||
|
|||
### Using an archetype |
|||
|
|||
[!code-cs[declare](../package/DocCodeSamples.Tests/DynamicBufferExamples.cs#add-with-archetype)] |
|||
|
|||
### Using the `[GenerateAuthoringComponent]` attribute |
|||
|
|||
You can use `[GenerateAuthoringComponent]`to generate authoring components for simple IBufferElementData implementations that contain only one field. Setting this attribute allows you add an ECS IBufferElementData component to a GameObject so that you can set the buffer elements in the Editor. |
|||
|
|||
For example, if you declare the following type, you can add it directly to a GameObject in the Editor: |
|||
|
|||
``` |
|||
[GenerateAuthoringComponent] |
|||
public struct IntBufferElement: IBufferElementData |
|||
{ |
|||
public int Value; |
|||
} |
|||
``` |
|||
|
|||
In the background, Unity generates a class named `IntBufferElementAuthoring` (which inherits from `MonoBehaviour`), which exposes a public field of `List<int>` type. When the GameObject containing this generated authoring component is converted into an entity, the list is converted into `DynamicBuffer<IntBufferElement>`, and then added to the converted entity. |
|||
|
|||
Note the following restrictions: |
|||
- Only one component in a single C# file can have a generated authoring component, and the C# file must not have another MonoBehaviour in it. |
|||
- `IBufferElementData` authoring components cannot be automatically generated for types that contain more than one field. |
|||
- `IBufferElementData` authoring components cannot be automatically generated for types that have an explicit layout. |
|||
|
|||
### Using an [EntityCommandBuffer](xref:Unity.Entities.EntityCommandBuffer) |
|||
|
|||
You can add or set a buffer component when you add commands to an entity command buffer. |
|||
|
|||
Use [AddBuffer](xref:Unity.Entities.EntityCommandBuffer.AddBuffer``1(Unity.Entities.Entity)) to create a new buffer for the entity, which changes the entity's archetype. Use [SetBuffer](xref:Unity.Entities.EntityCommandBuffer.SetBuffer``1(Unity.Entities.Entity)) to wipe out the existing buffer (which must exist) and create a new, empty buffer in its place. Both functions return a [DynamicBuffer](xref:Unity.Entities.DynamicBuffer`1) instance that you can use to populate the new buffer. You can add elements to the buffer immediately, but they are not otherwise accessible until the buffer is added to the entity when the command buffer is executed. |
|||
|
|||
The following job creates a new entity using a command buffer and then adds a dynamic buffer component using [EntityCommandBuffer.AddBuffer](xref:Unity.Entities.EntityCommandBuffer.AddBuffer``1(Unity.Entities.Entity)). The job also adds a number of elements to the dynamic buffer. |
|||
|
|||
[!code-cs[declare](../package/DocCodeSamples.Tests/DynamicBufferExamples.cs#add-in-job)] |
|||
|
|||
**Note:** You are not required to add data to the dynamic buffer immediately. However, you won't have access to the buffer again until after the entity command buffer you are using is executed. |
|||
|
|||
## Accessing buffers |
|||
|
|||
You can use [EntityManager](xref:Unity.Entities.EntityManager), [systems](ecs_systems.md), and jobs to access the [DynamicBuffer](xref:Unity.Entities.DynamicBuffer`1) instance in much the same way as you access other component types of entities. |
|||
|
|||
### EntityManager |
|||
|
|||
You can use an instance of the [EntityManager](xref:Unity.Entities.EntityManager) to access a dynamic buffer: |
|||
|
|||
[!code-cs[declare](../package/DocCodeSamples.Tests/DynamicBufferExamples.cs#access-manager)] |
|||
|
|||
### Looking up buffers of another entity |
|||
|
|||
When you need to look up the buffer data belonging to another entity in a job, you can pass a [BufferFromEntity](xref:Unity.Entities.BufferFromEntity`1) variable to the job. |
|||
|
|||
[!code-cs[declare](../package/DocCodeSamples.Tests/DynamicBufferExamples.cs#lookup-snippet)] |
|||
|
|||
### SystemBase Entities.ForEach |
|||
|
|||
You can access dynamic buffers associated with the entities you process with Entities.ForEach by passing the buffer as one of your lambda function parameters. The following example adds all the values stored in the buffers of type, `MyBufferElement`: |
|||
|
|||
[!code-cs[access-buffer-system](../package/DocCodeSamples.Tests/DynamicBufferExamples.cs#access-buffer-system)] |
|||
|
|||
Note that we can write directly to the captured `sum` variable in this example because we execute the code with `Run()`. If we scheduled the function to run in a job, we could only write to a native container such as NativeArray, even though the result is a single value. |
|||
|
|||
### IJobChunk |
|||
|
|||
To access an individual buffer in an `IJobChunk` job, pass the buffer data type to the job and use that to get a [BufferAccessor](xref:Unity.Entities.BufferAccessor`1). A buffer accessor is an array-like structure that provides access to all of the dynamic buffers in the current chunk. |
|||
|
|||
Like the previous example, the following example adds up the contents of all dynamic buffers that contain elements of type, `MyBufferElement`. `IJobChunk` jobs can also run in parallel on each chunk, so in the example, it first stores the intermediate sum for each buffer in a native array and then uses a second job to calculate the final sum. In this case, the intermediate array holds one result for each chunk, rather than one result for each entity. |
|||
|
|||
[!code-cs[declare](../package/DocCodeSamples.Tests/DynamicBufferExamples.cs#access-chunk-job)] |
|||
|
|||
## Reinterpreting buffers |
|||
|
|||
Buffers can be reinterpreted as a type of the same size. The intention is to |
|||
allow controlled type-punning and to get rid of the wrapper element types when |
|||
they get in the way. To reinterpret, call [Reinterpret<T>](xref:Unity.Entities.DynamicBuffer`1.Reinterpret*): |
|||
|
|||
[!code-cs[declare](../package/DocCodeSamples.Tests/DynamicBufferExamples.cs#reinterpret-snippet)] |
|||
|
|||
The reinterpreted buffer instance retains the safety handle of the original |
|||
buffer, and is safe to use. Reinterpreted buffers reference original data, so |
|||
modifications to one reinterpreted buffer are immediately reflected in |
|||
others. |
|||
|
|||
**Note:** The reinterpret function only enforces that the types involved have the same length. For example, you can alias a `uint` and `float` buffer without raising an error because both types are 32-bits long. You must make sure that the reinterpretation makes sense logically. |
|||
|
|||
## Buffer reference invalidation |
|||
Every [structural change](sync_points.md#structural-changes) invalidates all references to dynamic buffers. Structural changes generally cause entities to move from one chunk to another. Small dynamic buffers can reference memory within a chunk (as opposed to from main memory) and therefore, they need to be reacquired after a structural change. |
|||
|
|||
[!code-cs[declare](../package/DocCodeSamples.Tests/DynamicBufferExamples.cs#invalidation)] |
|
|||
--- |
|||
uid: ecs-building |
|||
--- |
|||
# Building your project |
|||
|
|||
> Synopsis: Cover any unique aspects for building ECS projects, including any cross-platform details. |
|||
|
|
|||
--- |
|||
uid: ecs-burst |
|||
--- |
|||
# Using Burst |
|||
|
|||
> Synopsis: Cover how, when, and why to use the Burst compiler with ECS. Everything burst -elated within ECS should go here. |
|||
|
|
|||
--- |
|||
uid: ecs-chunk-component-data |
|||
--- |
|||
|
|||
# Chunk component data |
|||
|
|||
Use chunk components to associate data with a specific [chunk](xref:Unity.Entities.ArchetypeChunk). |
|||
|
|||
Chunk components contain data that applies to all entities in a specific chunk. For example, if you have chunks of entities that represent 3D objects that are organized by proximity, you can use a chunk component to store a collective bounding box for them. chunk components use the interface type [IComponentData](xref:Unity.Entities.IComponentData). |
|||
|
|||
## Add and set the values of a chunk component |
|||
|
|||
Although chunk components can have values unique to an individual chunk, they are still part of the archetype of the entities in the chunk. Therefore, if you remove a chunk component from an entity, ECS moves that entity to a different chunk (possibly a new one). Likewise, if you add a chunk component to an entity, ECS moves that entity to a different chunk because its archetype changes; the addition of the chunk component does not affect the remaining entities in the original chunk. |
|||
|
|||
If you use an entity in a chunk to change the value of a chunk component, it changes the value of the chunk component that is common to all the entities in that chunk. If you change the archetype of an entity so that it moves into a new chunk that has the same type of chunk component, then the existing value in the destination chunk is unaffected. **Note:** If the entity is moved to a newly created chunk, then ECS creates a new chunk component for that chunk and assigns its default value. |
|||
|
|||
The main differences between working with chunk components and general-purpose components is that you use different functions to add, set, and remove them. chunk components also have their own [ComponentType](xref:Unity.Entities.ComponentType.ChunkComponentType) functions that you use to define entity archetypes and queries. |
|||
|
|||
|
|||
**Relevant APIs** |
|||
|
|||
| **Purpose** | **Function** | |
|||
| :--------------- | :---------------- | |
|||
| Declaration | [IComponentData](xref:Unity.Entities.IComponentData) | |
|||
| | | |
|||
| **[ArchetypeChunk methods](xref:Unity.Entities.ArchetypeChunk)** | |
|||
| Read | [GetChunkComponentData<T>(ArchetypeChunkComponentType<T>)](xref:Unity.Entities.ArchetypeChunk.GetChunkComponentData*) | |
|||
| Check | [HasChunkComponent<T>(ArchetypeChunkComponentType<T>)](xref:Unity.Entities.ArchetypeChunk.HasChunkComponent*) | |
|||
| Write | [SetChunkComponentData<T>(ArchetypeChunkComponentType<T>, T)](xref:Unity.Entities.ArchetypeChunk.SetChunkComponentData*) | |
|||
| | | |
|||
| **[EntityManager methods](xref:Unity.Entities.EntityManager)** | | |
|||
| Create | [AddChunkComponentData<T>(Entity)](xref:Unity.Entities.EntityManager.AddChunkComponentData``1(Unity.Entities.Entity)) | |
|||
| Create | [AddChunkComponentData<T>(EntityQuery, T)](xref:Unity.Entities.EntityManager.AddChunkComponentData``1(Unity.Entities.EntityQuery,``0)) | |
|||
| Create | [AddComponents(Entity,ComponentTypes)](xref:Unity.Entities.EntityManager.AddComponents(Unity.Entities.Entity,Unity.Entities.ComponentTypes)) | |
|||
| Get type info | [GetArchetypeChunkComponentType<T>(Boolean)](xref:Unity.Entities.EntityManager.GetArchetypeChunkComponentType*) | |
|||
| Read | [GetChunkComponentData<T>(ArchetypeChunk)](xref:Unity.Entities.EntityManager.GetChunkComponentData``1(Unity.Entities.ArchetypeChunk)) | |
|||
| Read | [GetChunkComponentData<T>(Entity)](xref:Unity.Entities.EntityManager.GetChunkComponentData``1(Unity.Entities.Entity)) | |
|||
| Check | [HasChunkComponent<T>(Entity)](xref:Unity.Entities.EntityManager.HasChunkComponent*) | |
|||
| Delete | [RemoveChunkComponent<T>(Entity)](xref:Unity.Entities.EntityManager.RemoveChunkComponent``1(Unity.Entities.Entity)) | |
|||
| Delete | [RemoveChunkComponentData<T>(EntityQuery)](xref:Unity.Entities.EntityManager.RemoveChunkComponentData*) | |
|||
| Write | [EntityManager.SetChunkComponentData<T>(ArchetypeChunk, T)](xref:Unity.Entities.EntityManager.SetChunkComponentData*) | |
|||
|
|||
<a name="declare"></a> |
|||
|
|||
## Declaring a chunk component |
|||
|
|||
Chunk components use the interface type [IComponentData](xref:Unity.Entities.IComponentData). |
|||
|
|||
[!code-cs[declare-chunk-component](../package/DocCodeSamples.Tests/ChunkComponentExamples.cs#declare-chunk-component)] |
|||
|
|||
|
|||
<a name="create"></a> |
|||
## Creating a chunk component |
|||
|
|||
To add a chunk component directly, use an entity in the target chunk, or use an entity query that selects a group of target chunks. You cannot add chunk components inside a job, nor can they be added with an `EntityCommandBuffer`. |
|||
|
|||
You can also include chunk components as part of the [EntityArchetype](xref:Unity.Entities.EntityArchetype) or list of [ComponentType](xref:Unity.Entities.ComponentType) objects that ECS uses to create entities. ECS creates the chunk components for each chunk and stores entities with that archetype. |
|||
|
|||
|
|||
Use [ComponentType.ChunkComponent<T>](xref:Unity.Entities.ComponentType``1) or [ComponentType.ChunkComponentReadOnly<T>](xref:Unity.Entities.ComponentTypeReadOnly``1) with these methods. Otherwise, ECS treats the component as a general-purpose component instead of a chunk component. |
|||
|
|||
**With an entity in a chunk** |
|||
|
|||
Given an entity in the target chunk, you can use the [EntityManager.AddChunkComponentData<T>()](xref:Unity.Entities.EntityManager.AddChunkComponentData``1) function to add a chunk component to the chunk: |
|||
|
|||
[!code-cs[em-snippet](../package/DocCodeSamples.Tests/ChunkComponentExamples.cs#em-snippet)] |
|||
|
|||
When you use this method, you cannot immediately set a value for the chunk component. |
|||
|
|||
**With an [EntityQuery](xref:Unity.Entities.EntityQuery)** |
|||
|
|||
Given an entity query that selects all the chunks that you want to add a chunk component to, you can use the [EntityManager.AddChunkComponentData<T>()](xref:Unity.Entities.EntityManager.AddChunkComponentData``1(Unity.Entities.EntityQuery,``0)) function to add and set the component: |
|||
|
|||
[!code-cs[desc-chunk-component](../package/DocCodeSamples.Tests/ChunkComponentExamples.cs#desc-chunk-component)] |
|||
|
|||
When you use this method, you can set the same initial value for all of the new chunk components. |
|||
|
|||
**With an [EntityArchetype](xref:Unity.Entities.EntityArchetype)** |
|||
|
|||
When you create entities with an archetype or a list of component types, include the chunk component types in the archetype: |
|||
|
|||
[!code-cs[archetype-chunk-component](../package/DocCodeSamples.Tests/ChunkComponentExamples.cs#archetype-chunk-component)] |
|||
|
|||
or list of component types: |
|||
|
|||
[!code-cs[component-list-chunk-component](../package/DocCodeSamples.Tests/ChunkComponentExamples.cs#component-list-chunk-component)] |
|||
|
|||
When you use these methods, the chunk components for new chunks that ECS creates as part of entity construction receive the default struct value. ECS does not change chunk components in existing chunks. See [Updating a chunk component](#update) for how to set the chunk component value given a reference to an entity. |
|||
|
|||
<a name="read"></a> |
|||
## Reading a chunk component |
|||
|
|||
To read a chunk component, you can use the [ArchetypeChunk](xref:Unity.Entities.ArchetypeChunk) object that represents the chunk, or use an entity in the target chunk. |
|||
|
|||
**With the ArchetypeChunk instance** |
|||
|
|||
Given a chunk, you can use the [EntityManager.GetChunkComponentData<T>](xref:Unity.Entities.EntityManager.GetChunkComponentData``1(Unity.Entities.ArchetypeChunk)) function to read its chunk component. The following code iterates over all of the chunks that match a query and accesses a chunk component of type `ChunkComponentA`: |
|||
|
|||
[!code-cs[read-chunk-component](../package/DocCodeSamples.Tests/ChunkComponentExamples.cs#read-chunk-component)] |
|||
|
|||
**With an entity in a chunk** |
|||
|
|||
Given an entity, you can access a chunk component in the chunk that contains the entity with [EntityManager.GetChunkComponentData<T>](xref:Unity.Entities.EntityManager.GetChunkComponentData``1(Unity.Entities.Entity)): |
|||
|
|||
[!code-cs[read-entity-chunk-component](../package/DocCodeSamples.Tests/ChunkComponentExamples.cs#read-entity-chunk-component)] |
|||
|
|||
<a name="update"></a> |
|||
## Updating a chunk component |
|||
|
|||
You can update a chunk component given a reference to the [chunk](xref:Unity.Entities.ArchetypeChunk) it belongs to. In an `IJobChunk` job, you can call [ArchetypeChunk.SetChunkComponentData](xref:Unity.Entities.ArchetypeChunk.SetChunkComponentData*). On the main thread, you can use the EntityManager version: [EntityManager.SetChunkComponentData](xref:Unity.Entities.EntityManager.SetChunkComponentData*). **Note:** You cannot access chunk components using SystemBase Entities.ForEach because you do not have access to the `ArchetypeChunk` object or the EntityManager. |
|||
|
|||
**With the ArchetypeChunk instance** |
|||
|
|||
To update a chunk component in a job, see [Reading and writing in a system](#read-and-write-jcs). |
|||
|
|||
To update a chunk component on the main thread, use the EntityManager: |
|||
|
|||
[!code-cs[set-chunk-component](../package/DocCodeSamples.Tests/ChunkComponentExamples.cs#set-chunk-component)] |
|||
|
|||
**With an Entity instance** |
|||
|
|||
If you have an entity in the chunk rather than the chunk reference itself, you can also use the EntityManger to get the chunk that contains the entity: |
|||
|
|||
**Note:** If you only want to read a chunk component and not write to it, you should use [ComponentType.ChunkComponentReadOnly](xref:Unity.Entities.ComponentType.ChunkComponentReadOnly*) when you define the entity query to avoid creating unnecessary job scheduling constraints. |
|||
|
|||
<a name="delete"></a> |
|||
## Deleting a chunk component |
|||
|
|||
Use the [EntityManager.RemoveChunkComponent](xref:Unity.Entities.EntityManager.RemoveChunkComponent*) functions to delete a chunk component. You can remove a chunk component given an entity in the target chunk or you can remove all of the chunk components of a given type from all chunks an entity query selects. |
|||
|
|||
If you remove a chunk component from an individual entity, that entity moves to a different chunk because the archetype of the entity changes. The chunk keeps the unchanged chunk component as long as there are other entities that remain in the chunk. |
|||
|
|||
<a name="in-query"></a> |
|||
|
|||
## Using a chunk component in a query |
|||
|
|||
To use a chunk component in an entity query, you must use either the [ComponentType.ChunkComponent<T>](xref:Unity.Entities.ComponentType``1) or [ComponentType.ChunkComponentReadOnly<T>](xref:Unity.Entities.ComponentTypeReadOnly``1) functions to specify the type. Otherwise, ECS treats the component as a general-purpose component instead of a Chunk component. |
|||
|
|||
**In an [EntityQueryDesc](Unity.Entities.EntityQueryDesc)** |
|||
|
|||
You can use the following query description to create an entity query that selects all chunks, and entities in those chunks, that have a chunk component of type, _ChunkComponentA_: |
|||
|
|||
[!code-cs[use-chunk-component](../package/DocCodeSamples.Tests/ChunkComponentExamples.cs#use-chunk-component)] |
|||
|
|||
|
|||
<a name="chunk-by-chunk"></a> |
|||
## Iterating over chunks to set chunk components |
|||
|
|||
To iterate over all chunks for which you want to set a chunk component, you can create an entity query that selects the correct chunks and then use the EntityQuery object to get a list of the ArchetypeChunk instances as a native array. The ArchetypeChunk object allows you to write a new value to the chunk component. |
|||
|
|||
[!code-cs[full-chunk-example](../package/DocCodeSamples.Tests/ChunkComponentExamples.cs#full-chunk-example)] |
|||
|
|||
Note that if you need to read the components in a chunk to determine the proper value of a chunk component, you should use [IJobChunk](#read-and-write-jcs). For example, the following code calculates the axis-aligned bounding box for all chunks containing entities that have LocalToWorld components: |
|||
|
|||
[!code-cs[aabb-chunk-component](../package/DocCodeSamples.Tests/ChunkComponentExamples.cs#aabb-chunk-component)] |
|||
|
|
|||
--- |
|||
uid: ecs-components |
|||
--- |
|||
# Components |
|||
|
|||
Components are one of the three principle elements of an Entity Component System architecture. They represent the data of your game or application. [Entities](ecs_entities.md) are identifiers that index your collections of components, while [systems](ecs_systems.md) provide the behavior. |
|||
|
|||
A component in ECS is a struct that has one of the following "marker interfaces": |
|||
|
|||
* [IComponentData](xref:Unity.Entities.IComponentData) — Use for [general purpose](xref:ecs-component-data) and [chunk components](xref:ecs-chunk-component-data). |
|||
* [IBufferElementData](xref:Unity.Entities.IBufferelementData) — Associates [dynamic buffers](xref:ecs-dynamic-buffers) with an entity. |
|||
* [ISharedComponentData](xref:Unity.Entities.ISharedComponentData) — Categorizes or groups entities by value within an archetype. For more information, see [Shared Component Data](xref:ecs-shared-component-data). |
|||
* [ISystemStateComponentData](xref:Unity.Entities.ISystemStateComponentData) — Associates a system-specific state with an entity and detects when individual entities are created or destroyed. For more information, see [System State Components](xref:ecs-system-state-component-data). |
|||
* [ISharedSystemStateComponentData](xref:Unity.Entities.ISharedSystemStateComponentData) — a combination of shared and system state data. See [System State Components](xref:ecs-system-state-component-data). |
|||
* [Blob assets](xref:ecs-blob-asset-data) – While not technically a "component," you can use blob assets to store data. Blob assets can be referenced by one or more components using a [BlobAssetReference](xref:Unity.Entities.BlobAssetReference) and are immutable. You can use blob assets to share data between assets and access that data in C# jobs. |
|||
|
|||
The EntityManager organizes unique combinations of components into **archetypes**. It stores the components of all entities with the same archetype together in blocks of memory called **chunks**. The entities in a given chunk all have the same component archetype. |
|||
|
|||
![](images/ArchetypeChunkDiagram.png) |
|||
|
|||
This diagram illustrates how ECS stores component data chunks by their archetypes. Shared components and chunk components are exceptions because ECS stores them outside of the chunk. A single instance of these component types apply to all of the entities in the applicable chunks. Additionally, you can optionally store dynamic buffers outside of the chunk. Even though ECS does not store these types of components inside of the chunk, you can generally treat them the same as other component types when querying for entities. |
|
|||
--- |
|||
uid: ecs-concepts |
|||
--- |
|||
# ECS concepts |
|||
|
|||
An Entity Component System (ECS) architecture separates identity (**entities**), data (**components**), and behavior (**systems**). The architecture focuses on the data. Systems read streams of component data, and then transform the data from an input state to an output state, which entities then index. |
|||
|
|||
The following diagram illustrates how these three basic parts work together: |
|||
|
|||
![](images/ECSBlockDiagram.png) |
|||
|
|||
In this diagram, a system reads `Translation` and `Rotation` components, multiplies them and then updates the corresponding `LocalToWorld` components (`L2W = T*R`). |
|||
|
|||
The fact that entities A and B have a `Renderer` component and entity C does not, doesn't affect the system, because the system does not care about `Renderer` components. |
|||
|
|||
You can set up a system so that it requires a `Renderer` component, in which case, the system ignores the components of entity C; or, alternately, you can set up a system to exclude entities with `Renderer` components, which then ignores the components of entities A and B. |
|||
|
|||
## Archetypes |
|||
|
|||
A unique combination of component types is called an [Archetype](xref:Unity.Entities.Archetype). For example, a 3D object might have a component for its world transform, one for its linear movement, one for rotation, and one for its visual representation. Each instance of one of these 3D objects corresponds to a single entity, but because they share the same set of components, ECS classifies them as a single archetype: |
|||
|
|||
![](images/ArchetypeDiagram.png) |
|||
|
|||
In this diagram, entities A and B share archetype M, while entity C has archetype N. |
|||
|
|||
To smoothly change the archetype of an entity, you can add or remove components at runtime. For example, if you remove the `Renderer` component from entity B, it then moves to archetype N. |
|||
|
|||
## Memory Chunks |
|||
|
|||
The archetype of an entity determines where ECS stores the components of that entity. ECS allocates memory in "chunks", each represented by an [ArchetypeChunk](xref:Unity.Entities.ArchetypeChunk) object. A chunk always contains entities of a single archetype. When a chunk of memory becomes full, ECS allocates a new chunk of memory for any new entities created with the same archetype. If you add or remove components, which then changes an entity archetype, ECS moves the components for that entity to a different chunk. |
|||
|
|||
![](images/ArchetypeChunkDiagram.png) |
|||
|
|||
This organizational scheme provides a one-to-many relationship between archetypes and chunks. It also means that finding all the entities with a given set of components only requires searching through the existing archetypes, which are typically small in number, rather than all of the entities, which are typically much larger in number. |
|||
|
|||
ECS does not store the entities that are in a chunk in a specific order. When an entity is created or changed to a new archetype, ECS puts it into the first chunk that stores the archetype, and that has space. Chunks remain tightly packed, however; when an entity is removed from an archetype, ECS moves the components of the last entity in the chunk into the newly vacated slots in the component arrays. |
|||
|
|||
**Note:** The values of shared components in an archetype also determine which entities are stored in which chunk. All of the entities in a given chunk have the exact same values for any shared components. If you change the value of any field in a shared component, the modified entity moves to a different chunk, just as it would if you changed that entity's archetype. A new chunk is allocated, if necessary. |
|||
|
|||
Use shared components to group entities within an archetype when it is more efficient to process them together. For example, the Hybrid Renderer defines its [RenderMesh component](https://docs.unity3d.com/Packages/com.unity.rendering.hybrid@latest?subfolder=/api/Unity.Rendering.RenderMesh.html) to achieve this. |
|||
|
|||
## Entity queries |
|||
|
|||
To identify which entities a system should process, use an [EntityQuery](xref:Unity.Entities.EntityQuery). An entity query searches the existing archetypes for those that have the components that match your requirements. You can specify the following component requirements with a query: |
|||
|
|||
* **All** — the archetype must contain all of the component types in the **All** category. |
|||
* **Any** — the archetype must contain at least one of the component types in the **Any** category. |
|||
* **None** — the archetype must not contain any of the component types in the **None** category. |
|||
|
|||
An entity query provides a list of the chunks that contain the types of components the query requires. You can then iterate over the components in those chunks directly with [IJobChunk](chunk_iteration_job.md). |
|||
|
|||
## Jobs |
|||
|
|||
To take advantage of multiple threads, you can use the [C# Job system]. ECS provides the [SystemBase](xref:Unity.Entites.SystemBase) class, along with the `Entities.ForEach` and [IJobChunk](chunk_iteration_job.md) `Schedule()` and `ScheduleParallel()` methods, to transform data outside the main thread. `Entities.ForEach` is the simplest to use and typically requires fewer lines of code to implement. You can use IJobChunk for more complex situations that `Entities.ForEach` does not handle. |
|||
|
|||
ECS schedules jobs on the main thread in the [order that your systems are arranged](#system-organization). As jobs are scheduled, ECS keeps track of which jobs read and write which components. A job that reads a component is dependent on any prior scheduled job that writes to the same component and vice versa. The job scheduler uses job dependencies to determine which jobs it can run in parallel and which must run in sequence. |
|||
|
|||
<a name="system-organization"></a> |
|||
## System organization |
|||
|
|||
ECS organizes systems by [World](xref:Unity.Entities.World) and then by [group](xref:Unity.Enties.ComponentSystemGroup). By default, ECS creates a default World with a predefined set of groups. It finds all available systems, instantiates them, and adds them to the predefined [simulation group](xref:Unity.Entities.SimulationSystemGroup) in the default World. |
|||
|
|||
You can specify the update order of systems within the same group. A group is a kind of system, so you can add a group to another group and specify its order just like any other system. All systems within a group update before the next system or group. If you do not specify an order, ECS inserts systems into the update order in a deterministic way that does not depend on creation order. In other words, the same set of systems always updates in the same order within their group even when you don't explicitly specify an order. [Entity component buffer systems](xref:Unity.Entities.EntityComponentBufferSystem) |
|||
|
|||
System updates happen on the main thread. However, systems can use jobs to offload work to other threads. [SystemBase](xref:Unity.entities.SystemBase) provide a straightforward way to create and schedule Jobs. |
|||
|
|||
For more information about system creation, update order, and the attributes you can use to organize your systems, see the documentation on [System Update Order](system_update_order.md) . |
|||
|
|||
## ECS authoring |
|||
|
|||
When you create your game or application in the Unity Editor, you can use GameObjects and MonoBehaviours to create a conversion system to map those UnityEngine objects and components to entities. For more information, see [Creating Gameplay](gp_overview.md). |
|
|||
## Creating a system |
|||
|
|||
Implement the abstract class [SystemBase] to create an ECS system. |
|||
|
|||
To create a system, you program the necessary system event callbacks. |
|||
Use the [SystemBase OnUpdate()][OnUpdate()] function to perform the work your system must do every frame. The other callback functions are optional; for example, you can use [OnCreate()] to initialize a system, but not every system requires initialization code. |
|||
|
|||
The system callback functions are invoked in the following order: |
|||
|
|||
![](images/SystemEventOrder.png) |
|||
|
|||
* [OnCreate()] -- called when the system is created. |
|||
* [OnStartRunning()] -- before the first [OnUpdate()] and whenever the system resumes running. |
|||
* [OnUpdate()] -- every frame as long as the system has work to do (see [ShouldRunSystem()]) and the system is [Enabled]. |
|||
* [OnStopRunning()] -- whenever the system stops updating, which can be because you set [Enabled] to false or because it finds no entities matching its queries. Also called before [OnDestroy()]. |
|||
* [OnDestroy()] -- when the system is destroyed. |
|||
|
|||
A system's [OnUpdate()] function is triggered by its parent [system group's] own [OnUpdate()] function. Likewise, when a group changes state, for example if you set the group's [Enabled] property, it changes the state of its child systems. However, children can also change state independently from their parent groups. See [System update order] for more information. |
|||
|
|||
All the system events run on the main thread. Ideally, your [OnUpdate()] function schedules jobs to perform most of the work. To schedule a job from a system, you can use one of the following mechanisms: |
|||
|
|||
* [Entities.ForEach] -- the simplest way to iterate over ECS component data. |
|||
* [Job.WithCode] -- execute a lambda function as a single, background job. |
|||
* [IJobChunk] -- a "lower level" mechanism for iterating over ECS component data chunk-by-chunk. |
|||
* [C# Job System] -- create and schedule general purpose C# jobs. |
|||
|
|||
The following example illustrates using [Entities.ForEach] to implement a system that updates one component based on the value of another: |
|||
|
|||
[!code-cs[basic-system](../package/DocCodeSamples.Tests/SystemBaseExamples.cs#basic-system)] |
|||
|
|||
[ComponentSystemGroup]: xref:ecs-system-update-order |
|||
[Entities.ForEach]: xref:Unity.Entities.SystemBase.Entities |
|||
[Job.WithCode]: xref:Unity.Entities.SystemBase.Job |
|||
[EntityCommandBufferSystem]: xref:ecs-entity-command-buffer |
|||
[EntityCommandBuffer]: xref:Unity.Entities.EntityCommandBuffer |
|||
[IJobChunk]: xref:Unity.Entities.IJobChunk) |
|||
[OnCreate()]: xref:Unity.Entities.ComponentSystemBase.OnCreate* |
|||
[OnDestroy()]: xref:Unity.Entities.ComponentSystemBase.OnDestroy* |
|||
[OnStartRunning()]: xref:Unity.Entities.ComponentSystemBase.OnStartRunning* |
|||
[OnStopRunning()]: xref:Unity.Entities.ComponentSystemBase.OnStopRunning* |
|||
[OnUpdate()]: xref:Unity.Entities.SystemBase.OnUpdate* |
|||
[syncronization points]: xref:sync-points |
|||
[system attributes]: system_update_order.md#attributes |
|||
[SystemBase]: xref:Unity.Entities.SystemBase |
|||
[Enabled]: xref:Unity.Entities.ComponentSystemBase.Enabled |
|||
[World]: xref:Unity.Entities.World |
|||
[GameObject conversion systems]: gp_overview.md |
|||
[time]: xref:Unity.Entities.Core.TimeData |
|||
[World]: xref:Unity.Entities.World |
|||
[UpdateWorldTimeSystem]: xref:Unity.Entities.UpdateWorldTimeSystem |
|||
[system events]: #system-events |
|||
[C# Job System]: https://docs.unity3d.com/Manual/JobSystem.html |
|||
[system group's]: system_update_order.md#groups |
|||
[system attributes]: system_update_order.md#attributes |
|||
[System update order]: system_update_order.md |
|||
[ShouldRunSystem()]: xref:Unity.Entities.ComponentSystemBase.ShouldRunSystem* |
|||
|
|||
<!--Note the following links are intentionally pointing to an old version--> |
|||
[ComponentSystem]: https://docs.unity3d.com/Packages/com.unity.entities@0.5/manual/entity_iteration_foreach.html |
|||
[IJobForEach]: https://docs.unity3d.com/Packages/com.unity.entities@0.5/manual/entity_iteration_job.html |
|||
[JobComponentSystem]: https://docs.unity3d.com/Packages/com.unity.entities@0.5/manual/entities_job_foreach.html |
|
|||
--- |
|||
uid: ecs-debugging |
|||
--- |
|||
# Debugging ECS |
|||
|
|||
In addition to "traditional" debugging using breakpoints, Unity provides the following tools for debugging DOTS code: |
|||
|
|||
* Entity Debugger window |
|||
* Burst Inspector |
|||
* DOTS Decompiler |
|||
* Entity Inspectors |
|||
* Livelink |
|||
* Profiler |
|||
|
|||
<a name="entity_debugger"></a> |
|||
## Entity Debugger |
|||
|
|||
The Entity Debugger allows you to visualize your entities, systems, and components |
|||
|
|||
Open the entity Debugger window using the menu: Window > Analysis > Entity Debugger. |
|||
|
|||
<a name="systems_list"></a> |
|||
### Systems list |
|||
|
|||
The Systems list shows the systems in your project and how much time a system takes to run each frame. You can turn systems on and off from the list using the checkbox provided for each system. |
|||
|
|||
Use the System Display control drop down at the top of the list to control what to display in the System list. The System Display control contains: |
|||
|
|||
* Worlds — Choose the World containing the entities and systems to display. By default, an **Editor World** exists when not in play mode and a **Default World** exists in play mode. |
|||
* **Show Full Player Loop** option — Choose to display the systems of all Worlds and show all of the Unity execution phases (not just those containing systems). |
|||
* **Show Inactive Systems** option — Choose to show systems that are not currently running in addition to the running systems. |
|||
|
|||
Select a system to view its details. |
|||
|
|||
**Note:** If you select the EntityManager entry in the System list, then you have different options on the System details section. |
|||
|
|||
<a name="system_details"></a> |
|||
### System details |
|||
|
|||
The System details section shows the groups of components that a System operates on and the list of entities associated with those component groups. |
|||
|
|||
Each component group shows the components in the group along with the number of entities associated with it. Select a component group to view information about the Chunks containing the data for the components in the group. |
|||
|
|||
When you select the EntityManager entry in the system list, the details section shows all of the entities in the displayed World. When you display a World (rather than the full player loop), you can also filter the list of entities by component |
|||
|
|||
To filter the Entity list: |
|||
1. Select a World in the System Display control. |
|||
2. Select the EntityManager for that World. |
|||
3. At the top of the System details section, click **Edit**. |
|||
4. In the **Choose Component** window, check the components whose entities you want to view. |
|||
|
|||
<a name="chunk_information"></a> |
|||
### Chunk information |
|||
|
|||
The Chunk information section shows the Chunks containing data for the components and entities selected in the details section. |
|||
|
|||
## Inspecting an Entity |
|||
|
|||
Select an entity in the Entity Debugger to view its data in the Unity Inspector window. |
|
|||
--- |
|||
uid: ecs-entities |
|||
--- |
|||
# Entities |
|||
<!-- |
|||
> Topics to add |
|||
> * Spawning Entities in Jobs -- Entity Command Buffers |
|||
> * Transferring Entities between worlds: EM.MoveEntity |
|||
--> |
|||
|
|||
Entities are one of the three principle elements of an Entity Component System architecture. They represent the individual "things" in your game or application. An entity has neither behavior nor data; instead, it identifies which pieces of data belong together. [Systems](ecs_systems.md) provide the behavior, and [components](ecs_components.md) store the data. |
|||
|
|||
An entity is essentially an ID. The easiest way to think of it is as a super lightweight [GameObject](https://docs.unity3d.com/Manual/class-GameObject.html) that does not even have a name by default. Entity IDs are stable; you can use them to store a reference to another component or entity. For example, a child entity in a hierarchy might need to reference its parent entity. |
|||
|
|||
An [EntityManager](xref:Unity.Entities.EntityManager) manages all of the entities in a [World](xref:Unity.Entities.World). An EntityManager maintains the list of entities and organizes the data associated with an entity for optimal performance. |
|||
|
|||
Although an entity does not have a type, groups of entities can be categorized by the types of data components associated with them. As you create entities and add components to them, the EntityManager keeps track of the unique combinations of components on the existing entities. Such a unique combination is called an __Archetype__. The EntityManager creates an [EntityArchetype](xref:Unity.Entities.EntityArchetype) struct as you add components to an entity. You can use existing `EntityArchetype`s to create new entities that conform to that archetype. You can also create an `EntityArchetype` in advance and use that to create entities. |
|||
|
|||
## Creating entities |
|||
|
|||
The easiest way to create an entity is in the Unity Editor. You can set ECS to convert both GameObjects placed in a Scene and Prefabs into entities at runtime. For more dynamic parts of your game or application, you can create spawning systems that create multiple entities in a job. Finally, you can use one of the [EntityManager.CreateEntity](xref:Unity.Entities.EntityManager.CreateEntity) functions to create entities one at a time. |
|||
|
|||
### Creating entities with an EntityManager |
|||
|
|||
Use one of the [EntityManager.CreateEntity](xref:Unity.Entities.EntityManager.CreateEntity) functions to create an entity. ECS creates the entity in the same World as the EntityManager. |
|||
|
|||
You can create entities one-by-one in the following ways: |
|||
|
|||
* Create an entity with components that use an array of [ComponentType](xref:Unity.Entities.ComponentType) objects. |
|||
* Create an entity with components that use an [EntityArchetype](xref:Unity.Entities.EntityArchetype). |
|||
* Copy an existing entity, including its current data, with [Instantiate](xref:Unity.Entities.EntityManager.Instantiate%28Unity.Entities.Entity%29) |
|||
* Create an entity with no components and then add components to it. (You can add components immediately or when additional components are needed.) |
|||
|
|||
You can also create multiple entities at a time: |
|||
|
|||
* Fill a NativeArray with new entities with the same archetype using [CreateEntity](xref:Unity.Entities.EntityManager.CreateEntity). |
|||
* Fill a NativeArray with copies of an existing entity, including its current data, using [Instantiate](xref:Unity.Entities.EntityManager.Instantiate%28Unity.Entities.Entity%29). |
|||
* Explicitly create chunks populated with a specified number of entities with a given archetype with [CreateChunk](xref:Unity.Entities.EntityManager.CreateChunk*). |
|||
|
|||
## Adding and removing components |
|||
|
|||
After an entity has been created, you can add or remove components. When you do this, the archetype of the affected entities change and the EntityManager must move altered data to a new chunk of memory, as well as condense the component arrays in the original chunks. |
|||
|
|||
Changes to an entity that cause structural changes — that is, adding or removing components that change the values of `SharedComponentData`, and destroying the entity — cannot be done inside a job because these could invalidate the data that the job is working on. Instead, you add the commands to make these types of changes to an [EntityCommandBuffer](xref:Unity.Entities.EntityCommandBuffer) and execute this command buffer after the job is complete. |
|||
|
|||
The EntityManager provides functions to remove a component from a single entity as well as all of the entities in a NativeArray. For more information, see the documentation on [Components](ecs_components.md). |
|||
|
|||
## Iterating entities |
|||
|
|||
Iterating over all entities that have a matching set of components, is at the center of the ECS architecture. See [Accessing entity Data](chunk_iteration.md). |
|
|||
--- |
|||
uid: ecs-entities-foreach |
|||
--- |
|||
|
|||
# Using Entities.ForEach |
|||
|
|||
Use the [Entities.ForEach] construction provided by the [SystemBase] class as a concise way to define and execute your algorithms over entities and their components. [Entities.ForEach] executes a lambda function you define over all the entities selected by an [entity query]. |
|||
|
|||
To execute a job lambda function, you either schedule the job using `Schedule()` and `ScheduleParallel()`, or execute it immediately (on the main thread) with `Run()`. You can use additional methods defined on [Entities.ForEach] to set the entity query as well as various job options. |
|||
|
|||
The following example illustrates a simple [SystemBase] implementation that uses [Entities.ForEach] to read one component (Velocity in this case) and write to another (Translation): |
|||
|
|||
[!code-cs[entities-foreach-example](../package/DocCodeSamples.Tests/LambdaJobExamples.cs#entities-foreach-example)] |
|||
|
|||
Note the use of the keywords `ref` and `in` on the parameters of the ForEach lambda function. Use `ref` for components that you write to, and `in` for components that you only read. Marking components as read-only helps the job scheduler execute your jobs more efficiently. |
|||
|
|||
## Selecting entities |
|||
|
|||
[Entities.ForEach] provides its own mechanism for defining the entity query used to select the entites to process. The query automatically includes any components you use as parameters of your lambda function. You can also use the `WithAll`, `WithAny`, and `WithNone` clauses to further refine which entities are selected. See [SystemBase.Entities] for the complete list of query options. |
|||
|
|||
The following example selects entities that have the components, Destination, Source, and LocalToWorld; and have at least one of the components, Rotation, Translation, or Scale; but which do not have a LocalToParent component. |
|||
|
|||
[!code-cs[entity-query](../package/DocCodeSamples.Tests/LambdaJobExamples.cs#entity-query)] |
|||
|
|||
In this example, only the Destination and Source components can be accessed inside the lambda function since they are the only components in the parameter list. |
|||
|
|||
### Accessing the EntityQuery object |
|||
|
|||
To access the [EntityQuery] object created by [Entities.ForEach], use [WithStoreEntityQueryInField(ref query)] with the ref parameter modifier. This function assigns a reference to the query to the field you provide. |
|||
|
|||
The following example illustrates how to access the EntityQuery object implicitly created for an [Entities.ForEach] construction. In this case, the example uses the EntityQuery object to invoke the [CalculateEntityCount()] method. The example uses this count to create a native array with enough space to store one value per entity selected by the query: |
|||
|
|||
[!code-cs[store-query](../package/DocCodeSamples.Tests/LambdaJobExamples.cs#store-query)] |
|||
|
|||
<a name="optional-components"></a> |
|||
### Optional components |
|||
|
|||
You cannot create a query specifying optional components (using WithAny<T,U>) and also access those components in the lambda function. If you need to read or write to a component that is optional, you can split the Entities.ForEach construction into multiple jobs, one for each combination of the optional components. For example, if you had 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 iterate by chunk using IJobChunk. |
|||
|
|||
<a name="change-filtering"></a> |
|||
### Change filtering |
|||
|
|||
In cases where you only want to process an entity component when another entity of that component has changed since the last time the current [SystemBase] instance has run, you can enable change filtering using WithChangeFilter<T>. The component type used in the change filter must either be in the lambda function parameter list or part of a WithAll<T> statement. |
|||
|
|||
[!code-cs[with-change-filter](../package/DocCodeSamples.Tests/LambdaJobExamples.cs#with-change-filter)] |
|||
|
|||
An entity query supports change filtering on up to two component types. |
|||
|
|||
Note that change filtering is applied at the chunk level. If any code accesses a component in a chunk with write access, then that component type in that chunk is marked as changed -- even if the code didn’t actually change any data. |
|||
|
|||
<a name="shared-component-filtering"></a> |
|||
### Shared component filtering |
|||
|
|||
Entities with shared components are grouped into chunks with other entities having the same value for their shared components. You can select groups of entities that have specific shared component values using the WithSharedComponentFilter() function. |
|||
|
|||
The following example selects entities grouped by a Cohort ISharedComponentData. The lambda function in this example sets a DisplayColor IComponentData component based on the entity’s cohort: |
|||
|
|||
[!code-cs[with-shared-component](../package/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, passing the new color to the lambda function as a captured variable. |
|||
|
|||
<a name="lambda-function"></a> |
|||
## Defining the ForEach function |
|||
|
|||
When you define the lambda function to use with [Entities.ForEach], you can declare parameters that the [SystemBase] class uses to pass in information about the current entity when it executes the function. |
|||
|
|||
A typical lambda function looks like: |
|||
|
|||
[!code-cs[lambda-params](../package/DocCodeSamples.Tests/LambdaJobExamples.cs#lambda-params)] |
|||
|
|||
You can pass up to eight parameters to an Entities.ForEach lambda function. The parameters must be grouped in the following order: |
|||
|
|||
1. Parameters passed-by-value first (no parameter modifiers) |
|||
2. Writable parameters second (`ref` parameter modifier) |
|||
3. Read-only parameters last (`in` parameter modifier) |
|||
|
|||
All components should use either the `ref` or the `in` parameter modifier keywords. Otherwise, the component struct passed to your function is a copy instead of a reference. This means an extra memory copy for read-only parameters and means that any changes to components you intended to update are silently thrown when the copied struct goes out of scope after the function returns. |
|||
|
|||
If your function does not obey these rules, 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` |
|||
|
|||
(Note that the error message cites the number of arguments as the issue even when the problem is the parameter order.) |
|||
|
|||
<a name="component-parameters"></a> |
|||
### Component parameters |
|||
|
|||
To access a component associated with an entity, you must pass a parameter of that component type to the lambda function. The compiler automatically adds all components passed to the function to the entity query as required components. |
|||
|
|||
To update a component value, you must pass it to the lambda function by reference using the `ref` keyword in the parameter list. (Without the `ref` keyword, any modifications would be made to a temporary copy of the component since it would be passed by value.) |
|||
|
|||
To designate a component passed to the lambda function as read-only, use the `in` keyword in the parameter list. |
|||
|
|||
**Note:** Using `ref` means that the components in the current chunk are marked as changed, even if the lambda function does not actually modify them. For efficiency, always designate components that your lambda function does not modify as read only using the `in` keyword. |
|||
|
|||
The following example passes a Source component parameter to the job as read-only, and a Destination component parameter as writable: |
|||
|
|||
[!code-cs[read-write-modifiers](../package/DocCodeSamples.Tests/LambdaJobExamples.cs#read-write-modifiers)] |
|||
|
|||
**Note:** Currently, you cannot pass chunk components to the Entities.ForEach lambda function. |
|||
|
|||
For dynamic buffers, use DynamicBuffer<T> rather than the Component type stored in the buffer: |
|||
|
|||
[!code-cs[dynamicbuffer](../package/DocCodeSamples.Tests/LambdaJobExamples.cs#dynamicbuffer)] |
|||
|
|||
<a name="named-parameters"></a> |
|||
### Special, named parameters |
|||
|
|||
In addition to components, you can pass the following special, named parameters to the Entities.ForEach lambda function, which are assigned values based on the entity the job is currently processing: |
|||
|
|||
* **`Entity entity`** — the Entity instance of the current entity. (The parameter can be named anything as long as the type is Entity.) |
|||
* **`int entityInQueryIndex`** — the index of the entity in the list of all entities selected by the query. 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. The entityInQueryIndex should also be used as the jobIndex for adding commands to a concurrent [EntityCommandBuffer]. |
|||
* **`int nativeThreadIndex`** — a unique index for the thread executing the current iteration of the lambda function. When you execute the lambda function using Run(), nativeThreadIndex is always zero. (Do not use `nativeThreadIndex` as the `jobIndex` of a concurrent [EntityCommandBuffer]; use `entityInQueryIndex`instead.) |
|||
|
|||
<a name="capturing-variables"></a> |
|||
## Capturing variables |
|||
|
|||
You can capture local variables for Entities.ForEach lambda functions. When you execute the function using a job (by calling one of the Schedule functions instead of Run) there are some restrictions on the captured variables and how you use them: |
|||
|
|||
* Only native containers and blittable types can be captured. |
|||
* 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 specify read-only access using `WithReadOnly(variable)`. |
|||
See [SystemBase.Entities] for more information about setting attributes for captured variables. The attributes you can specify include, `DeallocateOnJobCompletion`, `NativeDisableParallelForRestriction`, and others. [Entities.ForEach] provides these as functions because the C# language doesn't allow attibutes on local variables. |
|||
|
|||
**Note:** When executing the function with `Run()` you can write to captured variables that are not native containers. However, you should still use blittable types where possible so that the function can be compiled with [Burst]. |
|||
|
|||
## Dependencies |
|||
|
|||
By default, a system manages its ECS-related dependencies using its [Dependency] property. 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()] function. You can also manage job dependencies manually by passing a [JobHandle] to your `Schedule` functions, which then return the resulting dependency. See [Dependency] for more information. |
|||
|
|||
See [Job dependencies] for more general information about job dependencies. |
|||
|
|||
[SystemBase]: xref:Unity.Entities.SystemBase |
|||
[Dependency]: xref:Unity.Entities.SystemBase.Dependency |
|||
[race condition]: https://en.wikipedia.org/wiki/Race_condition |
|||
[Job dependencies]: xref:ecs-job-dependencies |
|||
[IJobParallelFor]: https://docs.unity3d.com/Manual/JobSystemParallelForJobs.html |
|||
[OnUpdate()]: xref:Unity.Entities.SystemBase.OnUpdate* |
|||
[Entities.ForEach]: xref:Unity.Entities.SystemBase.Entities |
|||
[SystemBase.Entities]: xref:Unity.Entities.SystemBase.Entities |
|||
[EntityCommandBuffer]:xref:Unity.Entities.EntityCommandBuffer |
|||
[native array]: https://docs.unity3d.com/ScriptReference/Unity.Collections.NativeArray_1.html |
|||
[EntityQuery]: xref:Unity.Entities.EntityQuery |
|||
[entity query]: xref:Unity.Entities.EntityQuery |
|||
[WithStoreEntityQueryInField(out query)]: xref:Unity.Entities.SystemBase.Entities |
|||
[CalculateEntityCount()]: xref:Unity.Entities.EntityQuery.CalculateEntityCount* |
|||
[Burst]: https://docs.unity3d.com/Packages/com.unity.burst@latest/index.html |
|
|||
--- |
|||
uid: ecs-entity-query |
|||
--- |
|||
# Using a EntityQuery to query data |
|||
|
|||
To read or write data, you must first find the data you want to change. The data in ECS is stored in components, which ECS groups together in memory according to the archetype of the entity to which they belong. You can use an [EntityQuery](xref:Unity.Entities.EntityQuery) to get a view of the ECS data that contains only the specific data you need for a given algorithm or process. |
|||
|
|||
You can use an EntityQuery to do the following: |
|||
|
|||
* Run a job to process the entities and components selected for the view |
|||
* Get a NativeArray that contains all of the selected entities |
|||
* Get NativeArrays of the selected components (by component type) |
|||
|
|||
The entity and component arrays an EntityQuery returns are guaranteed to be "parallel", that is, the same index value always applies to the same entity in any array. |
|||
|
|||
**Note:** The `SystemBase.Entities.ForEach` constructions create internal EntityQuery instances based on the component types and attributes you specify for these APIs. You cannot use a different EntityQuery object with `Entities.ForEach`, (though you can get the query object that an `Entities.ForEach` instance constructs and use it elsewhere). |
|||
|
|||
## Defining a query |
|||
|
|||
An EntityQuery query defines the set of component types that an archetype must contain for ECS to include its chunks and entities in the view. You can also exclude archetypes that contain specific types of components. |
|||
|
|||
For simple queries, you can create a EntityQuery based on an array of component types. The following example defines a EntityQuery that finds all entities with both RotationQuaternion and RotationSpeed components. |
|||
|
|||
``` c# |
|||
EntityQuery m_Query = GetEntityQuery(typeof(RotationQuaternion), |
|||
ComponentType.ReadOnly<RotationSpeed>()); |
|||
``` |
|||
|
|||
The query uses `ComponentType.ReadOnly<T>` instead of the simpler `typeof` expression to designate that the system does not write to RotationSpeed. Always specify read only when possible, because there are fewer constraints on read access to data, which can help the job scheduler execute the jobs more efficiently. |
|||
|
|||
### EntityQueryDesc |
|||
|
|||
For more complex queries, you can use an EntityQueryDesc object to create the EntityQuery. An EntityQueryDesc provides a flexible query mechanism to specify which archetypes to select based on the following sets of components: |
|||
|
|||
* `All`: All component types in this array must exist in the archetype |
|||
* `Any`: At least one of the component types in this array must exist in the archetype |
|||
* `None`: None of the component types in this array can exist in the archetype |
|||
|
|||
For example, the following query includes archetypes that contain the RotationQuaternion and RotationSpeed components, but excludes any archetypes that contain the Frozen component: |
|||
|
|||
``` c# |
|||
var query = new EntityQueryDesc |
|||
{ |
|||
None = new ComponentType[]{ typeof(Frozen) }, |
|||
All = new ComponentType[]{ typeof(RotationQuaternion), |
|||
ComponentType.ReadOnly<RotationSpeed>() } |
|||
} |
|||
EntityQuery m_Query = GetEntityQuery(query); |
|||
``` |
|||
|
|||
**Note:** Do not include optional components in the EntityQueryDesc. To handle optional components, use the `ArchetypeChunk.Has<T>()` method to determine whether a chunk contains the optional component or not. Because all entities within the same chunk have the same components, you only need to check whether an optional component exists once per chunk: not once per entity. |
|||
|
|||
### Query options |
|||
|
|||
When you create an EntityQueryDesc, you can set its `Options` variable. The options allow for specialized queries (normally you do not need to set them): |
|||
|
|||
* Default: No options set; the query behaves normally. |
|||
* `IncludePrefab`: Includes archetypes that contain the special Prefab tag component. |
|||
* `IncludeDisabled`: Includes archetypes that contain the special Disabled tag component. |
|||
* `FilterWriteGroup`: Considers the WriteGroup of any components in the query. |
|||
|
|||
When you set the `FilterWriteGroup` option, only entities with those components in a Write Group that are explicitly included in the query are included in the view. ECS excludes any entities that have any additional components from the same WriteGroup. |
|||
|
|||
In the following example, C2 and C3 are components in the same Write Group based on C1, and this query uses the FilterWriteGroup option that requires C1 and C3: |
|||
|
|||
``` c# |
|||
public struct C1: IComponentData{} |
|||
|
|||
[WriteGroup(C1)] |
|||
public struct C2: IComponentData{} |
|||
|
|||
[WriteGroup(C1)] |
|||
public struct C3: IComponentData{} |
|||
|
|||
// ... In a system: |
|||
var query = new EntityQueryDesc{ |
|||
All = new ComponentType{typeof(C1), ComponentType.ReadOnly<C3>()}, |
|||
Options = EntityQueryDescOptions.FilterWriteGroup |
|||
}; |
|||
var m_Query = GetEntityQuery(query); |
|||
``` |
|||
|
|||
This query excludes any entities with both C2 and C3 because C2 is not explicitly included in the query. While you can use `None` to design this into the query, doing it through a Write Group provides an important benefit: you don't need to change the queries other systems use (as long as these systems also use Write Groups). |
|||
|
|||
Write Groups are a mechanism that you can use to extend existing systems. For example, if C1 and C2 are defined in another system (perhaps part of a library that you don't control), you can put C3 into the same Write Group as C2 to change how C1 is updated. For any entities which you add to the C3 component, the system updates C1 and the original system does not. For other entities without C3, the original system updates C1 as before. |
|||
|
|||
For more information, see [Write Groups](ecs_write_groups.md). |
|||
|
|||
### Combining queries |
|||
|
|||
To combine multiple queries, you can pass an array of EntityQueryDesc objects rather than a single instance. You must use a logical OR operation to combine each query. The following example selects any archetypes that contain a RotationQuaternion component or a RotationSpeed component (or both): |
|||
|
|||
```c# |
|||
var query0 = new EntityQueryDesc |
|||
{ |
|||
All = new ComponentType[] {typeof(RotationQuaternion)} |
|||
}; |
|||
|
|||
var query1 = new EntityQueryDesc |
|||
{ |
|||
All = new ComponentType[] {typeof(RotationSpeed)} |
|||
}; |
|||
|
|||
EntityQuery m_Query |
|||
= GetEntityQuery(new EntityQueryDesc[] {query0, query1}); |
|||
``` |
|||
|
|||
## Creating a EntityQuery |
|||
|
|||
Outside of a system class, you can create a EntityQuery with the `EntityManager.CreateEntityQuery()` function as follows: |
|||
|
|||
``` c# |
|||
EntityQuery m_Query = CreateEntityQuery(typeof(RotationQuaternion), |
|||
ComponentType.ReadOnly<RotationSpeed>()); |
|||
``` |
|||
|
|||
However, in a system class, you must use the `GetEntityQuery()` function for use with an IJobChunk job: |
|||
|
|||
``` c# |
|||
public class RotationSpeedSystem : SystemBase |
|||
{ |
|||
private EntityQuery m_Query; |
|||
protected override void OnCreate() |
|||
{ |
|||
m_Query = GetEntityQuery(typeof(RotationQuaternion), |
|||
ComponentType.ReadOnly<RotationSpeed>()); |
|||
} |
|||
//… |
|||
} |
|||
``` |
|||
|
|||
If you plan to reuse the same view, cache the EntityQuery instance, instead of creating a new one for each use. For example, in a system, you can create the EntityQuery in the system’s `OnCreate()` function and store the result in an instance variable. The `m_Query` variable in the above example is used for this purpose. |
|||
|
|||
Note that queries created for a system are cached by the system. `GetEntityQuery()` returns the existing query if one already exists rather than creating a new one. However, filter settings are not considered when evaluating whether two queries are the same. In additon, if you set filters on a query, the same filters are set the next time you access that same query with `GetEntityQuery()`. Use `ResetFilter()` to clear the existing filters. |
|||
|
|||
## Defining filters |
|||
|
|||
You can filter the view as well as defining which components must be included or excluded from the query. You can specify the following types of filters: |
|||
|
|||
* **Shared component filter**: Filter the set of entities based on specific values of a shared component. |
|||
* **Change filter**: Filter the set of entities based on whether the value of a specific component type has changed. |
|||
|
|||
The filters you set remain in effect until you call `ResetFilter()` on the query object. |
|||
|
|||
### Shared component filters |
|||
|
|||
To use a shared component filter, include the shared component in the EntityQuery (along with other needed components), and then call the `SetFilter()` function. Then pass in a struct of the same ISharedComponent type that contains the values to select. All values must match. You can add up to two different shared components to the filter. |
|||
|
|||
You can change the filter at any time, but if you change the filter, it does not change any existing arrays of entities or components that you received from the group `ToComponentDataArray()` or `ToEntityArray()` functions. You must recreate these arrays. |
|||
|
|||
The following example defines a shared component named SharedGrouping and a system that only processes entities that have the Group field set to `1`. |
|||
|
|||
```c# |
|||
struct SharedGrouping : ISharedComponentData |
|||
{ |
|||
public int Group; |
|||
} |
|||
|
|||
class ImpulseSystem : SystemBase |
|||
{ |
|||
EntityQuery m_Query; |
|||
|
|||
protected override void OnCreate(int capacity) |
|||
{ |
|||
m_Query = GetEntityQuery(typeof(Position), |
|||
typeof(Displacement), |
|||
typeof(SharedGrouping)); |
|||
} |
|||
|
|||
protected override void OnUpdate() |
|||
{ |
|||
// Only iterate over entities that have the SharedGrouping data set to 1 |
|||
m_Query.SetFilter(new SharedGrouping { Group = 1 }); |
|||
|
|||
var positions = m_Query.ToComponentDataArray<Position>(Allocator.Temp); |
|||
var displacememnts = m_Query.ToComponentDataArray<Displacement>(Allocator.Temp); |
|||
|
|||
for (int i = 0; i != positions.Length; i++) |
|||
positions[i].Value = positions[i].Value + displacememnts[i].Value; |
|||
} |
|||
} |
|||
``` |
|||
|
|||
### Change filters |
|||
|
|||
If you only need to update entities when a component value has changed, you can add that component to the EntityQuery filter using the `SetFilterChanged()` function. For example, the following EntityQuery only includes entities from chunks that another system has already written to the Translation component: |
|||
|
|||
``` c# |
|||
protected override void OnCreate(int capacity) |
|||
{ |
|||
m_Query = GetEntityQuery(typeof(LocalToWorld), |
|||
ComponentType.ReadOnly<Translation>()); |
|||
m_Query.SetFilterChanged(typeof(Translation)); |
|||
} |
|||
|
|||
``` |
|||
|
|||
**Note:** For efficiency, the change filter applies to whole chunks, not individual entities. The change filter also only checks whether a system has run that declared write access to the component, not whether it actually changed any data. In other words, if another job which had the ability to write to that type of component accesses the chunk, then the change filter includes all entities in that chunk. This is why you should always declare read only access to components that you do not need to modify. |
|||
|
|||
## Executing the query |
|||
|
|||
An EntityQuery executes its query when you use the EntityQuery in a job or you call one of the EntityQuery methods that returns arrays of entities, components, or chunks in the view: |
|||
|
|||
* `ToEntityArray()` returns an array of the selected entities. |
|||
* `ToComponentDataArray<T>` returns an array of the components of type `T` for the selected entities. |
|||
* `CreateArchetypeChunkArray()` returns all of the chunks that contain the selected entities. Because a query operates on archetypes, shared component values, and change filters, which are all identical for all the entities in a chunk, the set of entities stored win the returned set of chunks is exactly the same as the set of entities `ToEntityArray()` returns . |
|||
|
|||
<!-- TODO: Discuss using the Job versions of these functions. --> |
|||
|
|||
### In jobs |
|||
|
|||
In a system that schedules an IJobChunk job, pass the EntityQuery object to the job's `ScheduleParallel()` or `ScheduleSingle()` methods. In the following example, from the HelloCube IJobChunk sample, the `m_Query` argument is the EntityQuery object |
|||
|
|||
``` c# |
|||
// OnUpdate runs on the main thread. |
|||
protected override void OnUpdate() |
|||
{ |
|||
var rotationType |
|||
= GetArchetypeChunkComponentType<Rotation>(false); |
|||
var rotationSpeedType |
|||
= GetArchetypeChunkComponentType<RotationSpeed>(true); |
|||
|
|||
var job = new RotationSpeedJob() |
|||
{ |
|||
RotationType = rotationType, |
|||
RotationSpeedType = rotationSpeedType, |
|||
DeltaTime = Time.deltaTime |
|||
}; |
|||
|
|||
return job.ScheduleParallel(m_Query, this.Dependency); |
|||
} |
|||
``` |
|||
|
|||
An EntityQuery uses jobs internally to create the required arrays. When you pass the group to one of the `Schedule()` methods, ECS schedules the EntityQuery jobs along with the system's own jobs and as such you can take advantage of parallel processing. |
|
|||
--- |
|||
uid: ecs-gameplay |
|||
--- |
|||
# Creating Gameplay with ECS |
|||
|
|||
<!-- |
|||
> Synopsis: Introductory topic about using ECS make things happen. Should probably put any discussions of hybrid versus all-ecs solutions here (unless such discussions become too involved and need thier own page) |
|||
> |
|
|||
--- |
|||
uid: ecs-job-dependencies |
|||
--- |
|||
|
|||
# Job dependencies |
|||
|
|||
Unity analyzes the data dependencies of each system based on the ECS components that the system reads and writes. If a system that updates earlier in the frame reads data that a later system writes, or writes data that a later system reads, then the second system depends on the first. To prevent [race conditions], the job scheduler makes sure that all the jobs a system depends on have finished before it runs that system's jobs. |
|||
|
|||
A system's [Dependency] property is a [JobHandle] that represents the ECS-related dependencies of the system. Before [OnUpdate()], the [Dependency] property reflects the incoming dependencies that the system has on prior jobs. By default, the system updates the [Dependency] property based on the components each job reads and writes as you schedule jobs in a system. |
|||
|
|||
To override this default behavior, use the overloaded versions of [Entities.ForEach] and [Job.WithCode] that take job dependencies as an parameter and return the updated dependencies as a [JobHandle]. When you use the explicit versions of these constructions, ECS does not automatically combine the job handles with the system's [Dependency] property. You must combine them manually when required. |
|||
|
|||
Note that the system [Dependency] property does not track the dependencies that a job might have on data passed through [NativeArrays] or other similar containers. If you write a NativeArray in one job, and read that array in another, you must manually add the JobHandle of the first job as a dependency of the second (typically by using [JobHandle.CombineDependencies]). |
|||
|
|||
When you call [Entities.ForEach.Run()] the job scheduler completes all scheduled jobs that the system depends on before starting the ForEach iteration. If you also use [WithStructuralChanges()] as part of the construction, then the job scheduler completes all running and scheduled jobs. Structural changes also invalidate any direct references to component data. See [sync points] for more information. |
|||
|
|||
See [JobHandle and dependencies] for more information. |
|||
|
|||
[Dependency]: xref:Unity.Entities.SystemBase.Dependency |
|||
[race conditions]: https://en.wikipedia.org/wiki/Race_condition |
|||
[IJobParallelFor]: https://docs.unity3d.com/Manual/JobSystemParallelForJobs.html |
|||
[OnUpdate()]: xref:Unity.Entities.SystemBase.OnUpdate* |
|||
[JobHandle]: https://docs.unity3d.com/ScriptReference/Unity.Jobs.JobHandle.html |
|||
[NativeArrays]: https://docs.unity3d.com/ScriptReference/Unity.Collections.NativeArray_1.html |
|||
[C# Job System]: https://docs.unity3d.com/Manual/JobSystem.html |
|||
[WithStructuralChanges()]: xref:Unity.Entities.SystemBase.Entities |
|||
[Entities.ForEach.Run()]: xref:Unity.Entities.SystemBase.Entities |
|||
[Entities.ForEach]: xref:Unity.Entities.SystemBase.Entities |
|||
[JobHandle.CombineDependencies]: https://docs.unity3d.com/ScriptReference/Unity.Jobs.JobHandle.CombineDependencies.html |
|||
[Job.WithCode]: xref:Unity.Entities.SystemBase.Job |
|||
[sync points]: xref:ecs-sync-points |
|||
[JobHandle and dependencies]: https://docs.unity3d.com/Manual/JobSystemJobDependencies.html |
|||
|
|||
|
|||
|
|||
|
|
|||
--- |
|||
uid: ecs-job-extensions |
|||
--- |
|||
# Job extensions |
|||
|
|||
The Unity C# job system lets you run code on multiple threads. The system provides scheduling, parallel processing, and multi-threaded safety. The job system is a core Unity module that provides the general purpose interfaces and classes to create and run jobs (whether or not you are using ECS). |
|||
|
|||
These interfaces include: |
|||
|
|||
* [IJob](https://docs.unity3d.com/ScriptReference/Unity.Jobs.IJob.html): Create a job that runs on any thread or core, which the job system scheduler determines. |
|||
* [IJobParallelFor](https://docs.unity3d.com/ScriptReference/Unity.Jobs.IJobParallelFor.html): Create a job that can run on multiple threads in parallel to process the elements of a [NativeContainer](https://docs.unity3d.com/Manual/JobSystemNativeContainer.html). |
|||
* [IJobExtensions](https://docs.unity3d.com/ScriptReference/Unity.Jobs.IJobExtensions.html): Provides extension methods to run IJobs. |
|||
* [IJobParalllelForExtensions](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. |
|||
|
|||
The [Jobs package](https://docs.unity3d.com/Packages/com.unity.jobs@latest) extends the job system to support ECS. It contains: |
|||
|
|||
* [IJobParallelForDeferExtensions](https://docs.unity3d.com/Packages/com.unity.jobs@latest?preview=1&subfolder=/api/Unity.Jobs.IJobParallelForDeferExtensions.html) |
|||
* [IJobParallelForFilter](https://docs.unity3d.com/Packages/com.unity.jobs@latest?preview=1&subfolder=/api/Unity.Jobs.IJobParallelForFilter.html) |
|||
* [JobParallelIndexListExtensions](https://docs.unity3d.com/Packages/com.unity.jobs@latest?preview=1&subfolder=/api/Unity.Jobs.JobParallelIndexListExtensions.html) |
|||
* [JobStructProduce<T>](https://docs.unity3d.com/Packages/com.unity.jobs@latest?preview=1&subfolder=/api/Unity.Jobs.JobParallelIndexListExtensions.JobStructProduce-1.html) |
|||
|
|
|||
--- |
|||
uid: ecs-jobs |
|||
--- |
|||
# Jobs in ECS |
|||
|
|||
ECS uses the [C# Job System] extensively. Whenever possible, you should use the jobs in your system code. The [SystemBase] class provides [Entities.ForEach] and [Job.WithCode] to help implement your game logic as multithreaded code. In more complex situations, you can use [IJobChunk]. |
|||
|
|||
For example, the following system updates positions: |
|||
|
|||
using Unity.Burst; |
|||
using Unity.Collections; |
|||
using Unity.Entities; |
|||
using Unity.Jobs; |
|||
using Unity.Transforms; |
|||
|
|||
public class MovementSpeedSystem : SystemBase |
|||
{ |
|||
// OnUpdate runs on the main thread. |
|||
protected override void OnUpdate() |
|||
{ |
|||
Entities |
|||
.ForEach((ref Translation position, in MovementSpeed speed) => |
|||
{ |
|||
float3 displacement = speed.Value * dt; |
|||
position = new Translation(){ |
|||
Value = position.Value + displacement |
|||
}; |
|||
}) |
|||
.ScheduleParallel(); |
|||
} |
|||
} |
|||
|
|||
|
|||
For more information about systems, see [ECS Systems](ecs_systems.md). |
|||
|
|||
[C# Job System]: https://docs.unity3d.com/Manual/JobSystem.html |
|||
[SystemBase]: xref:Unity.Entities.SystemBase |
|||
[Entities.ForEach]: ecs_entities_foreach.md |
|||
[Job.WithCode]: ecs_job_withcode.md |
|||
[IJobChunk]: chunk_iteration_job.md |
|
|||
--- |
|||
uid: ecs-job-withcode |
|||
--- |
|||
|
|||
# Using Job.WithCode |
|||
|
|||
The [Job.WithCode] construction provided by the [SystemBase] class is an easy way to run a function as a single background job. You can also run [Job.WithCode] on the main thread and still take advantage of [Burst] compilation to speed up execution. |
|||
|
|||
The following example uses one [Job.WithCode] lambda function to fill a [native array] with random numbers and another job to add those numbers together: |
|||
|
|||
[!code-cs[job-with-code-example](../package/DocCodeSamples.Tests/LambdaJobExamples.cs#job-with-code-example)] |
|||
|
|||
**Note:** To run a parallel job, implement [IJobFor], which you can schedule using [ScheduleParallel()] in the system [OnUpdate()] function. |
|||
|
|||
## Variables |
|||
|
|||
You cannot pass parameters to the [Job.WithCode] lambda function or return a value. Instead, you can capture local variables in your [OnUpdate()] function. |
|||
|
|||
When you schedule your job to run in the [C# Job System] using `Schedule()`, there are additional restrictions: |
|||
|
|||
* Captured variables must be declared as [NativeArray] -- or other [native container] -- or a [blittable] type. |
|||
* To return data, you must write the return value to a captured [native array], even if the data is a single value. (Note that you can write to any captured variable when executing with `Run()`.) |
|||
|
|||
[Job.WithCode] provides a set of functions to apply read-only and safety attributes to your captured [native container] variables. For example, you can use `WithReadOnly` to designate that you don't update the container and `WithDeallocateOnJobCompletion` to automatically dispose a container after the job finshes. ([Entities.ForEach] provides the same functions.) |
|||
|
|||
See [Job.WithCode] for more information about these modifiers and attributes. |
|||
|
|||
## Executing the function |
|||
|
|||
You have two options to execute your lambda function: |
|||
* `Schedule()` -- executes the function as a single, non-parallel job. |
|||
Scheduling a job runs the code on a background thread and thus can take better advantage of available CPU resources. |
|||
* `Run()` -- executes the function immediately on the main thread. |
|||
In most cases the [Job.WithCode] can be [Burst] compiled so executing code can be faster inside [Job.WithCode] even though it is still run on the main thread. |
|||
|
|||
Note that calling `Run()` automatically completes all the dependencies of the [Job.WithCode] construction. If you do not explicitly pass a [JobHandle] object to `Run()` the system assumes that the current [Dependency] property represents the function's dependencies. (Pass in a new [JobHandle] if the function has no dependencies.) |
|||
|
|||
## Dependencies |
|||
|
|||
By default, a system manages its ECS-related dependencies using its [Dependency] property. 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()] function. You can also manage job dependencies manually by passing a [JobHandle] to your `Schedule` functions, which then return the resulting dependency. See [Dependency] for more information. |
|||
|
|||
See [Job dependencies] for more general information about job dependencies. |
|||
|
|||
[C# Job System]: https://docs.unity3d.com/Manual/JobSystem.html |
|||
[Burst]: https://docs.unity3d.com/Packages/com.unity.burst@latest/index.html |
|||
[Dependency]: xref:Unity.Entities.SystemBase.Dependency |
|||
[race condition]: https://en.wikipedia.org/wiki/Race_condition |
|||
[Job dependencies]: xref:ecs-job-dependencies |
|||
[IJobFor]: https://docs.unity3d.com/Manual/JobSystemCreatingJobs.html |
|||
[ScheduleParallel()]: https://docs.unity3d.com/ScriptReference/Unity.Jobs.IJobForExtensions.ScheduleParallel.html |
|||
[OnUpdate()]: xref:Unity.Entities.SystemBase.OnUpdate* |
|||
[blittable]: https://docs.microsoft.com/en-us/dotnet/framework/interop/blittable-and-non-blittable-types |
|||
[sync point]: xref:sync-point |
|||
[JobHandle]: https://docs.unity3d.com/ScriptReference/Unity.Jobs.JobHandle.html |
|||
[Job.WithCode]: xref:Unity.Entities.SystemBase.Job |
|||
[Entities.ForEach]: xref:Unity.Entities.SystemBase.Entities |
|||
[SystemBase.Entities]: xref:Unity.Entities.SystemBase.Entities |
|||
[SystemBase]: xref:Unity.Entities.SystemBase |
|||
[NativeArray]: https://docs.unity3d.com/ScriptReference/Unity.Collections.NativeArray_1.html |
|||
[native array]: https://docs.unity3d.com/ScriptReference/Unity.Collections.NativeArray_1.html |
|||
[native container]: https://docs.unity3d.com/Manual/JobSystemNativeContainer.html |
|
|||
--- |
|||
uid: ecs-data-lookup |
|||
--- |
|||
|
|||
# Looking up data |
|||
|
|||
The most efficient way to access and modify your ECS data is to use a system with an entity query and job. This provides the best utilization of CPU resources with the fewest memory cache misses. In fact, one of the goals of your data design should be to perform the bulk of your data transformation using the most efficient, fastest path. However, sometimes you need to access an arbitrary component of an arbitrary entity at an arbitrary point in your program. |
|||
|
|||
Given an Entity object, you can look up data in its [IComponentData] and [dynamic buffers]. The method varies depending on whether your code executes in a system using [Entities.ForEach] or using an [IJobChunk] job, or elsewhere on the main thread. |
|||
|
|||
## Looking up entity data in a system |
|||
|
|||
Use [GetComponent<T>(Entity)] to look up data stored in a component of an arbitrary entity from inside a system's [Entities.ForEach] or [Job.WithCode] function. |
|||
|
|||
For example, if you have Target components with an Entity field identifying the entity to target, you can use the following code to rotate the tracking entities toward their target: |
|||
|
|||
[!code-cs[lookup-foreach](../package/DocCodeSamples.Tests/LookupDataExamples.cs#lookup-foreach)] |
|||
|
|||
Accessing data stored in [dynamic buffers] requires an extra step. You must declare a local variable of type [BufferFromEntity] in your [OnUpdate()] method. You can then "capture" the local variable in your lambda function. |
|||
|
|||
[!code-cs[lookup-foreach-buffer](../package/DocCodeSamples.Tests/LookupDataExamples.cs#lookup-foreach-buffer)] |
|||
|
|||
|
|||
## Looking up entity data in IJobChunk |
|||
|
|||
To randomly access component data in an IJobChunk or other job struct, use one of the following types to get an array-like interface to component, indexed by [Entity] object: |
|||
|
|||
* [ComponentDataFromEntity] |
|||
* [BufferFromEntity] |
|||
|
|||
Declare a field of type [ComponentDataFromEntity] or [BufferFromEntity], and set the value of the field before scheduling the job. |
|||
|
|||
For example, if you had Target components with an Entity field identifying the entities to target, you could add the following field to your job struct to look up the world position of the targets: |
|||
|
|||
[!code-cs[lookup-ijobchunk-declare](../package/DocCodeSamples.Tests/LookupDataExamples.cs#lookup-ijobchunk-declare)] |
|||
|
|||
Note that this declaration uses the [ReadOnly] attribute. You should always declare ComponentDataFromEntity<T> objects as read-only unless you do write to the components you access. |
|||
|
|||
You can set this field when scheduling the job as follows: |
|||
|
|||
[!code-cs[lookup-ijobchunk-set](../package/DocCodeSamples.Tests/LookupDataExamples.cs#lookup-ijobchunk-set)] |
|||
|
|||
Inside the job's `Execute()` function, you can lookup the value of a component using an Entity object: |
|||
|
|||
[!code-cs[lookup-ijobchunk-read](../package/DocCodeSamples.Tests/LookupDataExamples.cs#lookup-ijobchunk-read)] |
|||
|
|||
The following, full example shows a system that moves entities that have a Target field containing the Entity object of their target towards the current location of the target: |
|||
|
|||
[!code-cs[lookup-ijobchunk](../package/DocCodeSamples.Tests/LookupDataExamples.cs#lookup-ijobchunk)] |
|||
|
|||
## Data access errors |
|||
|
|||
If the data you are looking up overlaps the data you are directly reading and writing in the job, then random access can lead to race conditions and subtle bugs. When you are sure that there is no overlap between the specific entity data you are reading or writing directly in the job and the specific entity data you are reading or writing randomly, then you can mark the accessor object with the [NativeDisableParallelForRestriction] attribute. |
|||
|
|||
[dynamic buffers]: xref:ecs-dynamic-buffers |
|||
[GetComponent<T>(Entity)]: xref:Unity.Entities.SystemBase.GetComponent``1(Unity.Entities.Entity) |
|||
[Entity]: xref:Unity.Entities.Entity |
|||
[ComponentDataFromEntity]: xref:Unity.Entities.ComponentDataFromEntity`1 |
|||
[BufferFromEntity]: xref:Unity.Entities.BufferFromEntity`1 |
|||
[Why ECS]: http://www.example.com#need-to-add-this-page |
|||
[IComponentData]: xref:ecs-component-data |
|||
[dynamic buffers]: xref:ecs-dynamic-buffers |
|||
[Entities.ForEach]: xref:Unity.Entities.SystemBase.Entities |
|||
[OnUpdate()]: xref:Unity.Entities.SystemBase.OnUpdate* |
|||
[IJobChunk]: xref:Unity.Entities.IJobChunk |
|||
[ReadOnly]: https://docs.unity3d.com/ScriptReference/Unity.Collections.ReadOnlyAttribute.html |
|||
[NativeDisableParallelForRestriction]: https://docs.unity3d.com/ScriptReference/Unity.Collections.NativeDisableParallelForRestrictionAttribute.html |
|
|||
--- |
|||
uid: ecs-profiling |
|||
--- |
|||
# Profiling |
|||
|
|||
> Synopsis: All about profiling ECS programs |
|||
|
|
|||
--- |
|||
uid: ecs-systems |
|||
--- |
|||
# Systems |
|||
|
|||
A **System**, the *S* in ECS, provides the logic that transforms the component data from its current state to its next state — for example, a system might update the positions of all moving entities by their velocity multiplied by the time interval since the previous update. |
|||
|
|||
![](images/BasicSystem.png) |
|||
|
|||
## Instantiating systems |
|||
|
|||
Unity ECS automatically discovers system classes in your project and instantiates them at runtime. It adds each discovered system to one of the default system groups. You can use [system attributes] to specify the parent group of a system and the order of that system within the group . If you do not specify a parent, Unity adds the system to the Simulation system group of the default world in a deterministic, but unspecified, order. You can also use an attribute to disable automatic creation. |
|||
|
|||
A system's update loop is driven by its parent [ComponentSystemGroup]. A ComponentSystemGroup is, itself, a specialized kind of system that is responsible for updating its child systems. Groups can be nested. Systems derive their [time] data from the [World] they are running in; time is updated by the [UpdateWorldTimeSystem]. |
|||
|
|||
You can view the system configuration using the Entity Debugger window (menu: **Window** > **Analysis** > **Entity Debugger**). |
|||
|
|||
<a name="types"></a> |
|||
## System types |
|||
|
|||
Unity ECS provides several types of systems. In general, the systems you write to implement your game behavior and data transformations will extend [SystemBase]. The other system classes have specialized purposes. You typically use existing instances of the [EntityCommandBufferSystem] and [ComponentSystemGroup] classes. |
|||
|
|||
* [SystemBase] -- the base class to implement when creating systems. |
|||
* [EntityCommandBufferSystem] -- provides [EntityCommandBuffer] instances for other systems. Each of the default system groups maintains an Entity Command Buffer System at the beginning and end of its list of child systems. This allows you to group structural changes so that they incur fewer [syncronization points] in a frame. |
|||
* [ComponentSystemGroup] -- provides nested organization and update order for other systems. Unity ECS creates several Component System Groups by default. |
|||
* [GameObjectConversionSystem] -- converts GameObject-based, in-Editor representations of your game to efficient, entity-based, runtime representations. Game conversion systems run in the Unity Editor. |
|||
|
|||
**Important:** The [ComponentSystem] and [JobComponentSystem] classes, along with [IJobForEach], are being phased out of the DOTS API, but have not been officially deprecated yet. Use [SystemBase] and [Entities.ForEach] instead. |
|||
|
|||
|
|||
[ComponentSystemGroup]: xref:ecs-system-update-order |
|||
[Entities.ForEach]: xref:Unity.Entities.SystemBase.Entities |
|||
[Job.WithCode]: xref:Unity.Entities.SystemBase.Job |
|||
[EntityCommandBufferSystem]: xref:ecs-entity-command-buffer |
|||
[EntityCommandBuffer]: xref:Unity.Entities.EntityCommandBuffer |
|||
[IJobChunk]: xref:Unity.Entities.IJobChunk) |
|||
[OnCreate()]: xref:Unity.Entities.ComponentSystemBase.OnCreate* |
|||
[OnDestroy()]: xref:Unity.Entities.ComponentSystemBase.OnDestroy* |
|||
[OnStartRunning()]: xref:Unity.Entities.ComponentSystemBase.OnStartRunning* |
|||
[OnStopRunning()]: xref:Unity.Entities.ComponentSystemBase.OnStopRunning* |
|||
[OnUpdate()]: xref:Unity.Entities.SystemBase.OnUpdate* |
|||
[syncronization points]: xref:sync-points |
|||
[system attributes]: system_update_order.md#attributes |
|||
[SystemBase]: xref:Unity.Entities.SystemBase |
|||
[World]: xref:Unity.Entities.World |
|||
[GameObject conversion systems]: gp_overview.md |
|||
[GameObjectConversionSystem]: gp_overview.md |
|||
[time]: xref:Unity.Entities.Core.TimeData |
|||
[World]: xref:Unity.Entities.World |
|||
[UpdateWorldTimeSystem]: xref:Unity.Entities.UpdateWorldTimeSystem |
|||
[system events]: #system-events |
|||
[C# Job System]: https://docs.unity3d.com/Manual/JobSystem.html |
|||
[system groups]: system_update_order.md#groups |
|||
[system attributes]: system_update_order.md#attributes |
|||
[ComponentSystem]: https://docs.unity3d.com/Packages/com.unity.entities@0.5/manual/entity_iteration_foreach.html |
|||
[IJobForEach]: https://docs.unity3d.com/Packages/com.unity.entities@0.5/manual/entity_iteration_job.html |
|||
[JobComponentSystem]: https://docs.unity3d.com/Packages/com.unity.entities@0.5/manual/entities_job_foreach.html |
|||
|
|
|||
--- |
|||
uid: ecs-testing |
|||
--- |
|||
# Testing ECS code |
|||
|
|||
> Synopsis: Using the Unity test framework with ECS. Tips for testing ECS code effectively. |
|||
|
|
|||
--- |
|||
uid: ecs-writegroups |
|||
--- |
|||
|
|||
# Write groups |
|||
|
|||
A common ECS pattern is for a system to read one set of **input** components and write to another component as its **output**. However, in some cases, you might want to override the output of a system, and use a different system based on a different set of inputs to update the output component. Write groups provide a mechanism for one system to override another, even when you cannot change the other system. |
|||
|
|||
The write group of a target component type consists of all other component types that ECS applies the [`WriteGroup` attribute](xref:Unity.Entities.WriteGroupAttributes) to, with that target component type as the argument. As a system creator, you can use write groups so that your system's users can exclude entities that your system would otherwise select and process. This filtering mechanism lets system users update components for the excluded entities based on their own logic, while letting your system operate normally on the rest. |
|||
|
|||
To make use of write groups, you must use the [write group filter option](xref:Unity.Entities.EntityQueryOptions) on the queries in your system. This excludes all entities from the query that have a component from a write group of any of the components that are marked as writable in the query. |
|||
|
|||
To override a system that uses write groups, mark your own component types as part of the write group of the output component type of that system. The original system ignores any entities that have your components and you can update the data of those entities with your own systems. |
|||
|
|||
## Write groups example |
|||
In this example, you use an external package to color all characters in your game depending on their state of health. For this, there are two components in the package: `HealthComponent` and `ColorComponent`. |
|||
|
|||
```csharp |
|||
public struct HealthComponent : IComponentData |
|||
{ |
|||
public int Value; |
|||
} |
|||
|
|||
public struct ColorComponent : IComponentData |
|||
{ |
|||
public float4 Value; |
|||
} |
|||
``` |
|||
|
|||
Additionally, there are two systems in the package: |
|||
1. The `ComputeColorFromHealthSystem`, which reads from `HealthComponent` and writes to `ColorComponent` |
|||
1. The `RenderWithColorComponent`, which reads from `ColorComponent` |
|||
|
|||
To represent when a player uses a power-up and their character becomes invincible, you attach an `InvincibleTagComponent` to the character's entity. In this case, the character's color should change to a separate, different color, which the above example does not accommodate. |
|||
|
|||
You can create your own system to override the `ColorComponent` value, but ideally `ComputeColorFromHealthSystem` would not compute the color for your entity to begin with. It should ignore any entity that has `InvincibleTagComponent`. This becomes more relevant when there are thousands of players on the screen. Unfortunately, the system is from another package which does not know about the `InvincibleTagComponent`. This is when a write group is useful. It allows a system to ignore entities in a query when you know that the values it computes would be overridden anyway. There are two things you need to support this: |
|||
|
|||
1. The `InvincibleTagComponent` must marked as part of the write group of `ColorComponent`: |
|||
|
|||
```csharp |
|||
[WriteGroup(typeof(ColorComponent))] |
|||
struct InvincibleTagComponent : IComponentData {} |
|||
``` |
|||
|
|||
The write group of `ColorComponent` consists of all component types that have the `WriteGroup` attribute with `typeof(ColorComponent)` as the argument. |
|||
1. The `ComputeColorFromHealthSystem` must explicitly support write groups. To achieve this, the system needs to specify the `EntityQueryOptions.FilterWriteGroup` option for all its queries. |
|||
|
|||
You could implement the `ComputeColorFromHealthSystem` like this: |
|||
|
|||
```csharp |
|||
... |
|||
protected override void OnUpdate() { |
|||
Entities |
|||
.WithName("ComputeColor") |
|||
.WithEntityQueryOptions(EntityQueryOptions.FilterWriteGroup) // support write groups |
|||
.ForEach((ref ColorComponent color, in HealthComponent health) => { |
|||
// compute color here |
|||
}).ScheduleParallel(); |
|||
} |
|||
... |
|||
``` |
|||
When this executes, the following happens: |
|||
1. The system detects that you write to `ColorComponent` because it is a by-reference parameter |
|||
1. It looks up the write group of `ColorComponent` and finds the `InvincibleTagComponent` in it |
|||
1. It excludes all entities that have an `InvincibleTagComponent` |
|||
|
|||
The benefit is that this allows the system to exclude entities based on a type that is unknown to the system and might live in a different package. |
|||
|
|||
**Note:** For more examples, see the `Unity.Transforms` code, which uses write groups for every component it updates, including `LocalToWorld`. |
|||
|
|||
## Creating write groups |
|||
To create write groups, add the `WriteGroup` attribute to the declarations of each component type in the write group. The `WriteGroup` attribute takes one parameter, which is the type of component that the components in the group uses to update. A single component can be a member of more than one write group. |
|||
|
|||
For example, if you have a system that writes to component `W` whenever there are components `A` or `B` on an entity, then you can define a write group for `W` as follows: |
|||
|
|||
```csharp |
|||
public struct W : IComponentData |
|||
{ |
|||
public int Value; |
|||
} |
|||
|
|||
[WriteGroup(typeof(W))] |
|||
public struct A : IComponentData |
|||
{ |
|||
public int Value; |
|||
} |
|||
|
|||
[WriteGroup(typeof(W))] |
|||
public struct B : IComponentData |
|||
{ |
|||
public int Value; |
|||
} |
|||
``` |
|||
|
|||
**Note:** You do not add the target of the write group (component `W` in the example above) to its own write group. |
|||
|
|||
## Enabling write group filtering |
|||
|
|||
To enable write group filtering, set the `FilterWriteGroups` flag on your job: |
|||
|
|||
```csharp |
|||
public class AddingSystem : SystemBase |
|||
{ |
|||
protected override void OnUpdate() { |
|||
Entities |
|||
// support write groups by setting EntityQueryOptions |
|||
.WithEntityQueryOptions(EntityQueryOptions.FilterWriteGroup) |
|||
.ForEach((ref W w, in B b) => { |
|||
// perform computation here |
|||
}).ScheduleParallel();} |
|||
} |
|||
``` |
|||
|
|||
For query description objects, set the flag when you create the query: |
|||
|
|||
```csharp |
|||
public class AddingSystem : SystemBase |
|||
{ |
|||
private EntityQuery m_Query; |
|||
|
|||
protected override void OnCreate() |
|||
{ |
|||
var queryDescription = new EntityQueryDesc |
|||
{ |
|||
All = new ComponentType[] { |
|||
ComponentType.ReadWrite<W>(), |
|||
ComponentType.ReadOnly<B>() |
|||
}, |
|||
Options = EntityQueryOptions.FilterWriteGroup |
|||
}; |
|||
m_Query = GetEntityQuery(queryDescription); |
|||
} |
|||
// Define IJobChunk struct and schedule... |
|||
} |
|||
``` |
|||
|
|||
When you enable write group filtering in a query, the query adds all components in a write group of a writable component to the `None` list of the query unless you explicitly add them to the `All` or `Any` lists. As a result, the query only selects an entity if it explicitly requires every component on that entity from a particular write group. If an entity has one or more additional components from that write group, the query rejects it. |
|||
|
|||
In the example code above, the query: |
|||
* Excludes any entity that has component `A`, because `W` is writable and `A` is part of the write group of `W`. |
|||
* Does not exclude any entity that has component `B`. Even though `B` is part of the write group of `W`, it is also explicitly specified in the `All` list. |
|||
|
|||
## Overriding another system that uses write groups |
|||
If a system uses write group filtering in its queries, you use your own system to override that system and write to those components. To override the system, add your own components to the write groups of the components to which the other system writes. Because write group filtering excludes any components in the write group that the query doesn't explicitly required, the other system ignores any entities that have your components. |
|||
|
|||
For example, if you want to set the orientation of your entities by specifying the angle and axis of rotation, you can create a component and a system to convert the angle and axis values into a quaternion and write that to the `Unity.Transforms.Rotation` component. To prevent the `Unity.Transforms` systems from updating `Rotation`, no matter what other components besides yours are present, you can put your component in the write group of `Rotation`: |
|||
|
|||
```csharp |
|||
using System; |
|||
using Unity.Collections; |
|||
using Unity.Entities; |
|||
using Unity.Transforms; |
|||
using Unity.Mathematics; |
|||
|
|||
[Serializable] |
|||
[WriteGroup(typeof(Rotation))] |
|||
public struct RotationAngleAxis : IComponentData |
|||
{ |
|||
public float Angle; |
|||
public float3 Axis; |
|||
} |
|||
``` |
|||
|
|||
You can then update any entities with the `RotationAngleAxis` component without contention: |
|||
|
|||
```csharp |
|||
using Unity.Burst; |
|||
using Unity.Entities; |
|||
using Unity.Jobs; |
|||
using Unity.Collections; |
|||
using Unity.Mathematics; |
|||
using Unity.Transforms; |
|||
|
|||
public class RotationAngleAxisSystem : SystemBase |
|||
{ |
|||
protected override void OnUpdate() |
|||
{ |
|||
Entities.ForEach((ref Rotation destination, in RotationAngleAxis source) => |
|||
{ |
|||
destination.Value |
|||
= quaternion.AxisAngle(math.normalize(source.Axis), source.Angle); |
|||
}).ScheduleParallel(); |
|||
} |
|||
} |
|||
``` |
|||
|
|||
## Extending another system that uses write groups |
|||
|
|||
If you want to extend another system rather than override it, or if you want to allow future systems to override or extend your system, then you can enable write group filtering on your own system. However, when you do this, neither system handles no combinations of components by default. You must explicitly query for and process each combination. |
|||
|
|||
In the previous example, it defined a write group that contains components `A` and `B` that targets component `W`. If you add a new component, called `C`, to the write group, then the new system that knows about `C` can query for entities that contain `C` and it does not matter if those entities also have components `A` or `B`. However, if the new system also enables write group filtering, that is no longer true. If you only require component `C`, then write group filtering excludes any entities with either `A` or `B`. Instead, you must explicitly query for each combination of components that make sense. **Note:** You can use the `Any` clause of the query when appropriate. |
|||
|
|||
```csharp |
|||
var query = new EntityQueryDesc |
|||
{ |
|||
All = new ComponentType[] { |
|||
ComponentType.ReadOnly<C>(), |
|||
ComponentType.ReadWrite<W>() |
|||
}, |
|||
Any = new ComponentType[] { |
|||
ComponentType.ReadOnly<A>(), |
|||
ComponentType.ReadOnly<B>() |
|||
}, |
|||
Options = EntityQueryOptions.FilterWriteGroup |
|||
}; |
|||
``` |
|||
|
|||
If you have any entities that contain combinations of components in the write group that are not explicitly mentioned, then the system that writes to the target of the write group, and its filters, does not handle them. However, if you have any if these type of entities, it is most likely a logical error in the program, and they should not exist. |
|
|||
--- |
|||
uid: ecs-entities-foreach |
|||
--- |
|||
|
|||
# SystemBase lambda functions |
|||
|
|||
This documentation has been moved to the [SystemBase] class documentation. Also, see [Creating systems] for more information on programming systems. |
|||
|
|||
|
|||
[Creating systems]: ecs_creating_systems.md |
|||
[SystemBase]: xref:Unity.Entities.SystemBase |
|
|||
--- |
|||
uid: ecs-entity-command-buffer |
|||
--- |
|||
# Entity Command Buffers |
|||
|
|||
The [`EntityCommandBuffer`](xref:Unity.Entities.EntityCommandBuffer) (ECB) class solves two important problems: |
|||
|
|||
1. When you're in a job, you can't access the [`EntityManager`](xref:Unity.Entities.EntityManager). |
|||
2. When you perform a [structural change](sync_points.md) (like creating an entity), you create a [sync point](sync_points.md) and must wait for all jobs to complete. |
|||
|
|||
The `EntityCommandBuffer` abstraction allows you to queue up changes (from either a job or from the main thread) so that they can take effect later on the main thread. |
|||
|
|||
## Entity command buffer systems |
|||
[Entity command buffer systems](xref:Unity.Entities.EntityCommandBufferSystem) allow you to play back the commands queued up in ECBs at a clearly defined point in a frame. These systems are usually the best way to use ECBs. You can acquire multiple ECBs from the same entity command buffer system and the system will play back all of them in the order they were created when it is updated. This creates a single sync point when the system is updated instead of one sync point per ECB and ensures determinism. |
|||
|
|||
The default World initialization provides three system groups, for initialization, simulation, and presentation, that are updated in order each frame. Within a group, there is an entity command buffer system that runs before any other system in the group and another that runs after all other systems in the group. Preferably, you should use one of the existing command buffer systems rather than creating your own in order to minimize synchronization points. See [Default System Groups](system_update_order.md) for a list of the default groups and command buffer systems. |
|||
|
|||
If you want to use an ECB from a parallel job (e.g. in an `Entities.ForEach`), you must ensure that you convert it to a concurrent ECB first by calling `ToConcurrent` on it. To ensure that the sequence of the commands in the ECB does not depend on how the work is distributed across jobs, you must also pass the index of the entity in the current query to each operation. |
|||
|
|||
You can acquire and use an ECB like this: |
|||
|
|||
[!code-cs[ecb_concurrent](../package/DocCodeSamples.Tests/EntityCommandBuffers.cs#ecb_concurrent)] |
|
|||
--- |
|||
uid: ecs-fluent-query |
|||
--- |
|||
|
|||
# Using ComponentSystem methods |
|||
|
|||
The ComponentSystem class is being phased out in favor of [SystemBase]. See [Creating systems] for more information on programming systems. |
|||
|
|||
|
|||
[Creating systems]: ecs_creating_systems.md |
|||
[SystemBase]: xref:Unity.Entities.SystemBase |
|
|||
# Using IJobForEach jobs |
|||
|
|||
The IJobForEach class is deprecated in favor of [SystemBase] and [Entities.ForEach]. See [Creating systems] for more information on programming systems. |
|||
|
|||
[Entities.ForEach]: xref:Unity.Entities.SystemBase.Entities |
|||
[Creating systems]: ecs_creating_systems.md |
|||
[SystemBase]: xref:Unity.Entities.SystemBase |
|
|||
--- |
|||
uid: ecs-entity-manager |
|||
--- |
|||
# EntityManager |
|||
|
|||
The `EntityManager` owns `EntityData`, [EntityArchetypes](xref:Unity.Entities.EntityArchetype), [SharedComponentData](xref:Unity.Entities.ISharedComponentData) and [EntityQuery](xref:Unity.Entities.EntityQuery). |
|||
|
|||
`EntityManager` is where you find APIs to create entities, check if an entity is still alive, instantiate entities and add or remove components. |
|||
|
|||
```cs |
|||
// Create an entity with no components on it |
|||
var entity = EntityManager.CreateEntity(); |
|||
|
|||
// Adding a component at runtime |
|||
EntityManager.AddComponent(entity, new MyComponentData()); |
|||
|
|||
// Get the ComponentData |
|||
MyComponentData myData = EntityManager.GetComponentData<MyComponentData>(entity); |
|||
|
|||
// Set the ComponentData |
|||
EntityManager.SetComponentData(entity, myData); |
|||
|
|||
// Removing a component at runtime |
|||
EntityManager.RemoveComponent<MyComponentData>(entity); |
|||
|
|||
// Does the entity exist and does it have the component? |
|||
bool has = EntityManager.HasComponent<MyComponentData>(entity); |
|||
|
|||
// Is the entity still alive? |
|||
bool has = EntityManager.Exists(entity); |
|||
|
|||
// Instantiate the entity |
|||
var instance = EntityManager.Instantiate(entity); |
|||
|
|||
// Destroy the created instance |
|||
EntityManager.DestroyEntity(instance); |
|||
``` |
|||
|
|||
```cs |
|||
// EntityManager also provides batch APIs |
|||
// to create and destroy many entities in one call. |
|||
// They are significantly faster |
|||
// and should be used where ever possible |
|||
// for performance reasons. |
|||
|
|||
// Instantiate 500 entities and write the resulting entity IDs to the instances array |
|||
var instances = new NativeArray<Entity>(500, Allocator.Temp); |
|||
EntityManager.Instantiate(entity, instances); |
|||
|
|||
// Destroy all 500 entities |
|||
EntityManager.DestroyEntity(instances); |
|||
``` |
|||
|
|
|||
--- |
|||
uid: ecs-exclusive-entity-transaction |
|||
--- |
|||
# ExclusiveEntityTransaction |
|||
|
|||
`ExclusiveEntityTransaction` is an API to create & destroy entities from a job. The purpose is to enable procedural generation scenarios where instantiation on big scale must happen on jobs. As the name implies it is exclusive to any other access to the [EntityManager](entity_manager.md). |
|||
|
|||
`ExclusiveEntityTransaction` should be used on a manually created [World](world.md) that acts as a staging area to construct & setup entities. |
|||
|
|||
After the job has completed you can end the `ExclusiveEntityTransaction` and use ```EntityManager.MoveEntitiesFrom(EntityManager srcEntities);``` to move the entities to an active `World`. |
|||
|
|
|||
apiRules: |
|||
- exclude: |
|||
# inherited Object methods |
|||
uidRegex: ^System\.Object\..*$ |
|||
type: Method |
|||
- exclude: |
|||
# mentioning types from System.* namespace |
|||
uidRegex: ^System\..*$ |
|||
type: Type |
|||
- exclude: |
|||
hasAttribute: |
|||
uid: System.ObsoleteAttribute |
|||
type: Member |
|||
- exclude: |
|||
hasAttribute: |
|||
uid: System.ObsoleteAttribute |
|||
type: Type |
|||
- include: |
|||
uidRegex: IJobForEach`1 |
|||
type: Interface |
|||
- include: |
|||
uidRegex: IJobForEachWithEntity`1 |
|||
type: Interface |
|||
- exclude: |
|||
uidRegex: IJobForEach |
|||
type: Interface |
|||
- exclude: |
|||
uidRegex: Unity\.Entities\.JobForEachExtensions\.IBaseJobForEach_ |
|||
type: Interface |
|||
- exclude: |
|||
uidRegex: Unity\.Entities.EntityQueryBuilder |
|||
type: Delegate |
|||
- exclude: |
|||
uidRegex: Unity\.Entities.EntityQueryBuilder\.ForEach``\d |
|||
- exclude: |
|||
uidRegex: ^Unity\.Entities\.CodeGeneratedJobForEach |
|||
type: Namespace |
|||
- exclude: |
|||
uidRegex: Tests$ |
|||
type: Namespace |
|||
- exclude: |
|||
uidRegex: ^Unity\..*\.Tests\..*$ |
|||
type: Namespace |
|||
- exclude: |
|||
uidRegex: ^Some\.Namespace |
|||
type: Namespace |
|
|||
--- |
|||
uid: gameplay-ai |
|||
--- |
|||
# Pathfinding and AI |
|||
|
|||
> Synopsis: Describe how to do pathfinding and other AI tasks. At the very least, this topic must cover using existing Unity constructs such as the Nav Mesh. Ideally, the topic also covers data-oriented approaches to AI systems or provides resources for developers tackling the problem. |
|||
|
|||
|
|||
Note, this is a placeholder for work that has not been completed yet. |
|||
|
|||
Also, see the [AI Planner](https://docs.unity3d.com/Packages/com.unity.ai.planner@latest/index.html) package. |
|
|||
--- |
|||
uid: gameplay-animation |
|||
--- |
|||
# Animation |
|||
|
|||
> Synopsis: How to use Unity's animation features with ECS. How to optimize animation with ECS. |
|||
|
|||
Note, this is a placeholder for work that has not been completed yet. |
|
|||
--- |
|||
uid: gameplay-assets |
|||
--- |
|||
# Assets and resources |
|||
|
|||
> Synopsis: Considerations for using Unity assets and loading resources in an ECS-based project. |
|||
|
|||
Note, this is a placeholder for work that has not been completed yet. |
|
|||
--- |
|||
uid: gameplay-audio |
|||
--- |
|||
# Audio |
|||
|
|||
> Synopsis: How to use Unity's audio features with ECS. How to optimize audio with ECS. |
|||
|
|||
|
|||
Note, this is a placeholder for work that has not been completed yet. |
|
|||
--- |
|||
uid: gameplay-interop |
|||
--- |
|||
# Sharing data between ECS and MonoBehaviours |
|||
|
|||
> Synopsis: interoperation between ECS components and systems and MonoBehaviours or other managed code. |
|||
|
|||
Note, this is a placeholder for work that has not been completed yet. |
|
|||
--- |
|||
uid: gameplay-input |
|||
--- |
|||
# Player input |
|||
|
|||
> Synopsis: How to use Unity's input features with ECS. How to optimize input with ECS. |
|||
|
|||
Note, this is a placeholder for work that has not been completed yet. |
|
|||
--- |
|||
uid: gameplay-multiplayer |
|||
--- |
|||
# Networking and Multiplayer |
|||
|
|||
> Synopsis: How to use Unity's networking and multiplayer features with ECS. How to optimize networking and multiplayer with ECS. |
|||
|
|||
Note, this is a placeholder for work that has not been completed yet. |
|
|||
--- |
|||
uid: gameplay-overview |
|||
--- |
|||
|
|||
# Creating gameplay |
|||
|
|||
This section contains information on how to create DOTS-based games and other applications in the Unity Editor. It also covers the systems and components that ECS provides to help you implement game features. |
|||
|
|||
The systems include: |
|||
|
|||
* [Unity.Transforms](xref:Unity.Transforms): Provides components to define world-space transforms, 3D object hierarchies, and systems to manage them. |
|||
* [Unity.Hybrid.Renderer](https://docs.unity3d.com/Packages/com.unity.rendering.hybrid@latest): Provides components and systems to render ECS entities in the Unity runtime. |
|||
|
|||
## Authoring overview |
|||
|
|||
You can use the Unity Editor (with the required DOTS packages) to create DOTS-based games. In the Editor, you use GameObjects as normal to author a Scene and the ECS code converts the GameObjects to entities. |
|||
|
|||
The biggest difference when you use DOTS is that instead of writing your own [MonoBehaviours](https://docs.unity3d.com/ScriptReference/MonoBehaviour.html) to store instance data and implement custom game logic, you define [ECS components](ecs_components) to store the data at runtime, and write [systems](ecs_systems) for the custom logic. |
|||
|
|||
### GameObject conversion |
|||
|
|||
During GameObject conversion, various conversion systems handle the MonoBehaviour components that they recognize and then convert them into ECS-based components. For example, one of the the Unity.Transforms conversion systems examines the `UnityEngine.Transform` component and adds ECS components, such as [LocalToWorld](xref:Unity.Transforms.LocalToWorld), to replace it. |
|||
|
|||
You can implement an [IConvertGameObjectToEntity](xref:Unity.Entities.IConvertGameObjectToEntity) MonoBehaviour component to specify custom conversion steps. There often isn't a one-to-one relationship between the number of GameObjects ECS converts and the number of entities it creates; nor between the number of MonoBehaviours on a GameObject and the number of ECS components it adds to an entity. |
|||
|
|||
![](images/CreatingGameplay.png) |
|||
|
|||
The ECS conversion code converts a GameObject if it either has a [ConvertToEntity](xref:Unity.Entities.Hybrid.ConvertToEntity) MonoBehaviour component, or if it is part of a SubScene. In either case, the conversion systems provided for various DOTS features, such as Unity.Transforms and Unity.Hybrid.Render, process the GameObject or the Scene Asset and any of their child GameObjects. |
|||
|
|||
One difference between converting GameObjects with ConvertToEntity and converting with a SubScene is that ECS serializes and saves to disk the entity data it generates from converting a SubScene. You can load or stream this serialized data very quickly at run time. In contrast, ECS always converts GameObjects with ConvertToEntity MonoBehaviours at runtime. |
|||
|
|||
It's best practice to use standard MonoBehaviours to author, and use `IConvertGameObjectToEntity` to apply the values of those authoring components to [IComponentData](xref:Unity.Entities.IComponentData) structs for runtime use. Often, the most convenient data layout to use to author is not the most efficient data layout at runtime. |
|||
|
|||
You can use [IConvertGameObjectToEntity](xref:Unity.Entities.IConvertGameObjectToEntity) to customize the conversion of any GameObject in a SubScene, or a GameObject that has a `ConvertToEntity` MonoBehaviour, or one that is a child of a GameObject that has a `ConvertToEntity` MonoBehaviour. |
|||
|
|||
**Note:** The authoring workflow for DOTS-based applications is an area of active development. The general outlines are in place, but you should anticipate many changes in this area in the near future. |
|||
|
|||
## Generated authoring components |
|||
|
|||
Unity can automatically generate authoring components for simple runtime ECS components. When Unity generates an authoring component, you can add an add the script containing the ECS component directly to a GameObject within the Editor. You can then use the **Inspector** window to set the initial values for the component. |
|||
|
|||
### For IComponentData |
|||
|
|||
Unity can automatically generate authoring components for simple [IComponentData](xref:Unity.Entities.IComponentData) components. When Unity generates an authoring component, you can add an `IComponentData` directly to a GameObject in a Scene within the Unity Editor. You can then use the **Inspector** window to set the initial values for the component. |
|||
|
|||
To indicate that you want to generate an authoring component, add the `[GenerateAuthoringComponent]` attribute to the IComponentData declaration. Unity automatically generates a MonoBehaviour class that contains the public fields of the component and provides a Conversion method that converts those fields over into runtime component data. |
|||
|
|||
```c# |
|||
[GenerateAuthoringComponent] |
|||
public struct RotationSpeed_ForEach : IComponentData |
|||
{ |
|||
public float RadiansPerSecond; |
|||
} |
|||
``` |
|||
|
|||
Note the following restrictions: |
|||
|
|||
- Only one component in a single C# file can have a generated authoring component, and the C# file must not have another MonoBehaviour in it. |
|||
- ECS only reflects public fields and they have the same name as that specified in the component. |
|||
- ECS reflects fields of an Entity type in the IComponentData as fields of GameObject types in the MonoBehaviour it generates. ECS converts the GameObjects or Prefabs you assign to these fields as referenced Prefabs. |
|||
- Only public fields are reflected and they will have the same name as that specified in the component. |
|||
- Fields of an Entity type in the IComponentData are reflected as fields of GameObject types in the generated MonoBehaviour. GameObjects or Prefabs you assign to these fields are converted as referenced prefabs. |
|||
|
|||
### For IBufferElementData |
|||
|
|||
You can also generate authoring components for types that implement `IBufferElementData` by adding the `[GenerateAuthoringComponent]` attribute: |
|||
``` |
|||
[GenerateAuthoringComponent] |
|||
public struct IntBufferElement: IBufferElementData |
|||
{ |
|||
public int Value; |
|||
} |
|||
``` |
|||
|
|||
In this example, a class named `IntBufferElementAuthoring` (which inherits from `MonoBehaviour`) is generated, exposing a public field of `List<int>` type. During conversion, this list will be converted into `DynamicBuffer<IntBufferElement>`, and then added to the converted entity. |
|||
|
|||
Note the following restrictions: |
|||
|
|||
- Only one component in a single C# file can have a generated authoring component, and the C# file must not have another MonoBehaviour in it. |
|||
- `IBufferElementData` authoring components cannot be automatically generated for types that contain 2 or more fields. |
|||
- `IBufferElementData` authoring components cannot be automatically generated for types that have an explicit layout. |
|
|||
--- |
|||
uid: gameplay-physics |
|||
--- |
|||
# Physics |
|||
|
|||
> Synopsis: How to use Unity's physics features with ECS. How to optimize physics with ECS. |
|||
|
|||
Note, this is a placeholder for work that has not been completed yet. |
|||
|
|||
See the [Unity Physics](https://docs.unity3d.com/Packages/com.unity.physics@latest/index.html) and [Havok Physics](https://docs.unity3d.com/Packages/com.havok.physics@latest/index.html) packages for information about the DOTS-compatible physics APIs. |
|
|||
--- |
|||
uid: gameplay-prefabs |
|||
--- |
|||
# Prefabs |
|||
|
|||
> Synopsis: Using prefabs in ECS-based programs |
|||
|
|||
Note, this is a placeholder for work that has not been completed yet. |
|
|||
--- |
|||
uid: gameplay-rendering |
|||
--- |
|||
# Rendering |
|||
|
|||
|
|||
The Hybrid.Rendering package provides a ECS system to render 3D objects. |
|||
|
|||
See [DOTS Hybrid Renderer](https://docs.unity3d.com/Packages/com.unity.rendering.hybrid@latest/index.html) for information about the current DOTS-compatible rendering API. |
|
|||
--- |
|||
uid: gameplay-scenes |
|||
--- |
|||
# Scenes |
|||
|
|||
> Synopsis: How to organize an ECS-based project. Creating, loading, unloading scenes, etc |
|||
|
|||
Note, this is a placeholder for work that has not been completed yet. |
|
|||
--- |
|||
uid: gameplay-text |
|||
--- |
|||
# Handling text |
|||
|
|||
> Synopsis: Handling text in a performant way |
|||
|
|||
Note, this is a placeholder for work that has not been completed yet. |
|
|||
--- |
|||
uid: gameplay-ui |
|||
--- |
|||
# User Interfaces |
|||
|
|||
> Synopsis: How to use Unity's UI features with ECS. How to optimize your UI with ECS. |
|||
|
|||
Note, this is a placeholder for work that has not been completed yet. |
|
|||
var slideNumber = 1; |
|||
advanceSlide(0); |
|||
document.getElementById('slides').addEventListener('click', changeSlide); |
|||
|
|||
function changeSlide(evt){ |
|||
var rect = evt.target.getBoundingClientRect(); |
|||
|
|||
if(evt.clientX > rect.left + (rect.right - rect.left)/2){ |
|||
advanceSlide(1); |
|||
} else { |
|||
advanceSlide(-1); |
|||
} |
|||
} |
|||
|
|||
function advanceSlide(n) { |
|||
slideNumber += n; |
|||
var slides = document.getElementsByClassName("infographic"); |
|||
if (slideNumber > slides.length) |
|||
{ |
|||
slideNumber = 1; |
|||
} |
|||
else if (slideNumber < 1) |
|||
{ |
|||
slideNumber = slides.length; |
|||
} |
|||
|
|||
for (var i = 0; i < slides.length; i++) |
|||
{ |
|||
slides[i].style.display = "none"; |
|||
} |
|||
slides[slideNumber - 1].style.display = "block"; |
|||
} |
部分文件因为文件数量过多而无法显示
撰写
预览
正在加载...
取消
保存
Reference in new issue