Dealing damage to AI using hitboxes, ragdolls, and healthbars in Unity [AI #02]

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
kia ora welcome back i'm the kiwiker and this video i'm going to show you how to add damage to your ai characters uh so the characters are using hitboxes for collision detection which gives more accurate head testing uh when each character is hit there's like this blink to white effect which is achieved by modifying the color intensity and uh each of the agents has also got like a health bar just displaying like how much health it has left and this health bar is drawn in screen space to prevent it from like shrinking and getting like too small if the character is uh far away from you uh it also means that we don't need to align the health bar to the the screen at all and finally uh yeah when the characters basically when their health drops to zero they go into this cool ragdoll state which uh you can then just play around with which is quite fun sweet uh so yeah let's get into it this video follows on from a previous video in the ai series creating your first animated ai character so in the scene here i've just got a um really simple ai character that is following the player and you can see that the um when i shoot it basically the raycasts are just going directly through the the ai character and hitting whatever is behind it so the first thing to do is just adding some collision to the character and that way we can start looking at taking damage and ragdolling the the character uh so the first thing well there's a couple of ways that we can do it one is adding just like a rigid body and a collider directly to this object here but that won't give very accurate sort of head testing like it won't tell us if we've had the head or the the arms or anything like that you know if you wanted to do things like head shots so instead we're going to use the uh the ragdoll wizard and you can open that up from this menu here and yeah basically you just need to fill out the all of these fields it's a bit tedious but yeah i use the hips for the pelvis and um the upper leg r for the right hips upper leg l for the left hips and and so on so i'm just going to fill this out really quickly now okay so that's all done and um just going to set the total mass to 100 just to make the character a little bit heavier and uh when i push create you can now see it's basically added colliders for each of the different limbs it does recommend that your character is in t-pose but seems to work like good enough um you can then go and sort of fine-tune these colliders after the facts but i'll probably do that offline um so yeah if we just select like one of these bones for example we can see it's added a box collider a rigid body and a character joint and um there's a whole bunch of sort of twist limits and stuff that you can tweak but i'm gonna leave all of that stuff default just to begin with okay so um if we go into play mode then it probably looks okay on on the surface like i can walk on top of the character uh which means it does have collision and i think it managed to actually pick up that pistol so let me grab this one so yeah now if i uh if i shoot it you can actually see the sparks coming off the character which is a good start but there is like a slight issue and it's much more noticeable if we go to the window analysis menu and open up the physics debugger this basically lets you inspect the um the state of the physics world which can actually be different from what unity shows in the viewport and you can see that um if i just move the character back for a minute or let me just uh actually disable i'm just going to split this set the speed of the agent to zero and uh yeah i'll just unpause it now um so if i go to the scene view and select the physics debug tab you can actually see those rigid body colliders they're like flickering rounds quite a lot and um basically by default the ragdoll wizard sets up the rigid bodies in a simulated state so um we only want to be in that state when we actually ragdoll when we when the character is walking around this is kinematic flag needs to be set on every rigid body so yeah i'm just going to create a new script to do that now so if we just open a new c sharp script and i'll just call this like ragdoll and just add this to the agent ragdoll cool so open this up cool so the first thing that we need to do is just get a reference to all of the rigid bodies uh that the ragdoll wizard created so we can do that using get components in children and uh just you know passing the rigid body type there and yeah that will recurse through the entire hierarchy and get all of the rigid bodies um next thing i'm gonna do is create two functions one called deactivate ragdoll and another one called uh you can probably tell activate ragdoll sweet um so inside star i'm just gonna call deactivate ragdoll and we already know in here that we basically want to uh loop through all of the rigid bodies uh in that rigid bodies list and basically set the rigid body is kinematic flag equal to true and then pretty much just do the opposite inside activate ragdoll uh so here we want to set is kinematic to false which basically means it's being simulated instead of uh controlled by animation um and there's one more thing that we need to do which is get the animator and uh when we activate the ragdoll um basically the animator needs to be uh disabled so we can just get a reference to the animator inside start and just here just call animated.enabled equals to false and inside deactivate just call animated.enabled equals to true sweet so that should be it and that should basically fix the glitching issue so if we pop back into unity and go into play mode um so yeah now if i go into the scene view and open up the uh physics debugger again i'm not sure where that went uh analysis physics debugger yeah so now we can see that those uh ragdoll colliders they just sort of remaining static and they're actually color blue now rather than pink which just shows that they are kinematic cool so the next stage is just actually creating a health component for the agent so if i create a new health component here and just attach it to the agent then what we want to do is basically just create a new public float to represent the max health of the agent and a private one or i guess it could be public as well maybe i'll just make it public and uh yeah inside start we just want to set the current health equal to the max health and just create like a new function here called i don't know like uh take damage with some amount and just check if the current health is uh or i guess first subtract the amount and then check if the current health is less than or equal to zero then just call some function called light die and i'll just use alt enter to create that function and in here this is basically where we want to activate the ragdoll so need to get a reference to the ragdoll script here uh so again you can sign it in the inspector but i kind of prefer just to get it using get component which i guess has probably got a slight startup toll but uh yeah i don't know it's there's less stuff to configure in the inspector so yeah inside die i'm just gonna call activate ragdoll and yeah i think that should be pretty much it for the health component at the moment um so now we just need to create another script to actually call take damage and this needs to be invoked um inside a raycast weapon script so when a breakout actually hits something currently the only thing that we're doing is applying a force to a rigid body and in this case uh we have like multiple rigid bodies so there's a rigid body per one of these joints and we need some way to kind of point all of the rigid bodies back towards this single health component where we can call take damage i'll just set the uh the max health to a hundred and i think one more thing i'm just going to hide the current health in the inspector still nice having it public so i can access it from outside the script but i don't want this value to be configured inside the inspector um so yeah the way to do that is i'm going to create a new script called hitbox and the hitbox script is going to be attached to every single one of these rigid bodies and that hitbox script will point back towards the health script so let me just create the new script called hitbox so the hitbox script itself is going to need a reference to um the health script and i'm going to create a new function here called um on raycast hit and here it's going to take in the weapon on raycast weapon or public void and yeah here we just need to call now take damage um with the amount so the amount of damage to take should probably be a property of the weapon so i'll just create a new value here damage and i'll initialize it to something like 10 it's probably a decent default value and here we can just call take damage uh weapon damage so now we just want to look for basically a hitbox script on uh inside our raycast weapon script when we actually collide with something so i'm just going to copy this chunk of code here because it's pretty much the same but instead of rigid body this is going to be a hitbox um like this and instead of calling add force now we just want to call that on raycast hit and pass in this uh so it's it's currently asking for a raycast weapon and we're calling it from the raycast weapon script so we can just pass this in there um actually there's one more thing i might pass in just for now which is the uh the direction of the ray uh because we'll use that to do some physics stuff in a minute so may as well just do that while we're here just add the direction to the take damage script as well and uh then inside the recast weapon script we just want to pass in the ray dot direction sweet so the final thing is um we've got these hitbox scripts but they're not actually added to anything um so i could sort of go through each one of these manually and add it but i'm kind of too lazy to do that uh and so instead i'm just going to do it inside the health script so we already have a reference to the ragdoll ragdoll component which has got the rigid bodies and if they are public then i should be able to just access them um although yeah maybe it's not super clear which of these start functions is going to get called first um either the health or the reactor i know you can change that um using the the script execution order but i don't really like relying on that so i'm just gonna do the same thing here um it's just gonna be a one-off we don't need to hang on to those rigid bodies so it's going to go get components in children rigid body and just loop through each rigid body and ridge bodies and now we just basically want to add a new component um so we just need to get the game object that rigidbody is attached to and call add component and yeah create a new hitbox component for each bridge body so that hitbox component has got a public field which is the health which we just need to assign to ourself like this and yeah so now if i go into play mode inside unity when we hit play we should actually see these hitbox components being added to yeah so now we've got a hitbox component added to each one of these colliders which is um quite handy and it's pointing directly to the health component so this actually might work if i just get a gun and start shooting them no something's not working uh it might be the prefab um that damage field might not have been set correctly so if i just yeah damage is set to zero so just need to set that to something like 10. and if i go get a weapon now and shoot him yay so now he ragdolls when he dies which is awesome cool so the next step is going to be adding like just some some effects to the character okay so the effect that we're going to go for is just like a blink to white effect um so i can kind of illustrate that if you open up the skin to mesh renderer and then just double click the material if you enable emission and then just bump up the emission value to white uh you can then modify the intensity and make the character glow like this and uh what we'll do is basically just every time the character gets hit just set the intensity up and then just like blend it back down to its its normal value um so let me just reset all of that stuff and disable the emission and uh yeah i'll just do all of this inside the the health script for now you could potentially separate this out but it's uh that's fine to do it here as well so gonna need a few public variables one for the blank intensity uh so this will be how bright the effect is uh one for the blank duration so basically how long it sort of fades out how long it takes to fade out and another one for the blink timer and that will just keep track of where we are in our blend um so yeah i also need to get a reference to the skinned mesh renderer um so you can get a reference it's not on the the current the top level game object it's actually on a child so just need to call get component in children skin's mesh renderer cool and inside die uh no sorry inside take damage what we want to do is just set the blink timer equal to the blank duration um so each time we get hit we just reset the timer basically and uh inside update um just going to first thing that we'll do is just decrease the blink timer by time.delves time and uh yeah the next thing is uh basically we just want to get a blending factor or a lerp factor which is always zero to one so we can just call math f dot clamp zero to one which ensures it is between zero and one and then take the current value of the timer and divide it by the entire duration of the effect and now we can calculate a final intensity value uh equal to the value times our blink intensity and then finally we can just uh set the skin mesh renderer.material.color equal to color.white times the intensity and yeah basically if i was to put type like one here that would just have like the color white and it would just be the sort of default uh color that you see in the scene here um but yeah if the value is more than one then you get that like bright uh sort of white effect uh so yeah i think that is that's pretty much it um so if we just go back into unity and then set up the blink intensity value um i'll just pick like 10 and let's make the duration like two to start with now if i go into play mode oh the character is black oh yeah sorry i forgot something so yeah it does it does work like if if you shoot it then it goes like bright white but then it fades all the way out to black and that's basically because this uh this lerp value gets down to zero and then when we multiply white by zero we get black and so the way to fix that let's just add add on uh one here that means the minimum value will always be one so if i go back to unity hopefully unity just updates cool um yes that looks pretty good so my animation has stopped so i might just restart this and if we just try out like different values so if i set like quite a small value here like maybe 0.1 yeah then you get that that quite cool sort of blinking effect um blink intensity i mean you could bump this up something massive it looks a little bit over the top i think even if i set it to one you can see it even more it's pretty cool though like it's uh seems to be quite a decent effect for not much code at all cool um so yeah i'm pretty happy with that i think i'll just try the rifle because this one shoots a lot faster yeah it's it's maybe a little bit fast uh sorry a little bit slow i kind of want it blinking yeah that looks way cooler actually okay sweet okay so it's the last step of the tutorial where we'll just add like a ui health bar uh to to the agent here so what i'm gonna do is create a canvas element and i'm gonna remove the graphic raycaster because we don't need to interact with it in any way and i'm going to use screen space overlay for this and the main reason for that is i don't want the the ui element getting smaller if i get further away from the character so i'm going to do everything in screen space and i've added the canvas element to the child of the agent here so um what i'm going to do now is create a child object just to represent the ui health bar and create two more children one to represent the background of the health bar and another one to represent the foreground of the health bar and both of these ones i'm going to add an image component to them and use the built-in background image that comes with unity and i'll set the background to red and the foreground to green cool um so to actually we can actually see this object so to see it i'm just going to go to 2d view double click on the canvas and now we can see the object there so if i resize this ui health bar the background and foreground elements aren't updating with the size of the health bar so i'm just going to select both of them click this little icon here then hold down alt and click the this bottom uh right hand icon which just makes the the background and foreground elements stretch to the size of the ui health bar which is cool so i'm going to use a width of 100 and a height of like say 10 or something so you can check the game view how this how this is going to look it's about that size maybe it's a little bit too big it might go with 80. um yeah cool that will do so the first thing to do is um basically synchronize the position of this health bar with the agent so i'm going to create a new script called ui health bar and just add that to the root object here and this ui health bar is going to need a target transform to synchronize with so just open up that script and create a new public property called transform and this is just going to be the target transform i don't think i'll need the start function but i'm going to do all the synchronization inside late update so that just ensures that the target transform of the agent has been updated with animations and everything first and then we finally go and update the ui uh to match that position at the end of the frame um so because the target transformer is in world space but the ui health bar is in screen space we need to do a conversion here so we can do that using transform.position equals camera.main dot world to screen point and then just pass in the target transform position there cool so this target transform i will set to the the agent's head which does mean if it animates and stuff like if the agent were to bend down for example then the health bar would follow you may or may not want that but um yeah that's that's really up to you to your game i guess so if i just find the head object here and assign that now if i go into play mode we should be able to see that the health bar yeah cool so now synchronizes to the uh the head position of the character it's probably need to offset it a little bit so if we just open up the health bar script and create a new offset value here so offset and we can just add that offset onto the target transform like this cool so the offset value um probably it's best to tune and play mode so if we just go into play mode and adjust the y value here yeah 0.4 probably go with cool 0.4 so now uh the next thing to do is uh to actually adjust the the the health bar size we want to resize uh this element here uh so we want to resize the foreground element by stretching it down and i've seen a few ways uh quite a lot of youtube tutorials they they recommend switching to this field mode and then uh switching to horizontal and that gives you this like filler mount slider which is quite convenient but um notice what it's done to the edge of the image it's like stretched out quite a lot so i don't think that looks very good so i'm gonna carry on using the sliced uh mode and we do lose the ability to have that like filler mount slider but um we just need to do that in code now so what i need to do is get a reference to this uh this foreground image inside the health bar um so the because it's an image it's going to be inside the unity engine.ui namespace and just create a new property here called foreground image i'm actually going to need the background one later as well so i'll just do both at the same time and yeah i need to create a new function here called like set health bar percentage and this should just take a 0 to 1 percentage amount and we'll invoke this from the from the health script in a minute every time we take damage uh but first we we just need to calculate the width for the foreground image so that's going to be a proportion of the the size of this top level object so we can get that parent width by just going parent width equals get component rect transform so that's where this this script is on the top level object and the rec transform has a property called rex which you can then get the width from so the the final width of this foreground image should just be basically multiply the parent width by the percentage value there and finally we can set the width of the actual foreground image by using foreground image dot rect transform dot set size with current anchors and in here you can just pass in the horizontal axis and pass in the the final word it should be so the the next step is just adding a reference to this ui health bar inside the health and invoking this function so if we switch over to the health script and uh just create a new um property here called ui health bar and this is going to be health bar so you could either assign this in the inspector again or i prefer to just do it in code most of the time so just call get component in children ui health bar and now each time we take damage we can just call take damage dot set health bar percentage and because this is a zero to one amount we just want to take the current health and divide it by the max health and also finally when we die we probably just want to hide the health bar entirely so uh it's not hanging over dead characters cool um so i think that is pretty much it for the scripting part so if i just assign the background image in the foreground image to the the health bar script here uh then hit play go into play mode now if i shoot the character cool so you can see that the um this health bar is now resizing except the the pivot is not quite right it's still centered around the center here so we just need to sit uh select the foreground image uh click this icon hold down shift and uh wait make sure that little blue circle is there and uh click this this one in the middle left here and that basically moves the pivot point over to the um the left hand side of the image here which is the the pivot point is basically where all images and ui elements will scale from so now if i shoot it cool so now it's all scaling around the left-hand side so there is um still one small bug which uh i just killed killed him so i couldn't really show you but um if i look in this direction the position of this element is still off um and this is kind of the easiest thing to do is just hide the these two images here when uh when the health bar is behind the camera position so if we just go back into that health bar script we can calculate the direction the health bar or the the target transform that we're synchronizing to calculate the direction it is to the camera so if we just get the target dot position minus the camera.main dot transform dot position and we're going to use a dot product so this vector needs to be normalized and an easy way for testing if an object is behind another object is basically is behind we can just take the uh dot product of the direction vector with the forward vector of the camera that we of the object that we want to test so in this case it's the camera so we can just take the dot product of the direction with the camera transform and if it is less than or equal to zero then we know the object is behind us so if it is behind us then we can just set both the foreground image dot enabled equals to the opposite of is behind and same for the background image cool um i think that's that's pretty much it so just test this out quickly go back to play mode grab a oh i don't even need to grab a weapon yeah so you can see that the the health bar is visible here but if i look in the other direction oh there is a slight glitch oh no it's just because i was clipping with the player cool yeah so uh the the image is no longer hanging around in front of us like it was before sweet so yeah i mean that's that's pretty much it for this so there's one more thing that i completely forgot about um when we shoot the character currently it's just like falls limpy to the ground so it'd be nice to actually apply some kind of force when the character dies which is why we are already passing through the um the direction to the take damage function on the health component so if on the ragdoll if i just create a new function here calls like apply force and just give it the um the force to apply uh force now um we could just apply the force to every single one of these rigid bodies uh but another way that i found looks maybe sometimes better i'm still not sure is um you can uh just call it animated get bone transform and then just get the hip bone and then get the rigid body component from that and with that rigid body now we can just call add force and just add that force but the thing that i'm going to use i'm going to use force mode dot velocity change which just means it doesn't take into account the mass of the the character um so now from the health script when we die basically just pass this direction through here and this is going to be a vector3 and on the rag doll just call apply force and pass in the direction plus some scaling value which i'll call like die force i guess and i'll just create a new property up here called float die force something like that and um one more thing that i'm going to do is rather than just passing in the direction i'm going to um just set the y direction to 1 that means the character will kind of always fly off into the air [Music] rather than say like dying directly into the ground so yeah if we just hit the dive force to something like 10 then i think that should be pretty much it so grab a weapon and shoot him hey so he kind of like flies off into the air which yeah looks sort of cool and you can still shoot him okay cool yeah so that is uh literally it for this tutorial um thanks for watching really appreciate it if you want to stay up to date with this uh with this ai series and my future videos then please hit the subscribe button the bell notification icon and give it a like if you have enjoyed it thanks for watching and we'll see you in the next video kakite
Info
Channel: TheKiwiCoder
Views: 14,567
Rating: undefined out of 5
Keywords: ragdoll, ai, damage, health, hit effect, unity, unity ragdoll, unity ai ragdoll, ai damage, ai health, health bar, unity health bar, unity enemy health, unity damage, ai ragdoll, unity take damage, health system unity, ai system unity, health bar unity
Id: oLT4k-lrnwg
Channel Id: undefined
Length: 30min 40sec (1840 seconds)
Published: Fri Dec 18 2020
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.