Welcome to part 6 of this Unity tutorial series where we’re creating a survival shooter
game using Unity’s new implementation of the Entity-Component-System pattern, or Unity
ECS for short.
In part 5 we began implementing player shooting by creating a system that tagged the player
entity when the “Fire 1” button is pressed.
In this video we’ll continue our work by writing another system that’ll remove this
component after a certain period of time.
In other words, we’ll be implementing rate of fire.
So without further ado, let’s get right into the video.
Let’s get started by creating a new system.
I’ll call it “CleanupFiringSystem” and open it in the IDE.
This’ll be a pure system so let’s derive it from the JobComponentSystem class.
Before we go any further, let’s do a quick review.
This is the PlayerShootingSystem.
It also derives from the JobComponentSystem class, which mean that it utilizes Unity’s
new C# Job System.
The C# Job System was designed to make it easy to write highly efficient code that can
run on multiple processors.
It relies on multithreading, optimized memory layouts, and advanced compiling techniques
to produce builds that run extremely well on wider range of devices.
The PlayerShootingJob is responsible for adding the Firing component to an array of entities.
It derives from IJobParallelFor, which is basically a for loop that runs in parallel.
The PlayerShootingJob is scheduled inside of the PlayerShootingSystem’s OnUpdate method
whenever the “Fire1” button is pressed.
By the way, I got a bunch of comments about this code in part 5 and I’ve refactored
it to be more efficient.
If you see anything else like that, please continue to let me know!
The Data struct represents a concept that we’ve already seen in this series.
It’s purpose is to tell Unity what kind of entities this system is responsible for
to operating on.
We facilitate this by adding a Data field to the system and marking it with the Inject
attribute.
Unity then uses reflection to provide an instance of the struct that’s populated with all
of the appropriate values.
In this case, the ComponentDataArray tells Unity to populate the struct with entities
that have the Weapon component.
And the SubtractiveComponent tells Unity to ignore any entities that have the Firing component.
The EntitiyArray is populated with all of the entities that meet the component specification.
And the Length field is populated with a value that reflects the size of the EntityArray.
It’s a readonly field because if we were to modify the value by accident, the compiler
would throw an error.
Lastly, the PlayerShootingBarrier is a special class that we can use to perform actions on
our entities.
It’s not safe to add, modify, or remove entities inside of jobs.
But barriers live on the main thread, so we can use them to create a command buffer that
queues up and executes actions when it’s safe.
Alright, the review is over.
Time for pop quiz!
Just kidding, no tests in this series.
But we can use what we’ve learned to write our next system.
Let’s switch back to the CleanupFiringSystem class.
Like I mentioned before, the CleanupFiringSystem will be responsible for removing the Firing
component from entities after a certain period of time.
This will result in behavior that, in effect, will emulate rate of fire .
To switch things up a bit, let’s start by creating the Data struct.
The system will be modifying entities, so it’ll need fields to store an EntityArray
and its Length.
We only want to modify entities that have the Firing component, so it’ll also need
a ComponentDataArray.
Now let’s add a Data field to the CleanupFiringSystem and mark it with the Inject attribute.
Again, Unity will take note of this attribute at startup and populate the field with an
instance of Data.
We’ll also need to inject a barrier system that we can use to modify our entities safely.
Let’s create one now.
I’ll call it “CleanupFiringBarrier”.
And add it as a field to the system with the inject attribute.
The last thing we need to do is create the actual job and schedule it.
I’ll call it “CleanupFiringJob”.
Now we can add an EntityArray and EntitiyCommandBuffer and implement our logic.
Alright, we’re almost there but we’re missing an important business rule.
The job removes Firing components from entities immediately.
But instead we want to remove them after a certain period of time has elapsed.
So we’ll need to do a quick refactor.
First, let’s add a new field to the Firing component.
We’re going to base the rate of fire logic on the time a bullet is fired.
We’ll subtract the fire time from the current time and see if it’s greater than some predefined
period.
If it is, then we’ll remove the Firing component.
So let’s add a float called “FiredAt”.
And we’ll initialize it when the component is added to an entity, which currently only
happens in the PlayerShootingSystem.
Now we can grab the current time from Unity’s Time class, and pass it into the PlayerShootingJob.
Now let’s switch back to the CleanupFiringSystem.
And perform a check on current time versus the time that the bullet was fired.
We’ll need to add fields for the the current time and Firing components to the job first,
of course.
And then we’ll add a guard clause that returns if the difference is less than 0.5, which
amounts to half a second.
Lastly, we’ll finish up the system by scheduling the job and passing in all of the appropriate
values.
And that’s all there is to it!
Let’s switch to the editor and run the scene.
We aren’t generating bullets yet so we’ll need to open the entity debugger and select
the weapon entity to see the results.
Watch what happens when I press the “Fire 1” button.
The Firing component is added and subsequently removed half a second later.
Now watch what happens when I hold the “Fire 1” button.
Beautiful.
Now we’re ready for the fun part.
The part where we actually generate bullets!
If you’d like to download the project files for this video and support my content at the
same time, please consider becoming a tier-2 patron today.
You can find a download link in the description and comments below.
So the results of all of our hard work has been a little boring so far.
But I promise in the next video we’ll start to see some bullets fly.
And we’ll implement a new style of system that reacts to changes made to entities.
You won’t want to miss it.
If you found this Unity tutorial helpful, please leave a like and a comment letting
me know what you thought.
And for more videos just like this one, don't forget to subscribe with notifications on.
I'll catch you in the next video.
Thank you to all of my patrons.
And a special shoutout to Thomas, Chance McDonald, Glaswyll Entertainment, Richard Stan, and
Yakov.