Spawn and Move Prefabs
Full workflows and code to programmatically spawn, update, and destroy prefab entities
Last updated
Full workflows and code to programmatically spawn, update, and destroy prefab entities
Last updated
Use game settings to programmatically spawn asteroid prefabs to create an asteroid field (cube).
Github branch link: https://github.com/moetsi/Unity-DOTS-Multiplayer-XR-Sample/tree/Spawning-Updating-and-Destroying-Asteroids
An entity prefab is nothing more than an entity with a
Prefab
tag and aLinkedEntityGroup
. The former identifies the prefab and makes it invisible to all entity queries but the ones who explicitly include prefabs, and the latter links together a set of entities, since entity prefabs can be complex assemblies (equivalent to GameObject hierarchies).So the following two components are equivalent, one in classic Unity and the other in DOTS.
By default, the conversion workflow only processes the actual contents of an authoring scene, so a specific mechanism is required to also include prefabs from the asset folder. This is the purpose of the system group
GameObjectDeclareReferencedObjectsGroup
, it runs before the primary entities are created in the destination world, and provides a way of registering prefabs for conversion.From ECS Conversion Workflow documentation "Prefabs" section
Right click in the Hierarchy, choose "3D Object" > "Sphere"
Drag the GameObject into the Scripts and Prefabs folder then delete the GameObject from the hierarchy (right click Delete is better then hitting the delete key)
Click on the sphere in the Project window, then click "Open Prefab" in the Inspector
Rename it to "Asteroid"
Adjust the scale to (2,2,2)
Remove the "Sphere Collider" component (click three vertical dots menu to find Remove)
This is a MonoBehaviour collider, and in the next section, DOTS Physics, we will replace this collider with DOTS collider
In Scripts and Prefabs Create "AsteroidTag" runtime component (Create > ECS > Runtime Component Type)
This will be IComponentData but will not contain any data within it
Although this isn't necessary, it tends to help you remember how components are meant to be used
AsteroidTag.cs
Try to put the AsteroidTag on the asteroid prefab
Trying to put the AsteroidTag on the asteroid prefab causes an error: "Can't add script behavior to AsteroidTag. The script needs to derive from MonoBehaviour."
Because the Editor is not ECS, you cannot add IComponentData to a prefab before runtime
Even though it "seems" like you should, it is important to remember that the Asteroid is currently a GameObject and so putting a runtime ECS component on it actually doesn't make sense, what we need to do is make AsteroidTag an "authoring component"
For simple runtime components, the
GenerateAuthoringComponent
attribute can be used to request the automatic creation of an authoring component for a runtime component. You can then add the script containing the runtime component directly to a GameObject within the Editor.From ECS Conversion Workflow documentation "Generated authoring components" section
Add a [GenerateAuthoringComponent] decorator to the AsteroidTag
Now try to put the AsteroidTag on the Asteroid prefab again (select Asteroid in Hierarchy > Click "Add Component" in the Inspector)
Now we have an Asteroid entity with an AsteroidTag authoring component. This "tag" enables us to "find" asteroid entities, by checking whether or not entities have an AsteroidTag component
How do we "refer" to the "asteroid" prefab when we want to create asteroids? We need a way to "reference" this prefab when writing our ECS.
Create an empty GameObject in ConvertedSubScene named PrefabCollection
This GameObject will hold all the references to our prefabs
When the SubScene converts this GameObject into an entity, we will refer to the PrefabCollection entity's components to reference our prefabs
Create AsteroidAuthoringComponent (right click Scripts and Prefabs > Create > ECS > Authoring Component Type)
Go back and select the PrefabCollection GameObject in Hierarchy and add AsteroidAuthoringComponent (click Add Component in Inspector)
Drag the asteroid prefab from the Project window into the public Prefab field under the script component in Inspector
Save the SubScene then click "Reimport" in Inspector to make sure assets are reimported into SubScene
We now have a reference to our asteroid prefab
We will be using the following workflow to reference prefabs for the remainder of this gitbook:
Create a "PrefabAuthoringComponent" with an Entity field
Add it to our PrefabCollection GameObject in our SubScene
Drag our prefab from the project window into the Entity field of the authoring component
We will be able to reference the authoring component to instantiate our prefabs
Navigate to "Scripts and Prefabs", right click and select Create > ECS > System and name the system "AsteroidSpawnSystem"
"System" (the S in ECS)
The latest update creates a "bad" system (Unity has not updated the auto-generated systems yet) that is missing the "partial" keyword
In AsteroidSpawnSystem change public class AsteroidSpawnSystem : SystemBase
to public partial class AsteroidSpawSystem : SystemBase
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.
Hit the "play" button and check out the Systems window in our DOTS Windows
Our AsteroidSpawnSystem is automatically placed into the Simulation System Group
We are able to change which SystemGroup our system runs in by adding decorators to our system
Click on the AsteroidSpawnSystem and you will see that our system interacts with 2 components, Rotation and Translation
These are default components in the boilerplate system that are used by the EntityQuery (the EntityQuery "checks" for these components)
AsteroidSpawnSystem.cs 's boilerplate code is below
We can see from the boilerplate code that there is a reference to "Translation" and "Rotation" which was picked up in the Debugger
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 synchronization 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.
Let's briefly take a look at the system types referenced in the documentation.
SystemBase - this is the type of system we will be using most of the time for our runtime game play. The systems of this type will do things like provide movement to asteroids or Spawn/Destroy.
ComponentSystemGroup - this is groupings of systems.
GameObjectConversionSystem - We interacted with systems of this type when working with our GameSettingsComponent and our SetGameSettingsSystem. These are used for hybrid development.
EntityCommandBufferSystem - So far we have not used this type of system. The systems in EntityCommandBufferSystem are used as "sync" points for structural changes made to entities. You can see these systems in the "Default System Groups" image above. There is a Begin{SystemGroup}EntityCommandBufferSystem and End{SystemGroup}EntityCommandBufferSystem for each of the 3 main default system groups (InitializationSystemGroup, SimulationSystemGroup, and PresentationSystemGroup). If confused about "structural" changes it is good to refer to Unity's overview of ECS concepts.
An Entity is a collection of components. Let's say there is an asteroid with a Translation component which is a float3 and denotes the asteroid's position in space. Updating the Entity's Translation component values is not a structural change. The Entity still has the same amount and types of components, just different values.
A unique combination of component types is called an EntityArchetype. 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:
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.From Core ECS Overview
Let's say we add a "DestroyTag" to the asteroid Entity. That will actually "change" the EntityArchetype it is (because it has a different collection of components). Structural changes are computationally more expensive than just updating values.
Sync points are caused by operations that you cannot safely perform when there are any other jobs that operate on components. Structural changes to the data in ECS are the primary cause of sync points. All of the following are structural changes:
Creating entities
Deleting entities
Adding components to an entity
Removing components from an entity
Changing the value of shared components
Broadly speaking, any operation that changes the archetype of an entity or causes the order of entities within a chunk to change is a structural change. These structural changes can only be performed on the main thread.
Structural changes not only require a sync point, but they also invalidate all direct references to any component data. This includes instances of DynamicBuffer and the result of methods that provide direct access to the components such as ComponentSystemBase.GetComponentDataFromEntity.
You can use entity command buffers (ECBs) to queue up structural changes instead of immediately performing them. Commands stored in an ECB can be played back at a later point during the frame. This reduces multiple sync points spread across the frame to a single sync point when the ECB is played back.
Each of the standard ComponentSystemGroup instances provides a EntityCommandBufferSystem as the first and last systems updated in the group. By getting an ECB object from one of these standard ECB systems, all structural changes within the group occur at the same point in the frame, resulting in one sync point rather than several. ECBs also allow you to record structural changes within a job. Without an ECB, you can only make structural changes on the main thread. (Even on the main thread, it is typically faster to record commands in an ECB and then play back those commands, than it is to make the structural changes one-by-one using the EntityManager class itself.)
If you cannot use an EntityCommandBufferSystem for a task, try to group any systems that make structural changes together in the system execution order. Two systems that both make structural changes only incur one sync point if they update sequentially.
From ECS Sync points
If sync points and EntityCommandBuffers are making your head hurt, you should really watch Unity's talk on Entity Command Buffers: https://www.youtube.com/watch?v=SecJibpoTYw <-- Worth the watch!
We will be using the BeginSimulationEntityCommandBuffer in our AsteroidSpawnSystem. This command buffer plays back our structural changes (creating asteroid entities).
We will use a Job.WithCode() to generate our asteroids.
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.
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 andWithDisposeOnCompletion
to automatically dispose a container after the job finishes. (Entities.ForEach provides the same functions.)See Job.WithCode for more information about these modifiers and attributes.
Think of Job.WithCode as a super-hardcore ECS "for loop" in an Update() function.
Make AsteroidSpawnSystem and paste this code snippet into AsteroidSpawnSystem.cs:
Read through the comments to get a line-by-line understanding of what is going on
Important Note: A quirk of ECS is that you must declare local variables in the OnUpdate method if you want to use that data in a Job that runs in OnUpdate().
This means that if you want to use certain variables in your OnUpdate(), and you already declared them in your OnCreate(), you must still also declare variables locally in OnUpdate().
Is there any point to declaring variables in OnCreate()? Yes, there is! If you grab the variable from OnCreate(), you only reach "out" once during runtime. But, if you are calling for the value from OnUpdate(), that means you reach "out" for the local variable on each OnUpdate().
You'll notice the general set-up of an ECS system is broken down into two major parts:
OnCreate
EntityQueries, CommandBuffers, RequireForUpdates
OnUpdate
Setup your local variables
Run your Job.WithCode() or Entities.ForEach()
OnCreate
Here we set up EntityQueries on the entities and components we need
We will need to know how many Asteroids exist so we can spawn the right amount
We need data from our GameSettingsComponent, so we create a query and see if it exists before we continue to our OnUpdate
Here we also set up our EntityCommandBuffer which will be used to record structural changes
OnUpdate
Here we set our prefab by grabbing the data from our AsteroidAuthoringComponent
We declare the local variables we will need for our Job.WithCode()
We create a Translation that is on the perimeter of a cube defined by our levelHeight, levelWidth, and levelDepth in our GameSettingsComponent
We instantiate a new entity from our prefab and set its Translation component to our translation
Important note: The reason why we are able to easily pass our GameSettingsComponent data into the Job.WithCode() is because the entity with that data is a Singleton. A Singleton is an entity that is the only entity that exists with this particular component.
What if we wanted to pass in PlayerComponent data to the Job.WithCode() and there could be multiple players active during the OnUpdate()?
In this case you would use:
m_PlayersQuery = GetEntityQuery(ComponentType.ReadWrite<PlayerComponent>());
JobHandle playersDep;
var players = m_PlayersQuery.ToComponentDataArrayAsync(Allocator.TempJob, out playersDep);
This creates a NativeArray of Player component data
This array can now be referenced within a Job
Now there is an additional dependency to create that NativeArray which also must be disposed, this is where it gets tricky
You will need to combine dependencies to ensure there are no race conditions
Using NativeArrays will be discussed in future sections. So why are we confusing you by bringing up this additional information that isn't needed right now? That's because introducing future workflows to you now might help you build a better mental model of how to navigate ECS.
You might have noticed our AsteroidSpawnSystem ends with a .Schedule() which means we are running a a single job. A great benefit of Unity DOTS is the ability to schedule parallel jobs to make the most of the targeting compute platform. If we wanted to make AsteroidSpawnSystem run parallel jobs we would need to use an IJobFor.
Note: To run a parallel job, implement IJobFor, which you can schedule using ScheduleParallel() in the system OnUpdate() function.
We do not use an IJobFor here because it is a bit more involved than a Job.WithCode(), and we are just starting to warm up to ECS! If you are feeling adventurous, you can implement AsteroidJobSystem with an IJobFor and checkout the performance difference between single jobs and parallel jobs for spawning asteroids. If you have interesting results, please reach out to the Moetsi team either on our Discord or emailing us at olenka@moetsi.com and we will include it in this gitbook!
Hit the "play" button and see the spawned asteroids
Not working? Reimport the ConvertedSubScene if there are issues grabbing the AsteroidAuthoringComponent Singleton
Checkout the DOTS Hierarchy to see all the newly created entities
But where are all our asteroids?
We can see from the DOTS Hierarchy that the Entities are there but the Translation values of the asteroids are verrrrrry far apart, so we cannot see them and so we need to update our GameSettings
Let's go to our ConvertedSubScene in the Hierarchy, select Game Settings, and change our levelWidth, levelHeight, and levelDepth to 20 and change the number of asteroids to 20,000 in the Inspector
But we still can't see the asteroids even now when they are closer together, WHAT GIVES?!
The issue is that we are still using the "built in" render pipeline, rather than the Universal Render Pipeline (URP). Unity does not automatically change over. Following Unity's docs let's create a Universal Render Pipeline Asset and then Add the Asset to your Graphics Settings
The Universal Render Pipeline Asset controls the global rendering and quality settings of your Project, and creates the rendering pipeline instance. The rendering pipeline instance contains intermediate resources and the render pipeline implementation.
To create a Universal Render Pipeline Asset:
In the Editor, go to the Project window.
Right-click in the Project window in Assets/, and select Create > Rendering > Universal Render Pipeline > Pipeline Asset. Alternatively, navigate to the menu bar at the top, and select Assets > Create > Rendering > Universal Render Pipeline > Pipeline Asset (Forward Renderer).
You can either leave the default name for the new Universal Render Pipeline Asset, or type a new one.
Adding the Asset to your Graphics settings
To use URP, you need to add the newly created Universal Render Pipeline Asset to your Graphics settings in Unity. If you don't, Unity still tries to use the Built-in render pipeline.
To add the Universal Render Pipeline Asset to your Graphics settings:
Navigate to Edit > Project Settings... > Graphics.
In the Scriptable Render Pipeline Settings field, add the Universal Render Pipeline Asset you created earlier. When you add the Universal Render Pipeline Asset, the available Graphics settings immediately change. Your Project is now using URP.
Now let's hit play and see those Asteroids!
Why can't we see them?! It is because we are still using a Material from the built in render pipeline. Let's make a material that is compatible with Universal Render Pipeline.
Right click in Assets/ and Prefabs and choose Material and call it GreyMaterial
Choose the base map to be a Grey Color
Now let's add it to our AsteroidPrefab
Now let's hit play
Woah. That is way too many 😬
Let's change the number of Asteroids to 200
This looks more like an asteroid field ☑️
We now have spawned an Asteroid prefab (entity prefab) using ECS
we created an Asteroid prefab
we created a AsteroidTag authoring component
We placed the AsteroidTag on the prefab
We created AsteroidSpawnSystem which spawns Asteroids in a cube of a given height, width and depth
Many people come to ECS looking to increase the performance of their games and tools. While what you've built so far is a toy example, take a second to play around with the asteroid counts.
Push the asteroid counts as high as your machine can handle. Get comfortable with exploring which Systems carry the most load.
You've taken your first steps in high concurrency ECS systems! Congratulations!
Create a Velocity component (right click Scripts and Prefabs > Create > ECS > Authoring Component Type, you get the hang of this now, right?)
We will add this to our asteroid prefab and update the AsteroidSpawnSystem to set the Velocity component on our asteroid entity when instantiating
Just a heads up: we're going to shake things up a bit in the next section, where we use Unity Physics for velocity instead
paste the code snippet below into VelocityComponent.cs:
Then add the VelocityComponent to the asteroid prefab
We can add the VelocityComponent to the prefab because it has the [GenerateAuthoringComponent] decorator
We must now update our AsteroidSpawnSystem to initialize the asteroids VelocityComponent
Add the below code snippet to the end of the Job.WithCode() code block (just before the closing curly brace) in AsteroidSpawnSystem.cs:
Reimport ConvertedSubScene, hit "play", then go checkout the Entity list of asteroids in the DOTS Hierarchy, now with their new VelocityComponents
Now we need to make a system to update Translation values based on VelocityComponent data
Create MovementSystem in Scripts and Prefabs (Create > ECS > System)
Create MovementSystem and paste this code snippet into MovementSystem.cs:
Notice that this system is much simpler than AsteroidSpawningSystem
This is because there are no structural changes to be made and we do not need to use any data beyond the data that is already on the entities we want to update
All we want to do in MovementSystem is read data from one component (Velocity), and use it to adjust the data in another component (Translation)
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()
andScheduleParallel()
, or execute it immediately (on the main thread) withRun()
. You can use additional methods defined on Entities.ForEach to set the entity query as well as various job options....
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:
By default, you can pass up to eight parameters to an Entities.ForEach lambda function. (If you need to pass more parameters, you can define a custom delegate.) When using the standard delegates, you must group the parameters in the following order:
Parameters passed-by-value first (no parameter modifiers)
Writable parameters second (
ref
parameter modifier)Read-only parameters last (
in
parameter modifier)All components should use either the
ref
or thein
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.
Our MovementSystem queries all entities that have both a Translation and a Velocity component
We put "ref Translation position" because we will be writing to the Translation component
We put "in VelocityComponent velocity" because we are only reading from the Velocity component
Wait, where is this "Burst" we have been hearing so much about?
Job.WithCode() and Entities.ForEach() are automatically Burst compiled (woo!)
Sometimes you must specify .WithoutBurst() for certain workflows (we will see this later)
Burst documentation if you want to read up.
You can execute the lambda function on the main thread using
Run()
, as a single job usingSchedule()
, or as a parallel job usingScheduleParallel()
. These different execution methods have different constraints on how you access data. In addition, Burst uses a restricted subset of the C# language, so you need to specifyWithoutBurst()
when using C# features outside this subset (including accessing managed types).
If possible use .ScheduleParallel() to make the most of Unity ECS
That is the whole point of doing things in this new data-oriented way, performance improvements! 🚀
If you're curious, try and create the same AsteroidSpawnSystem using MonoBehaviours to see the performance difference
Sometimes you will not be able to use Burst, Schedule(), or ScheduleParallel() based on your workflow (limitations of the technology)
The following table shows which features are currently supported in Entities.ForEach for the different methods of scheduling available in SystemBase:
Note:
WithStructuralChanges()
will disable Burst. Do not use this option if you want to achieve high levels of performance in your Entities.ForEach (instead use an EntityCommandBuffer).An Entities.ForEach construction uses Roslyn source generators to translate the code you write for the construction into correct ECS code. This translation allows you to express the intent of your algorithm without having to include complex, boilerplate code. However, it can mean that some common ways of writing code are not allowed.
The following features are not currently supported:
Hit "play" and see the asteroids move in their random velocities
Go to the GameSettings in ConvertedSubScene and adjust the Asteroid Velocity to 2 to slow down the asteroids and make them more Space-like
Reimport the Sub Scene, hit "play", and checkout the Entity Debugger to see how the asteroids' Translation values are changed in real-time
We now have asteroids prefabs moving in random directions
We created a VelocityComponent and added it to the asteroid prefab
We added to the AsteroidSpawnSystem to set random velocities on the asteroids
We created MovementSystem to take any entity with a Translation and VelocityComponent and adjust the Translation value using the VelocityComponent
We are going to destroy any asteroid prefabs that leave the perimeter of our cube
We will create an AsteroidsOutOfBoundsSystem that adds a DestroyTag to any asteroid that leaves the cube perimeter
Create DestroyTag and paste this code snippet into DestroyTag.cs:
Notice we did not add [GenerateAuthoringComponent] to this tag
This is because this is data we add at runtime not at authoring time
Now let's create the AsteroidsOutOfBoundsSystem that will add the DestroyTag to any asteroid that leaves the cube's perimeter
Create AsteroidsOutOfBoundsSystem and paste this code snippet into AsteroidsOutOfBoundsSystem.cs:
Notice we are placing this system in a specific SystemGroup, the FixedStepSimulationGroup
We want to make sure it updates before the EndFixedStepSimulationEntityCommandBufferSystem because the latter is where recorded structural changes will playback
We want to start getting comfortable with placing systems in different groups and utilizing different EntityCommandBufferSystems because not only is it important in ECS in general, but also because we will be using Unity Physics in the next section (which is run in the FixedStepSimulationGroup)
We use .WithAll<AsteroidTag>() to query all entities with an AsteroidTag
We also needed to include "int entityInQueryIndex" in our .ForEach() because when running parallel jobs, we must include the entityInQueryIndexwhen making our changes. It is needed for parallel jobs to work!
commandBuffer.AddComponent(entityInQueryIndex, entity, new DestroyTag());
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 thesortKey
for adding commands to a concurrent EntityCommandBuffer.
Now we need to create a system that destroys any asteroids with a DestroyTag
Create AsteroidsDestructionSystem and paste this code snippet into AsteroidsDestructionSystem.cs:
We run this system in the LateSimulationSystemGroup
This way all systems are able to interact with the entity before we delete it
We will use the LateSimulationSystemGroup's CommandBufferSystem to playback our recorded structural changes (destroying the asteroid entity)
Hit "play" and see how the asteroids are destroyed when they pass the perimeter and how the AsteroidSpawnSystem creates new asteroids
Check out the DOTS Hierarchy and see the AsteroidPrefabs get created and destroyed
You just made a giant cloud of asteroids! Congratulations, you've taken your first steps in high concurrency ECS systems!
Many come to ECS looking to increase the performance of their games and tools. While what you've built so far is a toy example, take a second to play around with the asteroid counts. Push the asteroid counts as high as your machine can handle. Get comfortable with exploring which Systems carry the most load.
We now have an asteroid field
We created a DestroyTag which is added by the AsteroidsOutOfBoundsSystem when an asteroid leaves the cube perimeter
We created AsteroidsDestructionSystem that destroys asteroids with a destroy tag
Github branch link: https://github.com/moetsi/Unity-DOTS-Multiplayer-XR-Sample/tree/Spawning-Updating-and-Destroying-Asteroids
git clone https://github.com/moetsi/Unity-DOTS-Multiplayer-XR-Sample/
git checkout 'Spawning-Updating-and-Destroying-Asteroids'
Supported Feature | Run | Schedule | ScheduleParallel |
---|---|---|---|
Unsupported Feature |
---|