Entities in action: Tanks

Tutorial

·

intermediate

·

+10XP

·

30 mins

·

(121)

Unity Technologies

Entities in action: Tanks

In this tutorial, you'll put your new knowledge of jobs and entities into action in a sample project that will demonstrate how to implement common features of games. Your objective is to spawn moving tanks that shoot cannonballs from a spinning turret. Tanks are destroyed when hit by a cannonball. The player controls the movement of one tank.

By the end of this tutorial, you'll be able to do the following:

  • Make an entity move randomly using code.
  • Spawn entities from a prefab using code.
  • Control the behavior of spawned entities using code.
  • Add a user input system to an entities project.

Languages available:

1. Overview

This tutorial demonstrates the essentials of the Entities package and is intended as a follow-up to the HelloCube and job system tutorials.


Your objective in this tutorial is to spawn moving tanks that shoot cannonballs from a spinning turret. Tanks are destroyed when hit by a cannonball. The player controls the movement of one tank.


The tutorial uses a few DOTS features: entities, systems, entity queries, entity command buffers, entity jobs (IJobEntity), baking, and subscenes.


(This tutorial is a variation of the Tanks tutorial in the Entities samples repository.)


2. Before you begin

This tutorial assumes that you have a basic understanding of C# and GameObjects. It also assumes you have gone through the HelloCube tutorial (which covers basics of Entities, such as entity creation and subscenes) and the jobs tutorial.



Set up your Unity project


To set up a Unity project for this tutorial, follow these instructions:


1. Create a new Unity project using the 3D (URP) Template.


2. Install the Entities Graphics package via the Package Manager.


You’ll need the Entities Graphics package to render entities, and installing Entities Graphics will also automatically install the Entities package itself as a dependency.


3. Create a single, immobile tank

First you’ll create the tank that will be used throughout this tutorial. To create a Tank GameObject, follow these instructions:


1. In the Main scene, create a new subscene (check out the HelloCube tutorial for a refresher on how to do this). The name of the subscene is not important, so the default name "New Sub Scene" is fine.


2. In the subscene, create a 3D cube GameObject and name it “Tank”.


3. Create a 3D sphere GameObject as a child GameObject of Tank and name it “Turret”.


4. Set the position of the Turret GameObject to (0, 0.5, 0).


5. Create a 3D cylinder GameObject as a child GameObject of Turret and name it “Cannon”.


6. Set the scale of the Cannon GameObject to (0.2, 0.5, 0.2).


7. Set the position of the Cannon GameObject to (0, 0.5, 0).


8. Set the rotation of the Turret GameObject to (45, 0, 0).


Your tank should now look something like this:



As explained in the HelloCube tutorial, baking will serialize one entity for each of these GameObjects and give each entity Transform and Rendering components. When the scene is loaded at runtime, the entities are loaded, not the GameObjects, so the tank rendered in Play mode will be composed of three entities.


4. Move the tank along a random curvy path

First, let’s add a Tank component to each tank entity in baking (as explained in the HelloCube tutorial). Create a new script file named TankAuthoring.cs and add the following code:


[@portabletext/react] Unknown block type "code", specify a component for it in the `components.types` prop

Add a TankAuthoring component to the Tank GameObject, set the Turret property to the Turret GameObject, and set the Cannon property to the Cannon GameObject. In baking, the Tank component will be added to the baked tank root entity, the turret entity will be assigned to its "Turret" field, and the cannon entity will be assigned to its "Cannon" field. (These Turret and Cannon fields will be used in a later step).



Now let’s create a system to move the tank along a random curvy path. Create a new script file named "TankMovementSystem.cs" and add the following lines of code:


[@portabletext/react] Unknown block type "code", specify a component for it in the `components.types` prop

The MovementSystem will update every frame. In the update, the system queries for all entities that have a LocalTransform and Tank component, and it updates the LocalTransform to move each tank along a random, curvy path.


5. Spawn many tanks with random colors

To spawn many tanks, first you’ll turn your tank into a baked prefab. First, make the Tank GameObject and its child GameObjects into a new prefab named “Tank”, then delete the original tank from the subscene.


Next, add the URPMateiralPropertyBaseColorAuthoring component to all three GameObjects in the Tank prefab (the Tank, Turret, and Cannon GameObjects). This adds the Entities.Rendering.URPMaterialPropertyBaseColor component to the entities. Setting the value of this component at runtime dynamically changes the color of the entity.


