There are lots of ways for dealing damages in games. I will do more advanced stuff:
- Damage will be calculated from weapon and ammo type,
- Critical Chance will be calculated,
- I need to see what’s the damage and if there was a critical hit (UMG),
- I need to see how much HP enemy have,
- I want to have different damage depending on which body part I shoot, (eg head, leg etc)
Let’s gets started!
<strong>This Tutorial has been created using Unreal Engine 4.8.2</strong>.
Make sure you are working on the same version of the engine.
Unreal Engine 4 has damage system implemented. You can read about it here. I will use my own system in this tutorial because I have different data than ApplyDamage from engine.
Create Damage Interface.
Let’s create new Blueprint Interface named I_TakeDamage. You can read more about interfaces here.
There should be one function: TakeDamage
It will be used for all actors that implement this interface. For example if you want Bottle to take damage just implement this interface in its class preferences. Of course currently we aren’t firing this interface so it won’t work yet.
We need to do some changes to BP_BaseWeapon.
In CalculateShootInformations function we need to change Line Trace by Channel to Line Trace for Objects.
And add these variables:
- MinWeaponDamageModifier (float, default 0.5),
- MaxWeaponDamageModifier (float, default 1),
- CritDamageModifier (float, default 2),
Now create new function AddDamageTo with inputs: Hit Result (Hit Result), EndLocation (vector)
We will call our interface from this function.
We need to update Fire function because some of the functionality (CalculateShootInformations) was implemented in BP_Weapon_Pistol. I want to have this in main class so I won’t be copying nodes to other weapons.
So we are spawning effects and we are firing AddDamageTo function from here. After updating Fire function you would need to delete Fire events from BP_Weapon_Pistol and other weapons that extends BP_BaseWeapon.
So Fire in BP_Weapon_Pistol should look like this:
Thanks to this change every weapon we will extend from BP_BaseWeapon will have deal damage functionality.
Create Damage Feedback UMG.
Create new Widget Blueprint named UI_Debug_DamageInfo. It shouldn’t have Canvas Panel. Root should be Overlay.
It contains two text block: TextBlock_Damage and TextBlock_TextCrit in this video you can see the widget. Hope you will be able to create something like this.
Creating Damage Feedback actor.
Create new blueprint based on Actor and name it DamageFeedback. Add some variables:
- Damage (float, editable and expose on spawn),
- WasCrit (bool, editable and expose on spawn),
And in components (Viewport) add Scene component as root and add Widget Component. In 4.8 Widget Components are still in experimental stage but they should work correctly with our simple graphical feedback.
Widget shouldn’t have collision enabled! Create Begin Play and assign earlier created UI_Debug_DamageInfo widget.
Create Health Bar UMG
Create new Widget Blueprint named UI_Debug_EnemyHealth.
As in DamageInfo Widget this shouldn’t have canvas panel. Instead add Overlay. Add a progress bar to Overlay and set Alignments to fill. Now in Event Graph create new function UpdateHealthBar.
This will update progress bar depending on Current and Max variables. You could use UMG binding for this but I have seen that they are called on Tick so it isn’t so optimal.
Create new blueprint extending from Character name it BP_BaseEnemy and add variables:
- isDead (bool),
- CurrentHealth (default 100),
- MaxHealth (default 100),
Add Begin Play and assing MaxHealth to CurrentHealth.
MaxHealth will be used in later posts as a gameplay balance value.
Now let’s add health progress bar as a Widget Component. Here’s the properties:
Change Collision for Mesh so traces from weapon will block on enemy mesh.
Create new function UpdateHealthBar:
And another function ModifyHealth. Input: HealthToModify (float) and Output: IsDead (bool)
Last function: CalculateDamage.
- Damage (float),
- CritChance (float),
- CritDamageModifier (float),
- WeaponDamageModifier (float),
- HitInfo (Hit Result)
- DamageTaken (float),
- WasCrit (bool),
- Local_WasCrit (bool),
- Local_CurrentDamage (float),
This function is calculating damage and returning it.
Now in Class Settings add I_TakeDamage interface as implemented interfaces and create new event I_TakeDamage.
That’s all here. Now the most time taking part…
Implementing Body Parts
I want to use Physical Materials to check which body part was hit instead of calculating length. It will be faster but it require some preparations.
Go to Project Settings -> Physics and add those to Surface Types:
It can be done quicker if you go to Config folder (YourProject/Config) and in DefaultEngine.ini add those to [/Script/Engine.PhysicsSettings]
We have types added but now we need to create Physical Material for each of them. Names should be the same (PM_SurfaceTypeName) for example PM_Metal_RightFoot.
Open created Physical Mat and change Surface Type should correspond with name. For example here’s my PM_Flesh_RightFoot.
One important thing: we are using Desctructible Damage Threshold Scale as our Damage Scale. So for head it should be 1 and for foot it should be 0.1 – use values as you like!
I know – lot’s of clicking but this need to be done only once so be patient 🙂
Implementing Physical Materials
Create new blueprint extending from BP_BaseEnemy and name it as you wish – he will be used only for tests. Set mesh to hero TPP. Compile and add this character to your level so you can shoot at him.
Go toHeroTPP_Physics and when selecting bodies you can select Physical Material for the body.
Now assign Physical Materials to body parts and you are done! i know that we could create Head, Torso, Leg instead of LeftArm etc But these will be needed later when I will destroy body parts.
If you want to see different effect on different body type of character you need to update your ImpactEffect actor.
Thanks to this system I’m able to define body parts, calculate damage and crit chance!
Implementing game is taking time but writing about this is taking much more effort!