If you want to skip the tutorial, there is a unitypackage download on my patreon
I'm the type of guy to where before I waste time reading or watching something I need to see the end result.
Keep in mind this tutorial will teach you some of the best, easiest and most useful systems for any type of combat or AI game, animations are a very important aspect to any game and without the right ones it wont look the best!
So these are free Animations I used from Mixamo.com for testing purposes, feel free to use your own animations!
So here it is:
There are a few lines of code we need to change in our older scripts to get our character to work correctly.
We start off by opening our Camera Controller script and we remove everything except this chunk of code. That should be it for our camera controller as of now!
A little trick I learned after having to grab variables between scripts, is making a central brain. This is where we can place most of our important references and voids to keep the rest of our code clean and to avoid having to constantly pull up the same components, giving us great optimization!
So we create a new C# script and call it “PlayerBrain”.
First, we add the variables that will be used across most of our scripts such as the Animator, Health, HealthState, and some references. Since this will be our brain we will handle controlling some core functions here such as the taking damage, etc.
We also create an “isDead” bool so we can always detect when we are dead and if we die call a certain void.
Once all the variables are setup we can handle grabbing our references on Start(), making sure our curHealth matches our max health, then grab the Animator component.
GetHit() is a simple function that when called will take away from the current health, only after checking our “isDead” boolean, when this bool gets checked it will run our function and check the health. If we arent below 0, then it will continue to take away health, if we are, we will turn to the Dead health state.
Don’t forget to drag the Camera Root reference. This is the parent of the Camera.
Now we have some small updates to our PlayerController. First, we need to get access to our PlayerBrain, we add a new variable for PlayerBrain and on start, we search our game object for that component.
We add a new void that will handle rotating our player to look in the direction of the camera. This will use the “CameraRoot” from our player brain, so to access it we need to type “brain.cameraRoot”.
Then we just add the void after our move function.
With these changes, our controller should now be able to look in our camera direction and walk with no issues.
Getting to the final part we can add combat to our controller, first, let’s update our animation controller.
We add two new Triggers to our controller variables, “StrikeR” and “StrikeL”. For our combat, we will be using Mouse1 & Mouse2 to add with our left and right hands.
After adding new variables we added a second layer and called it to combat. Be sure to edit the settings and make the weight 1.
Right-click and create a “Sub-State Machine”.
Now we need at least two animations to test, we have Punch Left and Punch Right, so we named them accordingly. Our entry comes from “Any State” and we make sure the triggers match the animation and unclick “Has Exit Time”.
You can exit them at “Exit” and this doesn’t require anything inside the parameters or conditions to work just leave it empty.
So here comes one of the good parts, we need to create a new script and call it “damageCollider”. One of the hardest subjects I had difficulty learning was how to handle damage and impacts in a professional way.
Some say Raycasts, some say colliders, others have their own methods. Raycasts are great but they are very heavy on optimization and can easily cause lag. So the best method to go for as a base setup is colliders.
So I am going to teach you one of the best and easiest ways to handle damage to both enemies and players in the same script.
First, we create our enum to detect which hand our collider is in. Later we can add more options to fit our game needs.
We add a minDamage and maxDamage to give us full control over each individual damage and finally we create an “ignoreThis” GameObject because we don’t want to be hitting ourselves.
After adding the variables we need to create one more script and leave it blank, for now, create a new script and call it “AIBrain”, this will be our enemy version of PlayerBrain.
So we don’t have to drag our “ignoreThis” object to every damage collider we use we can instantly search for our PlayerBrain & AIBrain, stopping any future issues of hurting ourselves with our own weapon.
Now here is where the glory happens, we use Random.Range to give us a mix between our two damage values, then we check if our collider has either of our brain components, if it does we call the GetHit() function and watch the magic happen.
We will get to the AIBrain and I know right now there is an error showing for the enemy GetHit() side. If it bothers you feel free to copy and paste the PlayerBrain GetHit() function into AIBrain, we will be going over that soon!
So let's create a new script and call it “Combat Controller”, adding the variables we need such as PlayerBrain, our references for “damageCollider” and some variables for timers.
First, make sure we grab our PlayerBrain.
Next, we work on our Timers, this will help us have a structured sense of time and intervals for how long it takes us to attack. We make sure to check if our “rhCD” or “lhCD” is true before counting, then if we go over our timer we stop the timer and set it back to false, meaning we can start it again.
So we only need to check for if (!rhCD) then we can attack and while it's true, that means we are under cooldown.
Then to control our damageColliders, we create a coroutine and call it “flashWeaponColliders”, and add our enum HandSlot as a receiver, this will check every damage collider, and check the hand type to ensure it activates every left or right-handed damage Collider, then turn it back off.
The last piece is calling it all, we create another function called “AttackTrigger” and check for pressing the left or right mouse button and double-checking that rhCD or lhCD isn’t activated before attacking.
If everything checks out, we call our animation trigger, call the weapon flash for the right or left hand and set our CD to true, starting our timers.
Finnally adding the Timers() and AttackTrigger() functions into Update().
For the most part, everything is the same as PlayerBrain as of now so you can almost copy and paste that script.
The one difference is for visual effect and we add a “GetHitFlash” coroutine which will change our enemy’s color to red and back to white(default).
Setting It Up
To start let’s add our GameObject of choice, for us we added a capsule collider then attached our “AIBrain” script.
Then we set up the Player’s new damage system by first adding two new GameObject cubes onto our characters rig, under both the right hand and his left hand.
Set the “Box Collider” to be a trigger, add a rigidbody to detect collisions, click “isKinematic” to stop a rigidbody from using their default forces, then add the damageCollider script to each hand and set its variables.
Finally, attach the CombatController script, drag your damageColliders & set your timers!
You can see the final result here!