WEBVTT 1 00:00:06.025 --> 00:00:09.965 Game Advanced Prototype version 4 2 00:00:27.597 --> 00:00:29.184 Hi, everyone 3 00:00:29.184 --> 00:00:32.210 I am Kim Hyun jin, in charge of the creation of FPS game 4 00:00:32.210 --> 00:00:35.230 In this lesson, we will implement 5 00:00:35.230 --> 00:00:38.190 the Damaged and Die states for enemy AI 6 00:00:38.190 --> 00:00:40.133 and use a navigation system 7 00:00:40.133 --> 00:00:42.760 to avoid obstacles 8 00:00:42.760 --> 00:00:44.688 and move towards the destination 9 00:00:45.263 --> 00:00:49.045 Implementing the damaged and die state functions 10 00:00:49.055 --> 00:00:51.302 Damaged and Die states 11 00:00:51.302 --> 00:00:54.692 This time, let's implement 12 00:00:54.692 --> 00:00:57.120 the Damaged and Die states 13 00:01:01.982 --> 00:01:05.075 First, let's create the Damaged state 14 00:01:08.234 --> 00:01:12.795 he player will attack the enemy 15 00:01:12.795 --> 00:01:15.673 with the left mouse button 16 00:01:15.673 --> 00:01:20.016 Then, you'll be able to see 17 00:01:20.016 --> 00:01:21.547 the debris effect like this 18 00:01:23.359 --> 00:01:24.676 If the enemy 19 00:01:24.676 --> 00:01:27.296 is chasing the player 20 00:01:27.296 --> 00:01:29.818 and when it takes damage 21 00:01:29.818 --> 00:01:33.073 the enemy will briefly stop 22 00:01:33.073 --> 00:01:36.639 then wait for a certain amount of time 23 00:01:36.639 --> 00:01:40.064 before deciding whether to continue chasing the player 24 00:01:40.064 --> 00:01:43.117 stay still, 25 00:01:43.117 --> 00:01:46.640 or attack, 26 00:01:46.640 --> 00:01:49.234 and then change its state accordingly 27 00:01:51.798 --> 00:01:55.691 The player has attacked the enemy 28 00:01:55.691 --> 00:01:59.960 Then, regardless of the enemy's current state 29 00:01:59.960 --> 00:02:05.943 it will briefly stop and then decide 30 00:02:05.943 --> 00:02:08.539 whether to attack, move, or idle 31 00:02:08.539 --> 00:02:11.919 based on the distance to the player 32 00:02:11.919 --> 00:02:14.487 After the Damaged state ends, 33 00:02:14.487 --> 00:02:18.412 It will decide whether to 34 00:02:18.412 --> 00:02:26.179 transition to the Attack, Move, or Idle state 35 00:02:26.179 --> 00:02:29.894 In the EnemyFSM script, 36 00:02:29.894 --> 00:02:32.868 you will create a new function below the AttackDelay 37 00:02:36.419 --> 00:02:39.831 Since the function will be called 38 00:02:39.831 --> 00:02:43.739 from a different class and not within EnemyFSM, 39 00:02:43.739 --> 00:02:48.105 add the public keyword in front of the function 40 00:02:49.382 --> 00:02:53.688 Next, in the void HitEnemy function, 41 00:03:00.739 --> 00:03:06.434 it will handle transitioning 42 00:03:06.434 --> 00:03:10.795 to the damaged state 43 00:03:10.795 --> 00:03:15.237 Later, when implementing the Die state, 44 00:03:15.237 --> 00:03:17.809 the HP concept will be involved 45 00:03:17.809 --> 00:03:20.934 At that time, the code 46 00:03:20.934 --> 00:03:23.156 in the HitEnemy function will be expanded 47 00:03:25.219 --> 00:03:29.157 For now, let's just transition to the damaged state 48 00:03:31.009 --> 00:03:34.125 ChangeState 49 00:03:34.125 --> 00:03:36.669 It transitions to the Damaged state 50 00:03:36.669 --> 00:03:40.176 within EnemyState 51 00:03:42.820 --> 00:03:46.049 Next, let's create a function that will be called 52 00:03:46.049 --> 00:03:51.048 continuously when in the Damaged state 53 00:03:51.048 --> 00:03:54.727 under, void Damaged 54 00:03:59.580 --> 00:04:03.787 This will make the time flow 55 00:04:07.100 --> 00:04:16.487 If the time exceeds the damage delay time, 56 00:04:19.500 --> 00:04:25.676 calculate the distance to the player 57 00:04:27.700 --> 00:04:38.059 If the distance is smaller than the attack range, 58 00:04:38.059 --> 00:04:42.859 transition to the Attack state 59 00:04:42.859 --> 00:04:53.618 If not, and the distance is smaller than the detection range, 60 00:04:56.201 --> 00:05:00.841 transition to the Move state 61 00:05:00.841 --> 00:05:01.839 Otherwise, 62 00:05:06.810 --> 00:05:10.647 transition to the Idle state 63 00:05:12.899 --> 00:05:15.237 The comment we wrote 64 00:05:17.560 --> 00:05:21.546 is exactly the same as the content in the AttackDelay function 65 00:05:23.499 --> 00:05:26.585 The difference is 66 00:05:26.585 --> 00:05:32.860 whether the waiting time is the attack delay time 67 00:05:32.860 --> 00:05:34.645 or the damage delay time, 68 00:05:34.645 --> 00:05:37.678 that's all the difference there is 69 00:05:40.985 --> 00:05:43.953 create a function that takes 70 00:05:43.953 --> 00:05:46.758 the waiting time as a parameter 71 00:05:46.758 --> 00:05:50.179 to handle this part 72 00:05:50.179 --> 00:05:52.307 Below the Damaged function, 73 00:05:52.307 --> 00:05:58.019 we will create a function 74 00:05:58.019 --> 00:06:00.527 named void DecideState 75 00:06:00.527 --> 00:06:03.328 add a parameter 76 00:06:05.619 --> 00:06:11.715 named float delayTime 77 00:06:14.820 --> 00:06:19.920 Then, cut all the code 78 00:06:19.920 --> 00:06:23.228 from AttackDelay 79 00:06:27.010 --> 00:06:30.699 and move it to the DecideState function 80 00:06:30.699 --> 00:06:32.570 Paste 81 00:06:32.570 --> 00:06:37.954 And we will replace the AttackDelay variable 82 00:06:37.954 --> 00:06:39.645 with the delayTime parameter 83 00:06:39.645 --> 00:06:42.158 that is passed in 84 00:06:46.030 --> 00:06:48.005 Let's also modify the comments 85 00:06:48.005 --> 00:06:51.227 if the time 86 00:06:51.227 --> 00:06:55.617 becomes greater than the waiting time, we will modify 87 00:06:58.019 --> 00:07:01.874 Once the writing is complete, go to the AttackDelay function 88 00:07:01.874 --> 00:07:06.878 and execute the DecideState function you just created 89 00:07:06.878 --> 00:07:11.429 DecideState 90 00:07:11.429 --> 00:07:15.185 Open the parentheses and pass 91 00:07:15.185 --> 00:07:18.049 the desired wait time as an argument 92 00:07:18.049 --> 00:07:24.497 This should wait for the AttackDelay time 93 00:07:26.349 --> 00:07:29.186 And the Damaged function should 94 00:07:31.260 --> 00:07:33.427 also call DecideState 95 00:07:36.000 --> 00:07:40.306 For the parameter, we should pass 96 00:07:40.306 --> 00:07:43.700 the time for how long the enemy 97 00:07:43.700 --> 00:07:45.968 should pause when taking damage 98 00:07:45.968 --> 00:07:49.857 Since the variable hasn't been created yet, 99 00:07:49.857 --> 00:07:53.459 let's go up and create a variable for it 100 00:07:53.459 --> 00:07:58.079 The damage delay time 101 00:07:58.079 --> 00:08:00.106 public float 102 00:08:02.290 --> 00:08:08.037 The damage delay time is set to 1.5 seconds 103 00:08:10.769 --> 00:08:14.967 Then, scroll down and pass 104 00:08:14.967 --> 00:08:18.769 the damageDelay variable 105 00:08:18.769 --> 00:08:24.287 inside the parentheses of DecideState 106 00:08:26.420 --> 00:08:31.381 Then, the following content will be executed all at once 107 00:08:31.381 --> 00:08:34.477 If we don't create and use 108 00:08:34.477 --> 00:08:38.758 the DecideState function as we did, 109 00:08:38.758 --> 00:08:40.777 we would have to write 110 00:08:42.859 --> 00:08:45.858 such long code under 111 00:08:45.858 --> 00:08:50.728 both the Damaged function 112 00:08:50.728 --> 00:08:55.088 and inside the AttackDelay function 113 00:08:57.201 --> 00:09:02.747 Then, the same code will be duplicated like this 114 00:09:05.140 --> 00:09:07.670 As this increases, 115 00:09:07.670 --> 00:09:10.209 it will become very difficult to manage later on 116 00:09:12.810 --> 00:09:15.570 If this same code is in 10 places, 117 00:09:15.570 --> 00:09:18.079 we would have to change 118 00:09:18.079 --> 00:09:22.409 all 10 places just to modify one 119 00:09:22.409 --> 00:09:27.948 But if you implement it 120 00:09:27.948 --> 00:09:30.694 as a function like this, 121 00:09:30.694 --> 00:09:35.228 by only changing the function, the parts that use this function 122 00:09:35.228 --> 00:09:38.797 will automatically apply the updated content 123 00:09:41.140 --> 00:09:45.227 So, when you're coding and 124 00:09:45.227 --> 00:09:48.659 you notice that the same functionality 125 00:09:48.659 --> 00:09:52.718 is repeated in multiple places 126 00:09:52.718 --> 00:09:57.027 I hope you'll practice creating functions 127 00:09:57.027 --> 00:09:59.444 and calling them instead 128 00:10:01.900 --> 00:10:04.605 Now, we need to continuously 129 00:10:04.605 --> 00:10:06.586 call the Damaged function 130 00:10:08.900 --> 00:10:14.287 Go up and under UpdateState, 131 00:10:16.336 --> 00:10:22.148 execute the Damaged function when in the damaged state 132 00:10:24.780 --> 00:10:32.580 Now, when the player attacks the enemy with the left mouse button, 133 00:10:32.580 --> 00:10:36.284 the hitEnemy function will be executed 134 00:10:36.284 --> 00:10:40.939 to transition to the damaged state 135 00:10:44.067 --> 00:10:48.016 Go to the PlayerFire script, 136 00:10:48.016 --> 00:10:53.449 and in the FireRay function, if the collided game object 137 00:10:53.449 --> 00:10:56.746 is the Enemy game object, 138 00:10:56.746 --> 00:10:59.589 get the EnemyFSM script 139 00:10:59.589 --> 00:11:03.228 from that game object 140 00:11:03.228 --> 00:11:07.720 and then execute the hitEnemy function 141 00:11:07.720 --> 00:11:08.829 that we just implemented 142 00:11:11.319 --> 00:11:16.476 we will add the code below line 4 here 143 00:11:16.476 --> 00:11:25.741 If the collided object is Enemy, 144 00:11:31.820 --> 00:11:38.717 let's get the EnemyFSM component 145 00:11:41.460 --> 00:11:44.237 Execute the hitEnemy function 146 00:11:47.488 --> 00:11:53.639 from the retrieved component 147 00:11:55.739 --> 00:11:56.957 if not, 148 00:12:00.539 --> 00:12:03.588 Then, execute the existing functionality 149 00:12:03.588 --> 00:12:04.479 this is how it works 150 00:12:06.380 --> 00:12:08.931 If hitInfo, 151 00:12:10.446 --> 00:12:13.689 The collision information is stored here 152 00:12:13.689 --> 00:12:18.895 hitInfo, get the name 153 00:12:20.598 --> 00:12:22.906 from the collided object's transform 154 00:12:22.906 --> 00:12:25.856 Here, the name of the 155 00:12:25.856 --> 00:12:26.939 collided game object is stored 156 00:12:28.840 --> 00:12:34.519 You can compare the name 157 00:12:34.519 --> 00:12:36.553 using the Contains function 158 00:12:36.553 --> 00:12:41.428 It asks, 159 00:12:41.428 --> 00:12:45.849 Does the name contain Enemy? 160 00:12:45.849 --> 00:12:50.349 If the name of the game object 161 00:12:50.349 --> 00:12:52.441 contains Enemy 162 00:12:52.441 --> 00:12:54.047 it returns true 163 00:12:54.047 --> 00:12:57.057 otherwise, it returns false 164 00:12:58.859 --> 00:13:03.119 The enemy we implemented 165 00:13:03.119 --> 00:13:05.328 has the name Enemy 166 00:13:07.477 --> 00:13:10.163 If the name is Enemy100 167 00:13:10.163 --> 00:13:14.505 it will still return true because the name contains Enemy 168 00:13:16.739 --> 00:13:19.623 This code will return true 169 00:13:21.464 --> 00:13:23.646 Change the name back to Enemy 170 00:13:25.399 --> 00:13:28.012 If the collided object is checked as Enemy 171 00:13:28.012 --> 00:13:31.380 then these two functionalities should be 172 00:13:31.380 --> 00:13:33.837 implemented within these parentheses 173 00:13:36.419 --> 00:13:40.106 Since we're getting the EnemyFSM component, 174 00:13:40.106 --> 00:13:45.219 we will create a variable to store the retrieved component 175 00:13:45.219 --> 00:13:53.138 I will create it with the name EnemyFSM 176 00:13:53.138 --> 00:13:58.035 Then, hitInfo, 177 00:13:58.035 --> 00:14:01.131 I will get the FSM component 178 00:14:01.131 --> 00:14:03.460 through your transform 179 00:14:03.460 --> 00:14:07.968 we retrieve it through the component function 180 00:14:07.968 --> 00:14:10.494 which component to get is, 181 00:14:10.494 --> 00:14:16.140 write that we are getting 182 00:14:16.140 --> 00:14:18.684 the EnemyFSM component 183 00:14:18.684 --> 00:14:20.690 If the collided object 184 00:14:20.690 --> 00:14:23.568 contains EnemyFSM, 185 00:14:23.568 --> 00:14:26.889 store it in the FSM variable 186 00:14:29.020 --> 00:14:31.815 Next, hey FSM, 187 00:14:31.815 --> 00:14:33.629 this would be the component we assume 188 00:14:33.629 --> 00:14:35.868 Execute the hitEnemy function 189 00:14:35.868 --> 00:14:40.118 from the functions you have 190 00:14:41.980 --> 00:14:44.778 and then, else, if not, 191 00:14:48.739 --> 00:14:51.545 Place the existing code 192 00:14:51.545 --> 00:14:53.996 inside these parentheses 193 00:14:55.940 --> 00:14:59.082 Save it and then return to the editor 194 00:14:59.082 --> 00:15:02.678 to run it 195 00:15:02.678 --> 00:15:05.795 Activate the console window 196 00:15:08.059 --> 00:15:11.029 After aiming like this, 197 00:15:11.029 --> 00:15:14.055 when I press the left mouse button, 198 00:15:14.055 --> 00:15:16.956 In the console that it transitions from Idle to Damage, 199 00:15:16.956 --> 00:15:18.515 waits briefly, 200 00:15:18.515 --> 00:15:23.383 and then changes from Damage back to Idle 201 00:15:23.383 --> 00:15:27.009 Then, when it chases and you shoot, 202 00:15:27.009 --> 00:15:29.978 you'll see it briefly stop and then 203 00:15:29.978 --> 00:15:31.631 transition back to Idle 204 00:15:31.631 --> 00:15:36.038 This time, I'll make it keep moving 205 00:15:36.038 --> 00:15:37.676 while shooting 206 00:15:37.676 --> 00:15:40.226 Then, you'll see it briefly stop, 207 00:15:40.226 --> 00:15:42.870 move again, stop, 208 00:15:42.870 --> 00:15:45.467 and move again, all functioning properly 209 00:15:48.780 --> 00:15:52.022 We are currently checking 210 00:15:52.022 --> 00:15:55.564 the name of the game object 211 00:15:55.564 --> 00:15:59.619 to determine if the collided object is the Enemy 212 00:15:59.619 --> 00:16:03.369 Let's try doing this with layers instead 213 00:16:03.369 --> 00:16:05.892 Select the Enemy game object, 214 00:16:05.892 --> 00:16:09.110 then in the Inspector, 215 00:16:09.110 --> 00:16:11.281 under the Layer section, 216 00:16:11.281 --> 00:16:13.759 change it from Default to Add Layer 217 00:16:13.759 --> 00:16:16.186 by choosing the dropdown menu 218 00:16:18.206 --> 00:16:23.816 Next, write Enemy in Layer 6, 219 00:16:25.380 --> 00:16:28.832 then select the Enemy game object again, 220 00:16:28.832 --> 00:16:31.038 choose Default, and then 221 00:16:32.820 --> 00:16:34.830 select the Enemy Layer 222 00:16:34.830 --> 00:16:37.217 we just added 223 00:16:40.386 --> 00:16:42.394 Now, let's check 224 00:16:42.394 --> 00:16:44.502 using the layer value 225 00:16:44.502 --> 00:16:45.848 instead of the game object's name 226 00:16:48.520 --> 00:16:52.091 Return to the PlayerFire script, 227 00:16:52.091 --> 00:16:55.980 and if the collided object is Enemy, 228 00:16:55.980 --> 00:16:58.671 it is currently being searched by its name 229 00:16:58.671 --> 00:17:02.349 Instead of this, ask the transform of the collided object, 230 00:17:02.349 --> 00:17:05.699 What is your game object? 231 00:17:05.699 --> 00:17:08.983 It will ask if the layer value 232 00:17:08.983 --> 00:17:12.999 of the collided game object is the same 233 00:17:12.999 --> 00:17:15.068 what is it the same with? 234 00:17:15.068 --> 00:17:19.050 There is a struct called LayerMask 235 00:17:19.050 --> 00:17:24.219 It has a function called NameToLayer 236 00:17:24.219 --> 00:17:25.917 Open the parentheses, and inside, 237 00:17:28.020 --> 00:17:31.583 enter the name of the layer we added 238 00:17:31.583 --> 00:17:33.739 It's Enemy, and currently, there is only one 239 00:17:33.739 --> 00:17:36.495 It checks if it matches the Enemy Layer 240 00:17:38.099 --> 00:17:40.738 We set the Enemy game object's 241 00:17:40.738 --> 00:17:42.908 layer to Enemy only 242 00:17:42.908 --> 00:17:48.348 That's why only the Enemy game object will 243 00:17:48.348 --> 00:17:49.557 have the Enemy Layer 244 00:17:51.260 --> 00:17:54.288 You can also identify specific objects 245 00:17:54.288 --> 00:17:57.276 through layers like this 246 00:18:00.619 --> 00:18:05.269 This time, we will implement the Die state 247 00:18:05.269 --> 00:18:11.820 In the EnemyFSM script, you need to distinguish 248 00:18:11.820 --> 00:18:14.830 whether the HitEnemy function 249 00:18:14.830 --> 00:18:19.859 will transition to the damaged state 250 00:18:19.859 --> 00:18:22.205 or the Die state 251 00:18:22.205 --> 00:18:25.798 To do this, the Enemy 252 00:18:25.798 --> 00:18:29.188 must have an HP value 253 00:18:31.749 --> 00:18:34.755 Let's create global variables related to HP 254 00:18:36.540 --> 00:18:40.748 We will create two variables 255 00:18:43.260 --> 00:18:47.450 one for the maximum HP 256 00:18:47.450 --> 00:18:49.866 and one for the current HP 257 00:18:52.339 --> 00:18:55.166 Write public float 258 00:18:57.037 --> 00:19:02.175 and set maxHP as 100 259 00:19:04.140 --> 00:19:06.583 Next, we will set the current HP 260 00:19:06.583 --> 00:19:13.777 as public float currHP equals 0 261 00:19:16.260 --> 00:19:20.667 At the start, let's set the current HP 262 00:19:23.380 --> 00:19:28.939 to the maximum HP 263 00:19:28.939 --> 00:19:32.965 By doing this, when the game starts 264 00:19:32.965 --> 00:19:35.566 the current HP will be 265 00:19:35.566 --> 00:19:36.607 set to 100 266 00:19:38.418 --> 00:19:42.229 currtHP equals to maxHP 267 00:19:46.219 --> 00:19:48.967 Come down to HitEnemy function 268 00:19:51.020 --> 00:19:57.180 Now, the HitEnemy function will take 269 00:19:57.180 --> 00:20:00.730 a parameter that specifies how much HP 270 00:20:00.730 --> 00:20:03.323 should be reduced 271 00:20:03.323 --> 00:20:06.389 So, let's create 272 00:20:06.389 --> 00:20:13.300 a float parameter named HitPower 273 00:20:13.300 --> 00:20:17.080 Since the HitEnemy function has been changed, 274 00:20:17.080 --> 00:20:19.525 you will see an error like this 275 00:20:19.525 --> 00:20:22.900 in the PlayerFire script 276 00:20:22.900 --> 00:20:24.207 why so? 277 00:20:24.207 --> 00:20:27.527 The HitEnemy function requires a float parameter, 278 00:20:27.527 --> 00:20:29.068 which is why this error occurs 279 00:20:33.140 --> 00:20:36.847 So, you will create a global variable 280 00:20:36.847 --> 00:20:41.969 for the weapon damage in the PlayerFire script 281 00:20:41.969 --> 00:20:46.349 You will define it as 282 00:20:46.349 --> 00:20:50.758 public float weaponDamage 283 00:20:53.699 --> 00:20:55.684 let's set it as 20 284 00:20:57.278 --> 00:21:00.808 And when executing 285 00:21:00.808 --> 00:21:06.260 the HitEnemy function in the Raycast, 286 00:21:06.260 --> 00:21:09.575 pass the weaponDamage value as a parameter 287 00:21:12.699 --> 00:21:16.234 Returning to EnemyFSM, 288 00:21:16.234 --> 00:21:19.454 when the HitEnemy function is called, 289 00:21:19.454 --> 00:21:22.789 the value 20 will be passed 290 00:21:22.789 --> 00:21:26.672 After reducing the HP by that value, 291 00:21:26.672 --> 00:21:33.062 if the HP is greater than 0 292 00:21:33.062 --> 00:21:36.572 it will transition to the Damaged state 293 00:21:36.572 --> 00:21:39.939 If the HP is less than 294 00:21:39.939 --> 00:21:43.339 or equal to 0, it will transition 295 00:21:43.339 --> 00:21:46.719 to the Die state 296 00:21:46.719 --> 00:21:54.748 Reduce the current HP by the hitPower value 297 00:21:57.878 --> 00:22:01.762 If the current HP is 298 00:22:05.653 --> 00:22:11.615 greater than 0, 299 00:22:11.615 --> 00:22:13.835 transition to the Damaged state 300 00:22:16.500 --> 00:22:17.577 if not, 301 00:22:21.260 --> 00:22:25.435 Transition to the die state 302 00:22:25.435 --> 00:22:28.926 If not, it means the current HP is 303 00:22:31.020 --> 00:22:35.036 less than or equal to 0 304 00:22:37.619 --> 00:22:39.278 The current HP is stored 305 00:22:39.278 --> 00:22:42.860 in the CurrHP variable 306 00:22:42.860 --> 00:22:46.819 so CurrHP minus equals 307 00:22:46.819 --> 00:22:49.817 Reduce the HP by the hitPower value 308 00:22:51.660 --> 00:22:52.945 Now, we need to ask 309 00:22:52.945 --> 00:23:00.034 If CurrHP is greater than 0, 310 00:23:00.034 --> 00:23:07.586 transition to the Damaged state 311 00:23:07.586 --> 00:23:09.825 If not, else 312 00:23:11.819 --> 00:23:15.286 Transition to the Die state 313 00:23:15.286 --> 00:23:18.180 using ChangeState 314 00:23:18.180 --> 00:23:20.788 EnemyState and Die 315 00:23:24.412 --> 00:23:27.561 To see the value of the HP reduction, 316 00:23:27.561 --> 00:23:30.540 let's output it to the console 317 00:23:30.540 --> 00:23:31.735 print 318 00:23:35.648 --> 00:23:43.018 The current HP value is CurrHP 319 00:23:48.542 --> 00:23:51.589 Let's play this 320 00:23:51.589 --> 00:23:56.340 Mouse left click to fire 321 00:23:56.340 --> 00:23:58.232 Current HP is 80, right? 322 00:23:58.232 --> 00:24:05.569 As we fire additionally, 60, 40 ,20 323 00:24:05.569 --> 00:24:08.041 This time if I shoot again, 324 00:24:08.041 --> 00:24:10.084 It should transition to the Die state 325 00:24:10.084 --> 00:24:13.118 instead of the Damaged state 326 00:24:13.118 --> 00:24:15.070 if I shoot it 327 00:24:15.070 --> 00:24:18.448 Like this, it transitions to Die state 328 00:24:20.540 --> 00:24:23.387 let's add in a code that makes it 329 00:24:23.387 --> 00:24:27.916 destroyed after 1 second in Die state 330 00:24:29.619 --> 00:24:33.269 After going to ChangeState, 331 00:24:33.269 --> 00:24:35.109 add another case statement 332 00:24:35.109 --> 00:24:36.920 in the switch case statement 333 00:24:36.920 --> 00:24:40.189 case EnemyState and Die 334 00:24:45.219 --> 00:24:46.913 and write Destroy 335 00:24:46.913 --> 00:24:49.292 Destroy myself 336 00:24:49.292 --> 00:24:53.510 So, let's use the lowercase version of the game object 337 00:24:53.510 --> 00:24:55.420 and add a semicolon 338 00:24:55.420 --> 00:24:59.430 I'll have it destroyed it after 1 second 339 00:24:59.430 --> 00:25:00.747 So, 1 340 00:25:03.459 --> 00:25:07.182 Save it and then return to the editor 341 00:25:07.182 --> 00:25:09.580 to run the game 342 00:25:09.580 --> 00:25:14.790 After hitting five times, the enemy will die 343 00:25:14.790 --> 00:25:16.177 five 344 00:25:16.177 --> 00:25:18.979 we can see it gets destroyed after one second, right? 345 00:25:18.979 --> 00:25:23.330 But there is one more issue here 346 00:25:23.330 --> 00:25:28.583 We will shoot five times 347 00:25:28.583 --> 00:25:32.430 Now, I'll shoot a bit faster 348 00:25:32.430 --> 00:25:34.848 pewpewpewpew 349 00:25:34.848 --> 00:25:37.364 It does disappear 350 00:25:37.364 --> 00:25:41.040 Transition Idle state to Die state 351 00:25:41.040 --> 00:25:43.807 because it disappears after 1 second, 352 00:25:43.807 --> 00:25:48.094 It's transitioning from Die to Die, and from Die to Die 353 00:25:48.094 --> 00:25:49.172 repeatedly, right? 354 00:25:49.172 --> 00:25:51.900 And the current HP is 355 00:25:51.900 --> 00:25:55.168 continuously decreasing by the negative value as well 356 00:25:57.366 --> 00:26:00.763 So, this time, if the enemy is in the Die state, 357 00:26:00.763 --> 00:26:05.856 we will prevent any more damage from being applied 358 00:26:09.540 --> 00:26:13.016 Next, go to the EnemyFSM script, 359 00:26:13.016 --> 00:26:17.808 and in the hitEnemy function, you will handle the exception 360 00:26:21.540 --> 00:26:25.718 If the current state is Die, 361 00:26:28.380 --> 00:26:34.629 let's exit the function 362 00:26:37.579 --> 00:26:42.619 let's exit the function 363 00:26:42.619 --> 00:26:44.803 What is it same to? 364 00:26:44.803 --> 00:26:47.808 If it's the same as EnemyState's Die, 365 00:26:50.219 --> 00:26:55.938 I will end the function using the return statement 366 00:26:58.660 --> 00:27:00.754 When the return type is void, 367 00:27:00.754 --> 00:27:03.510 the return statement can be omitted 368 00:27:03.510 --> 00:27:05.648 but it can still be used 369 00:27:05.648 --> 00:27:07.157 So, when used, 370 00:27:07.157 --> 00:27:11.845 it will end the function without 371 00:27:11.845 --> 00:27:14.300 executing the code below 372 00:27:14.300 --> 00:27:16.917 Then, if the state is Die, we wouldn't perform 373 00:27:16.917 --> 00:27:20.859 the operation of reducing the current HP 374 00:27:20.859 --> 00:27:24.576 Based on that, we wouldn't execute 375 00:27:24.576 --> 00:27:27.185 the transition to the Damaged state 376 00:27:27.185 --> 00:27:29.660 or the Die state 377 00:27:29.660 --> 00:27:35.047 Save it and let's quickly test the damage 378 00:27:37.300 --> 00:27:39.363 Now, 379 00:27:39.363 --> 00:27:41.783 we see that the transition 380 00:27:41.783 --> 00:27:44.397 to Die state is called only once 381 00:27:46.770 --> 00:27:51.810 Pathfinding 382 00:27:51.810 --> 00:27:55.468 This time, I let's learn about 383 00:27:55.468 --> 00:27:57.106 pathfinding navigation systems 384 00:27:59.440 --> 00:28:09.557 I will place the enemy behind this wall 385 00:28:11.899 --> 00:28:15.431 When the player approaches the wall, 386 00:28:15.431 --> 00:28:22.014 the enemy should avoid the wall and come around it 387 00:28:22.014 --> 00:28:23.691 and that seems normal 388 00:28:23.691 --> 00:28:29.037 but right now, it's going straight through the wall 389 00:28:29.037 --> 00:28:30.265 Let's go ahead and check 390 00:28:32.979 --> 00:28:34.858 As shown, it goes through the wall 391 00:28:37.180 --> 00:28:43.540 If the enemy were a ghost, it would be fine movement 392 00:28:43.540 --> 00:28:47.171 If that's not the case, 393 00:28:47.171 --> 00:28:50.979 it should avoid obstacles 394 00:28:50.979 --> 00:28:54.993 In Unity, they provide 395 00:28:54.993 --> 00:28:57.580 a navigation system package 396 00:28:57.580 --> 00:28:59.838 that can perform this functionality 397 00:29:02.020 --> 00:29:05.170 Let's give it a try and apply it! 398 00:29:05.170 --> 00:29:10.219 First, we need to add 399 00:29:10.219 --> 00:29:12.633 the navigation system package 400 00:29:12.633 --> 00:29:15.940 Select the Package Manager menu 401 00:29:15.940 --> 00:29:20.245 under the Window tab in the editor's top menu 402 00:29:20.245 --> 00:29:25.639 If you click the drop down just to the right of the plus button 403 00:29:25.639 --> 00:29:30.491 under the Package Manager, you'll see several options 404 00:29:30.491 --> 00:29:35.928 Select Unity Registry from the options here 405 00:29:35.928 --> 00:29:42.804 If you type NAVI in the search bar at the top right, 406 00:29:42.804 --> 00:29:46.739 the AI Navigation package will appear 407 00:29:46.739 --> 00:29:50.226 Select the package and then click Install 408 00:29:53.008 --> 00:29:58.003 Now, using this navigation system, 409 00:29:58.003 --> 00:30:01.626 we will divide the terrain into areas 410 00:30:01.626 --> 00:30:04.659 that are accessible and areas that are not 411 00:30:06.979 --> 00:30:11.360 The terrain in this case refers to 412 00:30:11.360 --> 00:30:16.058 the Wall game object and the Floor game object 413 00:30:16.058 --> 00:30:18.960 So, click the plus button in the Hierarchy window 414 00:30:18.960 --> 00:30:24.219 and add an Create Empty game object 415 00:30:24.219 --> 00:30:28.409 and name it Env 416 00:30:28.409 --> 00:30:32.540 Next, set the position to 0, 0, 0 417 00:30:32.540 --> 00:30:35.081 Right click on the Transform name 418 00:30:35.081 --> 00:30:37.609 and select the Reset menu, 419 00:30:37.609 --> 00:30:39.997 and it will initialize like this 420 00:30:41.939 --> 00:30:46.973 Then, select the Ground and Wall, 421 00:30:46.973 --> 00:30:51.620 and place them under the Env game object 422 00:30:51.620 --> 00:30:53.745 as child objects 423 00:30:56.380 --> 00:31:00.383 Next, we'll add a component 424 00:31:00.383 --> 00:31:03.159 to the Env game object 425 00:31:05.619 --> 00:31:09.515 In the Inspector, click the Add Component button, 426 00:31:09.515 --> 00:31:12.589 then search for Nav 427 00:31:12.589 --> 00:31:18.076 and add the NavMeshSurface component 428 00:31:20.540 --> 00:31:23.364 Next, click the Bake 429 00:31:23.364 --> 00:31:25.276 button at the bottom 430 00:31:27.060 --> 00:31:30.174 Then, the areas that are 431 00:31:30.174 --> 00:31:34.109 highlighted in blue will appear like this 432 00:31:34.109 --> 00:31:36.347 The blue areas represent the regions 433 00:31:36.347 --> 00:31:38.316 that can be navigated 434 00:31:38.316 --> 00:31:42.140 using this navigation system 435 00:31:42.140 --> 00:31:47.979 And the areas that show their original color are 436 00:31:47.979 --> 00:31:52.956 marked as inaccessible regions 437 00:31:52.956 --> 00:31:56.245 There is also an inaccessible area 438 00:31:56.245 --> 00:31:59.649 above the Wall 439 00:31:59.649 --> 00:32:03.949 By setting it up like this, 440 00:32:03.949 --> 00:32:08.109 the areas where the player and enemy are 441 00:32:08.109 --> 00:32:13.579 located will also be marked as inaccessible 442 00:32:13.579 --> 00:32:15.638 If the enemy moves like this, 443 00:32:15.638 --> 00:32:19.470 and there’s nothing in the way but they still can’t pass through, 444 00:32:19.470 --> 00:32:20.707 that wouldn’t be right 445 00:32:24.044 --> 00:32:30.140 So, in the NavMeshSurface we just added, 446 00:32:30.140 --> 00:32:33.976 expand Object Collection menu 447 00:32:33.976 --> 00:32:37.802 and since the collect object here is 448 00:32:37.802 --> 00:32:40.939 set to all game object, 449 00:32:40.939 --> 00:32:43.550 this happens 450 00:32:43.550 --> 00:32:48.339 Select All GameObjects, then open the dropdown 451 00:32:48.339 --> 00:32:51.347 and choose the Current Object Hierarchy menu 452 00:32:51.347 --> 00:32:54.196 After that, click Bake again 453 00:32:55.939 --> 00:33:02.690 Then, only the Wall and Ground game objects 454 00:33:02.690 --> 00:33:05.739 will have their areas set 455 00:33:05.739 --> 00:33:08.971 This option is for setting the area 456 00:33:08.971 --> 00:33:12.217 only for game objects 457 00:33:12.217 --> 00:33:14.678 that have children 458 00:33:17.300 --> 00:33:22.538 Now, select the Enemy game object 459 00:33:22.538 --> 00:33:25.380 The game object that needs to find 460 00:33:25.380 --> 00:33:28.524 the path and move is the enemy 461 00:33:28.524 --> 00:33:34.380 The player is controlled by us, we don't need the game to find a path 462 00:33:34.380 --> 00:33:37.797 In the Inspector, click the Add Component button, 463 00:33:39.718 --> 00:33:44.838 and this time, add the NavMeshAgent component 464 00:33:47.300 --> 00:33:51.029 Through the NavMeshAgent component, 465 00:33:51.029 --> 00:33:56.589 when you set a target position within the blue areas, 466 00:33:56.589 --> 00:34:00.215 the enemy will move to that location 467 00:34:00.215 --> 00:34:02.259 However, if there is an obstacle in front, 468 00:34:02.259 --> 00:34:06.976 the enemy will navigate around it to reach the destination 469 00:34:09.120 --> 00:34:12.675 Now, go to the EnemyFSM script 470 00:34:12.675 --> 00:34:19.210 and create a variable to hold the NavMeshAgent component 471 00:34:19.210 --> 00:34:25.528 The variable for the navigation agent 472 00:34:27.780 --> 00:34:37.820 I'll name the NavMeshAgent variable as agent 473 00:34:37.820 --> 00:34:42.154 To use the NavMeshAgent class, 474 00:34:42.154 --> 00:34:45.260 you need to add the line 475 00:34:45.260 --> 00:34:51.563 using UnityEngine AI, at the top 476 00:34:51.563 --> 00:34:53.686 It usually gets added automatically, 477 00:34:53.686 --> 00:34:55.550 but sometimes it might not be 478 00:34:55.550 --> 00:35:00.884 If you see a red line under NavMeshAgent, 479 00:35:00.884 --> 00:35:05.327 make sure to add that line of code manually 480 00:35:05.327 --> 00:35:15.399 At the start, we will get the NavMeshAgent 481 00:35:20.660 --> 00:35:24.482 by setting, agent equals to, to retrieve it 482 00:35:24.482 --> 00:35:32.107 Use GetComponent and NavMeshAgent inside parenthesis 483 00:35:35.379 --> 00:35:38.725 Since the enemy needs to move, 484 00:35:38.725 --> 00:35:41.260 let's go to the Move function 485 00:35:41.260 --> 00:35:46.689 At this point, we made the enemy move towards the player 486 00:35:46.689 --> 00:35:50.753 Instead of calculating the direction and moving in that direction, 487 00:35:50.753 --> 00:35:53.915 we will command the enemy through 488 00:35:53.915 --> 00:35:57.738 the agent to go to the player's position 489 00:35:59.780 --> 00:36:02.215 So, we will comment out the code for calculating 490 00:36:02.215 --> 00:36:04.668 the direction towards the player 491 00:36:04.668 --> 00:36:07.927 and moving in that direction 492 00:36:07.927 --> 00:36:17.303 Let's tell the agent the destination 493 00:36:17.303 --> 00:36:21.699 The destination here would be the player's position 494 00:36:21.699 --> 00:36:25.741 So, the agent has a function called 495 00:36:25.741 --> 00:36:30.397 SetDestination among its features 496 00:36:30.397 --> 00:36:33.957 Open the parentheses, and inside, 497 00:36:33.957 --> 00:36:36.449 we can insert the position value of the destination 498 00:36:36.449 --> 00:36:41.676 So, we will write the code 499 00:36:41.676 --> 00:36:44.627 so that the player gets the position value 500 00:36:48.013 --> 00:36:50.426 Let's go ahead and save it, then try playing the game 501 00:36:52.340 --> 00:36:54.511 If the player gets closer 502 00:36:54.511 --> 00:36:56.898 unlike before when it went through the wall, 503 00:36:56.898 --> 00:37:00.959 this time the enemy is avoiding the wall 504 00:37:05.501 --> 00:37:07.660 We can see that the enemy is successfully avoiding the wall 505 00:37:10.137 --> 00:37:14.313 However, when the enemy attacks, 506 00:37:14.313 --> 00:37:17.808 it seems to push the player slightly as it moves in 507 00:37:19.699 --> 00:37:21.943 The reason this happens is that 508 00:37:21.943 --> 00:37:28.899 when the enemy is in this state, it will chase the player 509 00:37:28.899 --> 00:37:31.772 We set the destination 510 00:37:31.772 --> 00:37:33.858 using the NavMeshAgent, 511 00:37:33.858 --> 00:37:37.437 and that destination is the player's position 512 00:37:39.340 --> 00:37:40.518 So, the enemy is 513 00:37:42.899 --> 00:37:46.789 trying to reach that exact position 514 00:37:46.789 --> 00:37:50.630 But since the player and enemy don't overlap, 515 00:37:50.630 --> 00:37:53.559 the enemy ends up 516 00:37:53.559 --> 00:37:58.869 gently pushing the player 517 00:37:58.869 --> 00:38:02.260 while moving towards the destination 518 00:38:02.260 --> 00:38:04.849 And then, after that, the enemy attacks 519 00:38:07.179 --> 00:38:09.356 This shouldn't happen like this 520 00:38:09.356 --> 00:38:12.928 If the enemy enters the attack range, 521 00:38:12.928 --> 00:38:15.216 even if the destination was set to the player's position, 522 00:38:15.216 --> 00:38:20.939 it should stop and perform the attack instead 523 00:38:20.939 --> 00:38:23.048 Let's move to the ChangeState function 524 00:38:25.020 --> 00:38:27.919 When the state changes, 525 00:38:27.919 --> 00:38:30.946 we will stop the agent 526 00:38:32.609 --> 00:38:35.789 Even if the enemy is heading towards the destination, 527 00:38:35.789 --> 00:38:39.226 once the state changes, it will stop moving 528 00:38:40.939 --> 00:38:44.558 Let's stop the pathfinding 529 00:38:46.820 --> 00:38:49.866 Hey agent, among the properties you have, 530 00:38:49.866 --> 00:38:53.969 there's one called isStoped 531 00:38:53.969 --> 00:38:56.538 By setting isStopped to true, 532 00:38:58.540 --> 00:39:00.219 it will stop the pathfinding 533 00:39:03.179 --> 00:39:05.906 Save the changes, return to the editor, 534 00:39:05.906 --> 00:39:10.060 and when you play, the enemy should move 535 00:39:10.060 --> 00:39:12.225 based on the distance, right? 536 00:39:12.225 --> 00:39:15.636 But it doesn't move 537 00:39:15.636 --> 00:39:18.184 It moved before, but now it doesn't 538 00:39:18.184 --> 00:39:21.884 The reason is that when it went to the Move state, 539 00:39:21.884 --> 00:39:24.933 it simply stopped the pathfinding 540 00:39:24.933 --> 00:39:28.283 Even though the destination was set, 541 00:39:28.283 --> 00:39:31.709 it’s not moving because it was 542 00:39:31.709 --> 00:39:33.107 set to stop, so it's not functioning 543 00:39:35.100 --> 00:39:38.958 So, we will change isStopped to false 544 00:39:38.958 --> 00:39:41.893 only in specific states 545 00:39:41.893 --> 00:39:42.406 When? 546 00:39:42.406 --> 00:39:44.550 When it's moving 547 00:39:44.550 --> 00:39:46.546 So, you will add another case 548 00:39:46.546 --> 00:39:49.478 to the switch statement 549 00:39:49.478 --> 00:39:54.629 case Move State 550 00:40:00.283 --> 00:40:04.569 Hey agent, among the properties you have 551 00:40:04.569 --> 00:40:08.060 change the isStoped property to what? 552 00:40:08.060 --> 00:40:10.810 to false 553 00:40:10.810 --> 00:40:14.889 Save and come back to the editor 554 00:40:17.300 --> 00:40:19.986 Go ahead and try playing again 555 00:40:19.986 --> 00:40:25.983 Now, the enemy moves, stops, and then attacks 556 00:40:25.983 --> 00:40:30.728 The enemy moves again, stops, and attacks 557 00:40:30.728 --> 00:40:31.471 repeating the cycl 558 00:40:34.699 --> 00:40:36.508 Being too far away, so it switches to the Idle state 559 00:40:36.508 --> 00:40:39.971 While getting close to the enemy, 560 00:40:39.971 --> 00:40:44.248 I'll try attacking by clicking the left mouse button 561 00:40:44.248 --> 00:40:47.145 It stops and then comes back again 562 00:40:47.145 --> 00:40:48.878 Since the enemy has transitioned to the damaged state, 563 00:40:48.878 --> 00:40:51.558 it stops, comes back, and then attacks 564 00:40:53.899 --> 00:40:57.315 Let's summarize the key points we've learned in this session 565 00:40:58.009 --> 00:40:58.888 Summary Implementing Damaged and Die state functions Damaged Transition to Damaged ChangeState EnemyState Damaged 566 00:40:58.888 --> 00:40:59.769 The void Damaged function is the same as the AttackDelay function Call the DecideState function and change the parameter attackDelay to delayTime 567 00:40:59.769 --> 00:41:00.513 If it collides with something if Physics Taycast ray, out hitInfo If the collided object is an enemy, get the EnemyFSM component 568 00:41:00.513 --> 00:41:01.336 Execute the HitEnemy function from the retrieved component if hitlnfo transform name Contains Enemy 569 00:41:01.336 --> 00:41:02.108 EnemyFMS fam equals hitlnfo transform GetComponent EnemyFSM fsm HitEnemy If the collided object is not an enemy, execute the existing code 570 00:41:02.108 --> 00:41:02.999 can also use the LayerMask.NameToLayer function to determine specific objects based on their layer 571 00:41:02.999 --> 00:41:04.352 Implementing Damaged and Die state functions UDie Set the current HP to the max HP Decrease the current HP by the hit power 572 00:41:04.352 --> 00:41:05.712 If HP is bigger than 0, call the ChangeState function with EnemyState Damaged as the parameter 573 00:41:05.712 --> 00:41:06.861 If 0 is bigger than or equals to HP, call the ChangeState function with EnemyState.Die as the parameter 574 00:41:06.861 --> 00:41:07.941 If the current state is the Die state, exit the function if currState equals to EnemyState Die 575 00:41:07.941 --> 00:41:09.069 Pathfinding Navigation System AI navigation that avoids obstacles and finds a path is provided NavMeshSurface component divding the terrains 576 00:41:09.069 --> 00:41:09.850 Enemy Object Add the NavMeshAgent component to the Enemy object Destination set by the NavMeshAgent is player position Retrieve the navigation agent 577 00:41:09.850 --> 00:41:10.791 agent equals to GetComponent NavMeshAgent Tells the agent the destination agent SetDestination player position 578 00:41:10.791 --> 00:41:11.739 Stop the pathfinding agent isStopped equals true To change the isStopped property to false only in specific states, 579 00:41:11.739 --> 00:41:12.838 To add a case in the ChangeState function using a switch statement case EnemyState.Move agent isStopped equals to false break