To bake the prefab as an entity, follow these instructions:


1. Add a new GameObject in the subscene and name it “Config”.


2. Create a new script file named "ConfigAuthoring.cs" with the following code:


[@portabletext/react] Unknown block type "code", specify a component for it in the `components.types` prop

3. Add an instance of ConfigAuthoring to the Config GameObject, set its "Tank Prefab" field to the Tank prefab, and set its "Tank Count" to 10. (The "CannonBallPrefab" field will be set in a later step.)



Now, to spawn the tanks and set their colors, create a new script file named TankSpawnSystem.cs with the following code:


[@portabletext/react] Unknown block type "code", specify a component for it in the `components.types` prop

Now if you enter Play mode, you should see 10 random-colored tanks wandering in different directions:



Note that you might see the tanks in the Game view but not the Scene view. To make the tanks visible in the Scene view, select Edit > Preferences > Entities > Scene View Mode to "Runtime Data":



6. Move one tank with player input and have the camera follow

If you want one tank to be controlled by the player, you first need a way to distinguish the player’s tank entity from the other tanks, so let’s create a new Player component add it to one of the spawned tanks:


1. Add the following code to to a new file Player.cs:


[@portabletext/react] Unknown block type "code", specify a component for it in the `components.types` prop

Note: As a matter of convention, entity component types are often defined in the same file as a related authoring component, but in this case the Player component doesn't need an authoring component.


2. In the TankSpawnSystem, add the following code in the loop that creates the tanks (after the line that instantiates the prefab):


[@portabletext/react] Unknown block type "code", specify a component for it in the `components.types` prop

3. To stop the player tank from moving along a random path like the other tanks, modify the header of the loop in TankMovementSystem to match the following code:


[@portabletext/react] Unknown block type "code", specify a component for it in the `components.types` prop

The WithNone<Player> call specifies that the query should exclude any entity that has a Player component, so the player tank will no longer be moved in this loop.


4. To move the player and the camera, create a new script file named PlayerSystem.cs with the following code:


[@portabletext/react] Unknown block type "code", specify a component for it in the `components.types` prop

Now in Play mode, you should be able to control the first tank, and the camera will follow. Because the camera moves with you, it may be hard to tell in the Game view that you're moving.


7. Spawn cannonballs at the tip of the turret

To have the tank fire cannonballs, you first need a CannonBall component:


1. Create a new script named CannonBallAuthoring.cs with the following code:


[@portabletext/react] Unknown block type "code", specify a component for it in the `components.types` prop

2. Create a new prefab named CannonBall with a single GameObject that renders a 3D sphere and set its scale to (0.4, 0.4, 0.4).


3. Add the CannonBallAuthoring component to the CannonBall prefab's root GameObject.


4. In the Config GameObject in the subscene, set the "CannonBallPrefab" field in ConfigAuthoring to the CannonBall prefab.


5. To spawn the cannonballs, create a new script file named ShootingSystem.cs with the following code:


[@portabletext/react] Unknown block type "code", specify a component for it in the `components.types` prop

Now in Play mode, the tanks fire cannon balls at regular intervals, but the cannonballs don't yet move, so they stay where they spawn:



8. Rotate the turret

To rotate the turrets, you can create another system, but it’s simpler to add some additional code to the bottom of the OnUpdate method of the TankMovementSystem:


[@portabletext/react] Unknown block type "code", specify a component for it in the `components.types` prop

9. Move the cannonballs

To move the cannonballs and to destroy them when they hit the ground, create a new script named CannonBallSystem.cs and add the following lines of code:


[@portabletext/react] Unknown block type "code", specify a component for it in the `components.types` prop

By putting the work into a job, the work is moved from the main thread to the worker threads.


10. More things to try

Here are some gameplay tweaks you might implement:


  • Give the player direct control of their turret.

  • Destroy tanks when they are hit by a cannonball.

  • Respawn destroyed tanks after a timer.

  • Display a victory message when the player destroys all enemy tanks.

  • Respawn the player but give them a limited number of lives.

11. Next steps

For more tutorials and samples demonstrating all the features of the Entities package, see the Entities Components Samples repository on GitHub.


Then, as you explore DOTS further, be sure to try our DOTS Best Practices course.


Complete this tutorial