Spawn and Move Players
Full workflows and code to spawn and move Player from user input
Last updated
Full workflows and code to spawn and move Player from user input
Last updated
We will create a player prefab and have it spawn when the user inputs commands. The player will move according to user input.
Github branch link: https://github.com/moetsi/Unity-DOTS-Multiplayer-XR-Sample/tree/Spawning-and-Moving-Player
We use many of the same steps as spawning an asteroid prefab to spawn a player prefab. First we make our player prefab.
Create a capsule GameObject in the Hierarchy of SubScene, name it "Player", and drag it into Scripts and Prefabs then delete the cylinder GameObject in the Hierarchy
Remove the Capsule Collider component
We will add DOTS Physics colliders in the next section
Add a cube to the Player hierarchy named "Visor"
(Double click on the Player prefab to get to the Player hierarchy)
Remove the Box Collider component
Change the position to (0, 0.5, 0.24)
Change the scale to (0.95, 0.25, 0.5)
Right click on the Assets, click "Create" > "Material"
Make the name "Black"
Make sure it is a Universal Render Pipeline/Lit shader
Double click Base Map and change the value to "000000"
Click on the Visor in the Player Hierarchy and drag "Black" to Mesh Renderer Material
Add a cylinder to the Player hierarchy named "Gun"
Remove the Capsule Collider component
Change the position to (0.5, 0, 0.5)
Change the rotation to (90, 0 , 0)
Change the scale to (0.25, 0.5, 0.25)
Change the material to "Black"
Finally let's add a camera
Right click in the Player prefab Hierarchy and create a Camera GameObject
Change the position to (0, 2, -5)
And make sure your Player prefab has a Position of (0, 0, 0)
Now we need to create a PlayerTag in our Scripts and Prefabs folder to put on our Player prefab
We use this tag to query for our player entity
Because we are putting this component data on a prefab before runtime we must make it an Authoring component with [GenerateAuthoringComponent]
Create PlayerTag and paste this code snippet into PlayerTag.cs:
Now we will put the PlayerTag component and the VelocityComponent on our Player prefab
Finally, we must add our Player prefab to the PrefabCollection GameObject in our ConvertedSubScene like we did with our Asteroid prefab
The first step here is to create the PlayerAuthoringComponent
code snippet for PlayerAuthoringComponent.cs below:
Next we open our ConvertedSubScene, select PrefabCollection, click Add Component to add PlayerAuthoringComponent to the PrefabCollection GameObject and drag the Player prefab into the the Prefab field
Unity might ask you to save your Player prefab when navigating to the Sub Scene if you have not already, hit save
Save the Sub Scene, open SampleScene and reimport the ConvertedSubScene
We now have our Player prefab created and registered with our PrefabCollection so we can reference it in ECS.
We are going to create InputSpawnSystem in Scripts and Prefabs that will take in user input and spawn a player prefab if the user presses the spacebar
AsteroidSpawnSystem spawns asteroids programmatically based on the GameSettingsComponents values
InputSpawnSystem will spawn entities based on user input
InputSpawnSystem will have a lot of similarities to AsteroidSpawnSystem because both systems need to set prefabs and use EntityCommandBuffer to record structural changes
InputSpawnSystem does not need GameSettingsComponent data so it will have a few less lines in OnCreate()
Create InputSpawnSystem and paste the code snippet below into InputSpawnSystem.cs:
You will notice the similarities of InputSpawnSystem with AsteroidSpawnSystem, but also a few differences:
why aren't we using the EntityCommandBuffer in InputSpawnSystem?!
And what the heck is an EntityManager?!
We are not using the EntityCommandBuffer to demonstrate how to make structural changes during InputSpawnSystem's OnUpdate()
That's not to torture you, it's to teach you more ECS functionalities 💪
We will continue using the EntityCommandBuffer later on when we spawn bullets 😌
A World organizes entities into isolated groups. A world owns both an EntityManager and a set of Systems. Entities created in one world only have meaning in that world, but can be transfered to other worlds (with EntityManager.MoveEntitiesFrom). Systems can only access entities in the same world. You can create as many worlds as you like.
By default Unity creates a default World when your application starts up (or you enter Play Mode). Unity instantiates all systems (classes that extend ComponentSystemBase) and adds them to this default world. Unity also creates specialized worlds in the Editor. For example, it creates an Editor world for entities and systems that run only in the Editor, not in playmode and also creates conversion worlds for managing the conversion of GameObjects to entities. See WorldFlags for examples of different types of worlds that can be created.
Use World.DefaultGameObjectInjectionWorld to access the default world.
You might have noticed that when we hit the "play" button the list of available worlds in our DOTS Windows changes
"Editor World" changes to "Default World"
You can also disable the default World creation entirely by defining the following global symbols:
#UNITY_DISABLE_AUTOMATIC_SYSTEM_BOOTSTRAP_RUNTIME_WORLD
disables generation of the default runtime World.
#UNITY_DISABLE_AUTOMATIC_SYSTEM_BOOTSTRAP_EDITOR_WORLD
disables generation of the default Editor World.
#UNITY_DISABLE_AUTOMATIC_SYSTEM_BOOTSTRAP
disables generation of both default Worlds.
A World owns a single EntityManager and a set of Systems
The EntityManager is what we use to interact with Entities if we are not using EntityCommandBuffer
The EntityManager provides an API to create, read, update, and destroy entities.
A World has one EntityManager, which manages all the entities for that World.
Many EntityManager operations result in structural changes that change the layout of entities in memory. Before it can perform such operations, the EntityManager must wait for all running Jobs to complete, an event called a sync point. A sync point both blocks the main thread and prevents the application from taking advantage of all available cores as the running Jobs wind down.
Although you cannot prevent sync points entirely, you should avoid them as much as possible. To this end, the ECS framework provides the EntityCommandBuffer, which allows you to queue structural changes so that they all occur at one time in the frame.
Now that we've created our InputSpawnSystem let's hit "play" then "space bar" to spawn our Player prefab
Woo, all set! Right?
Not quite, why isn't the camera updated to the Player prefab's Camera GameObject?
The Camera is a GameObject but is being instantiated through ECS... a little wonky
To fix this we need to add HYBRID_ENTITIES_CAMERA_CONVERSION
to the Player Settings
Navigate to "File", choose "Build settings", then "Player Settings", then "Player" on the left, expand the drop down menu for "Other Settings", and scroll down to "Scripting Define Symbols". Add HYBRID_ENTITIES_CAMERA_CONVERSION
Hit "Apply"
This will cause a warning on the Build Settings window
Wait until the warning on the Build Settings window goes away
Now hit "play" and press "space bar" again to see our new camera view
Through testing we have found that sometimes this change does not immediately "take"
In that case, go to "Assets" and choose "Reimport All"
We can now spawn our player from user input
We created our player prefab
We added a PlayerTag and VelocityComponent to the prefab
We created InputSystem which takes in inputs and spawns our player
We are going to create InputMovementSystem in Scripts and Prefabs to take in "WASD" and mouse input to change the rotation and velocity of our player
We could implement this functionality into the existing InputSpawnSystem (and name the system something else) but we are separating to make the code easier to understand
Create InputMovementSystem and paste the code snippet below into InputMovementSystem.cs:
Hit "play" and once the game is loaded press "space bar" to spawn your player, hold down the right button and move your mouse to change rotation and hit "w" to add forward thrust
We didn't need to make a second MovementSystem for our player entity because the MovementSystem works on any entity that has a Translation and VelocityComponent
Nice benefit of ECS
It is a bit fast, let's change the Player Force to 10
Go into ConvertedSubScene and change Player Force from 50 to 10
Save and return to SampleScene and reimport ConvertedSubScene and hit play and try again
We can now move our player from user input
We updated our InputSystem to read more user inputs
We can adjust the rotation and velocity of our player entity with user inputs
Github branch link: https://github.com/moetsi/Unity-DOTS-Multiplayer-XR-Sample/tree/Spawning-and-Moving-Player
git clone https://github.com/moetsi/Unity-DOTS-Multiplayer-XR-Sample/
git checkout 'Spawning-and-Moving-Player'