Hello everyone!
Welcome to this episode on unity DOTS. Today we will learn more about the unity physics package.
We will scan our surroundings to react to near by entities using the OverlapSphere method
and discover the ClosestHitCollector.
And if you stick to the end of the video, you will
also learn about the ComponentLookup concept.
This concept will allow us to interact with other
entities to implement simple combat mechanics.
You can get the starter project for this video
in the GitHub linked in the description. The only thing missing will be the skeleton model.
But you can get it for free in the asset store.
And you can check out my other videos to learn
how we made this starter project from zero.
By the end of this video, you will be one
step closer to a tower defense prototype as you can see on screen.
Now, let’s dive into it!
The first thing we want to do is to prevent the
user from placing two towers in the same place.
To do that, in our InputSystem, before
placing the tower, we will check that there not already a tower at the same place.
That can be done by using an OverlapSphere.
Unlike a RayCast that finds entities in a
single direction, the OverlapSphere will look for all entities within a fixed Sphere.
It must not be confused with a SphereCast. A SphereCast like a RayCast is moving in a single
direction and will gather entities along its path. But instead of the line of the RayCast,
it will use a Sphere, making a larger path.
The OverlapSphere doesn’t move. It checks
its surroundings in all directions.
We can use it to make sure there isn’t
anything where we want to place our tower.
Using the physicsWorld, call
the OverlapSphere metod.
Provide it the position where the tower would
be adding some margin not to be on the terrain.
Then the radius of the sphere. 0.1 will be
enough for it to overlap with the tower.
For the list of hits, we will need to
declare a NativeList. This is one of the native containers that are compatible
with Burst and thread safe to use in jobs.
For now, we don’t really use it so you can just
consider it as a normal list. We will explore native containers in another video.
Finally for the collision filter, you can use the default filter as we want to check
if anything is in the place of the future tower.
The method returns a bool, so we can just wrap
our tower spawning logic in an if statement.
If we head back to unity and enter
play mode, you will see that we can’t place two towers at the same place.
But we can make a tower of towers…
That’s because all our physics is
using the default filter layers. So, everything collides with everything.
Including the RayCast to get the terrain position and the Tower.
To fix that we can create a Physics Category Names asset to define collision layers.
We can name one Input, another Terrain and the third Tower. Let’s also make an Enemy and
Projectile layer for later in the video.
Last thing to do, parametrize our ray cast
that is hard coded with the default filter.
In the PlayerManager add two PhysicsCategoryTags,
one called belongs to and another called collide with.
Assign their values to override the default filter.
Then save your file and test it in Unity.
Set the PlayerManager to belong to the input
layer and collide with the terrain layer.
Now assign the layer to the
objects using a physics shape authoring component instead
of the colliders and body.
And the tower to belong to the Tower layer
and only collide with the Tower layer.
As you can see we can now place towers on the
terrain without the risk of placing multiple tower in the same spot or one on another.
Now what I want is for the tower to find the closest enemy and fire a projectile at it.
The logic is very similar to the one used to spawn our enemies, so I won’t
go over the details again.
If you want more detailed explanations, you can
check the video where we spawned our enemies.
You can just grab the GitHub
gist in the description and paste it in your project to get all the code.
It’s also a good time for you to hit the like
button if you enjoy learning with my videos. It shows me the content is appreciated and motivates
me to continue this journey. That also help the YouTube algorithm know this content is worth
sharing with other. So, thank you for your like!
Once the gist is in you project you can
dispatch its content into separate files.
After that you can setup the authoring components
you just imported on the game objects.
Create a sphere prefab for our projectile
and add the Projectile Auhtoring component, a Shape and a Body to it.
In each tower prefab, add an empty above the tower
and assign it the Tower Authoring component.
The enemy will also need a new
Damageable Authoring component.
Now, If you enter play mode an make a tower,
you’ll see the tower spawns a projectile. But the projectile doesn’t do anything.
To make it go towards an enemy we
must first find the closest one.
We will again use an Overlap Sphere.
But this time we will use it with the ClosestHitCollector to get the closest
entity. That will be our target enemy.
We can store a reference to the target
enemy entity on the projectile.
To make our projectile follow the target,
we can make another foreach loop that will make the projectile move forward.
For the projectile forward vector to point toward the target entity,
we will need the target position.
But since we are iterating over the projectiles,
not the enemy, we don’t have access to it.
To get the target entity position we
will need to use a component lookup.
The component lookup provides a way to get
the component data for a specific entity.
It is less performant than the foreach
iteration as it can access any arbitrary entity, so avoid using it to iterate over data.
Despite the random memory access it implies, it is the best solution when it comes
to implement entity interaction.
You can create the component lookup in OnCreate,
with a true flag to mark it read only.
And update it outside the foreach
before using it as an array.
We can also check if the entity
still has the expected component.
For instance, if our enemy has been destroyed,
we won’t be able to get its position.
And the projectile will have nowhere
to go. So, we can destroy it.
Going back to the editor, after setting
the collision layer on the enemy, you should now see your projectile move toward
the closest enemy at the time it is created.
We can use another component lookup on the
health component to deal damage to our enemy.
Use the false parameter value
to mark it a read and write.
Now in a foreach, iterate over all
projectiles and if it is near the enemy, destroy it and reduce the enemy health.
To write to a component using a component lookup, you will need to first make
a local variable of the component, modify it and assign it back to the lookup.
This is because the components are value types, and they are not returned by reference.
So, when you get the component from the lookup. What you get is a copy of the
component. Not the actual component itself.
Finally, we can check if the new health is
below zero. And destroy the enemy if it is.
Go back to unity and start killing your
enemies by placing defense towers.
We used a simple distance check to know
when our projectile hit our target.
This is fine because we know between
which entity to check the distance.
If we were to make a piercing projectile, we would not know that since it
could hit any number of targets.
And iterating over all enemies to check
the distance of each one isn’t ideal.
So, for the next video, we will see how
trigger events work. This will also be the opportunity to introduce the Job concept.
In the meantime, hit the like button and share this video with anyone learning DOTS.
If you have a question or feedback leave a message in the comment section or on discord.
See you in the next video!