WEBVTT 1 00:00:28.000 --> 00:00:30.000 Hello, this is Youngho Lee 2 00:00:30.000 --> 00:00:33.000 In this lecture, we will learn about Respawn 3 00:00:33.000 --> 00:00:35.000 What you will learn is 4 00:00:35.000 --> 00:00:37.000 to implement the first respawn logic 5 00:00:37.000 --> 00:00:40.000 When the user clicks the Retry UI button 6 00:00:40.000 --> 00:00:42.700 a location for respawning is selected 7 00:00:42.700 --> 00:00:45.400 and player respawning is processed through RPC communication 8 00:00:45.400 --> 00:00:50.300 Second, you will learn about the respawn problem and lifecycle function analysis 9 00:00:50.300 --> 00:00:54.320 When respawning, problems may arise with the player's initialization settings 10 00:00:54.320 --> 00:01:00.020 This part can be solved through lifecycle function processing between the server and the client 11 00:01:00.020 --> 00:01:03.000 Let's learn how this information is processed 12 00:01:03.897 --> 00:01:06.197 Implementing Respawn Logic 13 00:01:09.000 --> 00:01:12.000 This time, we will implement the respawn logic 14 00:01:13.680 --> 00:01:17.000 The way the player is 15 00:01:17.000 --> 00:01:19.420 created now is 16 00:01:19.420 --> 00:01:21.000 when you press the play button 17 00:01:21.000 --> 00:01:25.160 if you look at this as the world screen 18 00:01:26.000 --> 00:01:27.700 the screen looks like this 19 00:01:27.700 --> 00:01:31.620 There will be various level designs here 20 00:01:31.620 --> 00:01:35.240 And this is where the player appears 21 00:01:35.240 --> 00:01:38.590 So here we have the player start 22 00:01:38.660 --> 00:01:41.000 The player is currently running 23 00:01:43.640 --> 00:01:48.060 in the area where the player start actor is located 24 00:01:49.000 --> 00:01:51.000 At this time, it's spawned 25 00:01:51.000 --> 00:01:55.380 and our team is strategically located at the point we want 26 00:01:55.380 --> 00:01:58.580 Then, if I were to create a structure like this 27 00:01:58.580 --> 00:02:00.660 so that the opposing team could come out 28 00:02:00.660 --> 00:02:02.800 players would have to be created 29 00:02:02.800 --> 00:02:06.370 exactly at this point that I specified, right? 30 00:02:06.370 --> 00:02:09.600 Or, in this part, I think we need logic to make it 31 00:02:09.600 --> 00:02:13.450 randomly created around here or in some specific area 32 00:02:13.450 --> 00:02:14.660 like this 33 00:02:15.540 --> 00:02:18.680 Right now, the player is at this point 34 00:02:18.680 --> 00:02:21.000 but I don't want this to be where the player spawns 35 00:02:21.000 --> 00:02:24.000 when he dies or when he spawns 36 00:02:24.000 --> 00:02:27.790 Or, I hope it gets to this point 37 00:02:27.790 --> 00:02:30.000 The player is created at a point that we specify 38 00:02:30.000 --> 00:02:33.000 and then when the player dies, it respawns at this point 39 00:02:33.000 --> 00:02:34.520 and we want to do something like this 40 00:02:34.520 --> 00:02:39.120 So first, there is only one player starting, so let's put the player starting 41 00:02:39.120 --> 00:02:42.000 in two locations as a sample 42 00:02:42.000 --> 00:02:44.540 So, let's think of it as a two-player game 43 00:02:44.540 --> 00:02:47.480 and by putting one here and one here 44 00:02:47.480 --> 00:02:49.800 I will work on making it possible to respawn 45 00:02:53.000 --> 00:02:58.200 I duplicate the player by pressing the Alt key again 46 00:02:59.000 --> 00:03:03.100 and then I rotate them like this so they're facing each other 47 00:03:03.900 --> 00:03:05.040 Let's rotate it 180 degrees 48 00:03:06.340 --> 00:03:09.400 and make it look like this 49 00:03:09.400 --> 00:03:12.000 We're going to do this so that some players will respawn here 50 00:03:12.000 --> 00:03:15.640 and other players will respawn here 51 00:03:17.660 --> 00:03:20.580 We will put two players in positions like this 52 00:03:21.840 --> 00:03:22.860 Okay? 53 00:03:25.720 --> 00:03:27.170 Then 54 00:03:28.160 --> 00:03:30.920 at the player start location 55 00:03:30.920 --> 00:03:34.220 it is a process of spawning when a player is created 56 00:03:34.220 --> 00:03:36.380 To do this 57 00:03:36.380 --> 00:03:41.520 you need to request that the server respawn the player for these parts 58 00:03:42.660 --> 00:03:45.440 We continue to do the same things 59 00:03:46.000 --> 00:03:51.250 but when we make these requests 60 00:03:51.750 --> 00:03:55.040 we process them using RPC, not attribute replication 61 00:03:55.080 --> 00:03:57.760 Now, let's create a C++ class 62 00:04:00.520 --> 00:04:04.000 Let's specify the player control as the parent class 63 00:04:05.760 --> 00:04:06.740 Next 64 00:04:08.000 --> 00:04:11.980 I'll name it NetPlayerController 65 00:04:12.920 --> 00:04:17.300 NetPlayerController, then Create Class 66 00:04:18.000 --> 00:04:21.000 When classes are created, reload them all 67 00:04:22.560 --> 00:04:23.960 Let's move on to the header file 68 00:04:23.960 --> 00:04:28.000 Let's move to the header file of NetPlayerController 69 00:04:28.000 --> 00:04:31.260 Below this, as members 70 00:04:31.260 --> 00:04:34.600 the logic that causes players to respawn 71 00:04:34.600 --> 00:04:37.800 and rejoin the game needs to be done using game modes 72 00:04:37.800 --> 00:04:40.900 Now, we already have a game mode class 73 00:04:40.900 --> 00:04:44.220 There is already a class called NetTPS Game Mode 74 00:04:45.220 --> 00:04:50.080 Let's first create a game mode so that we can access the game mode 75 00:04:50.100 --> 00:04:53.300 from the PlayerController and utilize the features in the game mode 76 00:04:54.660 --> 00:04:58.360 Let's write down, public, then UPROPERTY 77 00:05:00.740 --> 00:05:06.600 class ANetTPSGameMode* 78 00:05:06.600 --> 00:05:10.780 and put it as game mode, gm 79 00:05:10.780 --> 00:05:13.980 Next, I will redefine the BeginPlay function 80 00:05:15.200 --> 00:05:20.800 I will call it virtual void BeginPlay 81 00:05:22.000 --> 00:05:23.720 and put an override like this 82 00:05:26.640 --> 00:05:32.100 I will make BeginPlay auto-complete in the cpp file 83 00:05:32.120 --> 00:05:33.940 Super:: 84 00:05:34.000 --> 00:05:38.720 BeginPlay() 85 00:05:38.760 --> 00:05:42.240 And what role does this NetPlayerController 86 00:05:42.240 --> 00:05:46.390 want to play in this BeginPlay? Game mode 87 00:05:46.390 --> 00:05:49.730 I want to play the role of adding value to the class 88 00:05:49.730 --> 00:05:52.460 In this BeginPlay 89 00:05:53.740 --> 00:05:57.480 I will create a GameMode class, create an instance 90 00:05:57.480 --> 00:06:00.300 and put it in the gm variable 91 00:06:00.300 --> 00:06:02.480 When getting a game mode in BeginPlay 92 00:06:02.480 --> 00:06:05.680 we must determine 93 00:06:05.700 --> 00:06:10.020 whether the currently executing PlayerController runs on the client or server 94 00:06:10.880 --> 00:06:14.080 So, if(HasAuthority) 95 00:06:14.160 --> 00:06:17.560 What are the rules for this HasAuthority? 96 00:06:19.400 --> 00:06:23.030 When the local role is set to ROLE Authority 97 00:06:23.030 --> 00:06:26.500 the hasAuthority value is true 98 00:06:26.500 --> 00:06:29.300 In other words, in the case of a server, if this is running on the server 99 00:06:29.300 --> 00:06:32.240 we will add gm, why? Because game modes 100 00:06:32.240 --> 00:06:34.090 only exist on servers 101 00:06:34.140 --> 00:06:38.360 Do Cast ANetTPSGameMode 102 00:06:38.360 --> 00:06:40.140 Getworld 103 00:06:41.380 --> 00:06:46.320 and put GetAuthGameMode instance 104 00:06:48.640 --> 00:06:52.740 We will process it so that 105 00:06:52.740 --> 00:06:56.000 NetTPSGameMode can be added 106 00:06:56.000 --> 00:06:58.680 Now, let's create a serverRPC function 107 00:06:58.680 --> 00:07:01.280 to request that 108 00:07:01.280 --> 00:07:04.370 the client respawn from the server 109 00:07:04.370 --> 00:07:06.720 Let's move to the header file 110 00:07:06.760 --> 00:07:11.100 I'll put it separately by making it public at the bottom 111 00:07:13.640 --> 00:07:15.220 It's a respawn function 112 00:07:16.300 --> 00:07:19.600 UFUNCTION 113 00:07:19.600 --> 00:07:23.140 Since we will add serverRPC, we will set it as server 114 00:07:23.140 --> 00:07:25.300 and then as Reliable 115 00:07:26.540 --> 00:07:33.320 Then I will put void ServerRPC_RespawnPlayer() 116 00:07:34.600 --> 00:07:38.040 Then I will implement ServerRPC_RespawnPlayer 117 00:07:38.040 --> 00:07:41.740 at .cpp 118 00:07:45.700 --> 00:07:48.420 So, the header authority has been entered under BeginPlay 119 00:07:48.420 --> 00:07:51.000 and one more function has been added below that 120 00:07:51.000 --> 00:07:55.080 I put ServerRPC_RespawnPlayer_Implementation 121 00:07:56.180 --> 00:08:00.000 Then, here at ServerRPC_RepawnPlayer 122 00:08:00.000 --> 00:08:02.000 I will implement the respawning in the serverRPC respawning player 123 00:08:02.640 --> 00:08:05.080 First 124 00:08:07.360 --> 00:08:08.360 the Pawn you were 125 00:08:09.200 --> 00:08:11.240 using previously This is player, right? 126 00:08:11.240 --> 00:08:14.380 This is player character NetTPSCharacter 127 00:08:16.500 --> 00:08:18.440 Let's remember 128 00:08:19.600 --> 00:08:22.180 Then, at the back, we will 129 00:08:22.180 --> 00:08:24.400 newly spawn again 130 00:08:24.400 --> 00:08:29.160 But there is a function that automatically does this spawning 131 00:08:29.160 --> 00:08:31.981 There is a thing called Restart Player in the game mode 132 00:08:31.981 --> 00:08:35.321 There is a thing called Restart Player in the game mode 133 00:08:36.081 --> 00:08:38.940 If you use this, it will spawn you internally 134 00:08:38.940 --> 00:08:41.980 Remember the Pawn 135 00:08:42.720 --> 00:08:44.280 and then UnPossess 136 00:08:46.540 --> 00:08:51.420 Then what? Let's remove the previously used Pawn 137 00:08:51.420 --> 00:08:54.200 from memory 138 00:08:54.200 --> 00:08:59.480 The logic is to add this new spawning content after that 139 00:08:59.500 --> 00:09:04.400 So, I set the Pawn I previously used as an auto player 140 00:09:04.400 --> 00:09:06.340 In GetPawn 141 00:09:06.340 --> 00:09:09.840 GetPawn returns the Pawn 142 00:09:09.840 --> 00:09:12.420 currently owned by PlayerController 143 00:09:12.420 --> 00:09:14.560 Shall we try building it here first? 144 00:09:14.560 --> 00:09:19.561 Now, the build was successful Then, let's do UnPossess 145 00:09:21.500 --> 00:09:22.820 The one we're doing now 146 00:09:22.820 --> 00:09:25.980 When you UnPossess and go into this UnPossess function 147 00:09:25.980 --> 00:09:29.450 the Pawn you were using is retrieved from GetPawn 148 00:09:29.450 --> 00:09:33.390 Then, we take the current Pawn and UnPossess it 149 00:09:33.390 --> 00:09:36.810 Then, I will distroy 150 00:09:36.820 --> 00:09:37.941 the player again 151 00:09:37.941 --> 00:09:41.981 At Player, Destory 152 00:09:42.580 --> 00:09:47.380 Then, we take the player, and do UnPossess 153 00:09:47.380 --> 00:09:50.320 destroy this player because we need to remove it from memory 154 00:09:50.320 --> 00:09:53.460 and now we need a function to respawn the player 155 00:09:53.460 --> 00:09:55.321 by respawning it again 156 00:09:55.321 --> 00:09:57.581 Where is it? It's right there in GameMode 157 00:09:58.281 --> 00:10:00.241 That's why we came up with the game mode 158 00:10:00.241 --> 00:10:06.280 Whether we call a function in the game mode or do something else, we need this game mode instant to be able to use it 159 00:10:06.280 --> 00:10:10.460 There is a thing called RestartPlayer in the GameMode 160 00:10:11.960 --> 00:10:15.060 Now, if you look here, there is RestartPlayer 161 00:10:15.060 --> 00:10:17.760 and if you look below, there is something called RestartPlayerAtPlayerStart 162 00:10:17.760 --> 00:10:20.860 Next, there is something called RestartPlayerAtTransform below 163 00:10:22.160 --> 00:10:27.179 If you look at the restart player, the thing to add here is PlayerController 164 00:10:27.959 --> 00:10:33.720 Who will take charge of the newly created player Pawn object? 165 00:10:33.721 --> 00:10:37.821 It's me As this NetPlayerController, I will Possess 166 00:10:37.840 --> 00:10:41.439 I added this, and I'll build now 167 00:10:41.439 --> 00:10:46.739 Okay, then you don't have to call this serverRPC from somewhere 168 00:10:46.739 --> 00:10:48.520 When will we call? 169 00:10:48.520 --> 00:10:53.320 When the player dies, we create a retry button in the game over UI 170 00:10:53.320 --> 00:10:54.981 At GameOver UI 171 00:10:54.981 --> 00:10:56.821 When we press Retry button 172 00:10:57.400 --> 00:11:02.140 this code will run to respawn at that time 173 00:11:02.141 --> 00:11:04.001 Then, where is the content? 174 00:11:04.001 --> 00:11:07.981 It's in the mainUI header file, so go to the mainUI header 175 00:11:07.981 --> 00:11:12.001 This controls the game over UI in our mainUI 176 00:11:12.001 --> 00:11:16.999 The retry button is also here Now, let's create an event function 177 00:11:17.861 --> 00:11:20.841 to react when clicking this button 178 00:11:21.599 --> 00:11:27.599 Then, you need to add a form that links this retry button with that event function 179 00:11:27.599 --> 00:11:30.119 Let me add that part below here 180 00:11:31.601 --> 00:11:33.881 Now, do public first 181 00:11:35.181 --> 00:11:39.341 Like our Beginfly, the lifecycle function executed when this UI is 182 00:11:40.839 --> 00:11:47.199 first run is a virtual void native construct 183 00:11:47.199 --> 00:11:52.750 Let me clarify this When it runs inside this function, what is it going to do? 184 00:11:52.761 --> 00:11:56.861 There is a retry button and a binding that actually performs the function 185 00:11:56.861 --> 00:12:00.011 I'll take care of the content that ties together the events 186 00:12:00.011 --> 00:12:01.919 Then, I will set the processing function 187 00:12:01.919 --> 00:12:07.501 to UFUNCTION and create this void button on retry 188 00:12:07.501 --> 00:12:11.660 Now, let's add the native construct implementation part 189 00:12:11.660 --> 00:12:15.239 Next, we will add the on retry implementation 190 00:12:16.181 --> 00:12:18.201 It's MainUI.cpp 191 00:12:18.741 --> 00:12:20.041 Now, Super 192 00:12:21.361 --> 00:12:22.181 Native 193 00:12:22.979 --> 00:12:28.140 Construct, right? Write down NativeConstruct, and then 194 00:12:28.140 --> 00:12:33.190 you can implement the part that connects the two functions 195 00:12:33.190 --> 00:12:35.119 in this NativeConstruct below 196 00:12:35.119 --> 00:12:41.779 Now, we're going to connect 197 00:12:43.241 --> 00:12:46.501 the retry button and the processing function connection event 198 00:12:47.759 --> 00:12:51.159 Now, it says btn_retry like this 199 00:12:51.159 --> 00:12:55.559 You can use the OnClicked event for this 200 00:12:55.559 --> 00:12:58.609 This is delegate, and if you go to the implementation department 201 00:12:58.609 --> 00:13:01.680 to see how it is structured, it says on clicked 202 00:13:02.960 --> 00:13:06.710 Now, if you search for this 203 00:13:06.710 --> 00:13:12.000 there is simply no DYNAMIC_MULTICAST_DELEGATE parameter at the moment 204 00:13:12.000 --> 00:13:14.000 So I think you can write it like this 205 00:13:14.000 --> 00:13:17.682 If it is created like this and is DYNAMIC_MULTICAST 206 00:13:17.682 --> 00:13:21.142 it can be called from the Blueprint because DYNAMIC_MULTICAST is included 207 00:13:22.000 --> 00:13:27.622 OnClicked in MainUI can also be called from Blueprint 208 00:13:27.622 --> 00:13:31.222 So, I wrote UFUNCTION in the header file 209 00:13:33.479 --> 00:13:36.229 Now, let’s group the functions to be performed here 210 00:13:36.229 --> 00:13:40.320 Since this is multicast, add dynamic 211 00:13:41.002 --> 00:13:42.002 Then this 212 00:13:43.840 --> 00:13:48.040 It's on retry in MainUI 213 00:13:48.040 --> 00:13:50.300 Let me connect it like this 214 00:13:51.240 --> 00:13:55.640 Now, if you build it, there is currently no information about the UI button 215 00:13:55.640 --> 00:13:59.160 So, you need to include it at the top 216 00:13:59.160 --> 00:14:03.600 Now, I'm adding a button below the image here 217 00:14:04.162 --> 00:14:07.662 Now, let's build it like this and add the content 218 00:14:08.642 --> 00:14:11.202 you want to add in on retry 219 00:14:12.020 --> 00:14:16.920 Now, you need to call Server RPC respawn OnRetry 220 00:14:16.921 --> 00:14:19.940 Let's try this OnRetry 221 00:14:20.940 --> 00:14:26.222 First, the game-ending UI is floating on the screen 222 00:14:26.222 --> 00:14:30.020 If the game is over, you won't see them on-retry button when it's floating 223 00:14:30.020 --> 00:14:32.920 If you just click the button, it disappears from the screen 224 00:14:32.920 --> 00:14:34.740 Let's process this UI 225 00:14:36.440 --> 00:14:41.680 so it disappears and becomes invisible 226 00:14:42.600 --> 00:14:46.200 and then request a respawn from the server 227 00:14:49.519 --> 00:14:54.820 As for the game end UI, what we have now is called Gameover UI 228 00:14:56.160 --> 00:14:59.760 That's right; it's declared that way in the main UI header file 229 00:14:59.760 --> 00:15:01.399 So, in the game over UI 230 00:15:01.862 --> 00:15:07.259 I'll add the SetVisibility property 231 00:15:09.040 --> 00:15:12.490 and set it to hidden like this 232 00:15:12.490 --> 00:15:15.960 Then, it will not be visible on the screen like this 233 00:15:18.640 --> 00:15:21.390 Next, I will request a respawn from the server 234 00:15:21.390 --> 00:15:24.160 This is now created in PlayerController 235 00:15:24.160 --> 00:15:27.400 Since we created Server RPC in NetPlayerController 236 00:15:28.182 --> 00:15:32.702 we need NetPlayerController 237 00:15:33.420 --> 00:15:39.080 So bring Cas ANetPlayerController 238 00:15:40.480 --> 00:15:43.630 and in world, when you do GetFirstPlayerController 239 00:15:44.730 --> 00:15:48.379 it will hand over the player it is using 240 00:15:48.379 --> 00:15:53.200 If PlayerController is brought in properly 241 00:15:54.600 --> 00:15:58.650 I first output the mouse cursor on the screen 242 00:15:58.650 --> 00:16:01.279 so that the current UI can show the screen 243 00:16:01.279 --> 00:16:03.929 The mouse cursor 244 00:16:03.929 --> 00:16:07.040 If you press the retry button again, it should disappear 245 00:16:07.040 --> 00:16:09.590 Then, when the game is over, the UI will pop up 246 00:16:09.590 --> 00:16:12.359 so I will process it so that the mouse cursor is visible again 247 00:16:12.359 --> 00:16:17.600 Here, when repawning, to not show the mouse cursor, at PlayerController 248 00:16:18.200 --> 00:16:19.560 there is SetShow 249 00:16:21.720 --> 00:16:24.020 MouseCursor 250 00:16:24.020 --> 00:16:26.920 Let's add false here 251 00:16:27.760 --> 00:16:29.510 And you have to call serverRPC 252 00:16:29.510 --> 00:16:36.582 Now, this is the ServerRPC_RespawnPlayer function in PlayerController, right? 253 00:16:36.582 --> 00:16:38.242 I'll handle it like this 254 00:16:39.020 --> 00:16:43.660 Well, we have to use ANetPlayerController, too, right? 255 00:16:43.660 --> 00:16:46.810 Next, this GameoverUI is a horizontal box 256 00:16:46.810 --> 00:16:51.580 This horizontal box must also be included so it can be used here 257 00:16:53.500 --> 00:16:59.200 So, I will add a horizontal box like this at the bottom 258 00:17:00.342 --> 00:17:02.840 And what else do you need? 259 00:17:02.840 --> 00:17:09.060 ANetPlayerController must also have a header added like this to use it 260 00:17:10.560 --> 00:17:12.879 Now, let's try building it 261 00:17:15.359 --> 00:17:21.580 Cast in the main UI, oh, I missed the t Let's put it like this: cast 262 00:17:24.500 --> 00:17:25.520 There you go 263 00:17:26.760 --> 00:17:32.599 Once it's built, I'll go to Unreal Editor and make it a Blueprint 264 00:17:34.279 --> 00:17:38.699 Coming from C++, the NetPlayerController we created 265 00:17:39.702 --> 00:17:43.522 has a C++ class here 266 00:17:43.522 --> 00:17:47.939 I'll right-click and make it a Blueprint class 267 00:17:50.819 --> 00:17:55.520 For the location, put BP_NetPlayerController 268 00:17:56.760 --> 00:17:59.400 in the blueprint folder of NetTPS 269 00:18:01.799 --> 00:18:03.660 and Create Blueprint Class 270 00:18:08.000 --> 00:18:12.150 Then, a Blueprint is created like this 271 00:18:12.150 --> 00:18:14.980 Let me save and close it 272 00:18:16.959 --> 00:18:22.820 Now, I created a blueprint class like this, and if you go to that folder 273 00:18:24.000 --> 00:18:26.300 you will find BP_NetTPS GameMode 274 00:18:27.062 --> 00:18:29.462 Okay, let's open this Blueprint 275 00:18:34.159 --> 00:18:37.580 If you go here and look at the current PlayerController 276 00:18:40.080 --> 00:18:44.939 the PlayerController class here is just this default PlayerController 277 00:18:45.479 --> 00:18:50.042 So, we just created a PlayerController using NetTPS 278 00:18:50.042 --> 00:18:53.722 Let's change it to NetPlayerController 279 00:18:53.722 --> 00:18:58.199 If you look here, PlayerController is BP_NetPlayerController, modify it to this 280 00:19:01.080 --> 00:19:05.719 Only then will the class we have created operate normally, compile 281 00:19:10.620 --> 00:19:14.080 Then play, play one more 282 00:19:16.082 --> 00:19:17.802 Well, there is a client 283 00:19:18.600 --> 00:19:23.599 and there is a server If you look at the server first, the user is 284 00:19:24.159 --> 00:19:25.509 created normally there 285 00:19:25.509 --> 00:19:30.059 Since there are two player starts, one will be created here, and one will be created there 286 00:19:31.239 --> 00:19:34.442 So, let's grab a gun and shoot another user 287 00:19:34.442 --> 00:19:36.402 Bang, bang, bang 288 00:19:37.082 --> 00:19:37.942 Then 289 00:19:38.822 --> 00:19:42.642 there is a Retry button and an Exit button on the client screen like this, right? 290 00:19:42.642 --> 00:19:46.880 If you can't see the Retry button on the screen right now, I'll put it like this 291 00:19:46.880 --> 00:19:49.440 There's a Retry button, let's click on it 292 00:19:50.082 --> 00:19:55.979 Then it's respawned normally 293 00:19:55.979 --> 00:20:00.419 Again, I can move and jump while holding the gun 294 00:20:00.602 --> 00:20:02.762 Now, I'll fight I'll shoot the sub-character 295 00:20:02.762 --> 00:20:05.122 Bang, bang, bang 296 00:20:06.302 --> 00:20:09.882 Then, the Gameover UI will appear on the server as well 297 00:20:10.562 --> 00:20:11.782 There is a Retry button 298 00:20:12.602 --> 00:20:13.322 Right? 299 00:20:13.322 --> 00:20:15.282 Then, I'll click the Retry button 300 00:20:15.282 --> 00:20:16.322 Click 301 00:20:16.322 --> 00:20:20.682 Then, it Retry normally, and respawn will work well 302 00:20:20.682 --> 00:20:22.822 Respawn Problem and Lifecycle Function Analysis 303 00:20:24.922 --> 00:20:26.361 But I think there is a problem here 304 00:20:26.361 --> 00:20:28.041 What's the problem on the screen? 305 00:20:29.722 --> 00:20:31.841 This part is the problem 306 00:20:32.681 --> 00:20:37.882 In its main UI, this is not filled in properly 307 00:20:37.882 --> 00:20:41.642 and this part is now created anew above the head 308 00:20:41.642 --> 00:20:44.722 Weird, what about the main player? 309 00:20:45.602 --> 00:20:49.321 When I used it, I processed it so that it wouldn't be visible 310 00:20:49.321 --> 00:20:50.602 but the fact that 311 00:20:51.382 --> 00:20:54.122 it's showing up now means there's some kind of problem 312 00:20:54.122 --> 00:20:56.082 Let's find out what the problem is 313 00:20:57.122 --> 00:20:58.262 So let's come to our code 314 00:20:59.842 --> 00:21:03.161 and go to the cpp of NetTPSCharacter 315 00:21:04.841 --> 00:21:08.361 Here, we have InitUIWidget 316 00:21:10.781 --> 00:21:15.062 If you come to the InitUIWidget function in NetTPSCharacter 317 00:21:15.062 --> 00:21:19.242 it doesn't process anything if the PlayerController is not in control 318 00:21:19.242 --> 00:21:21.241 Right? 319 00:21:23.682 --> 00:21:27.861 Then, if it's a PlayerController, how about creating a new main UI underneath 320 00:21:27.861 --> 00:21:30.282 and then initializing the bullet? 321 00:21:30.282 --> 00:21:32.521 I need to make the health bar above my head invisible 322 00:21:32.521 --> 00:21:37.001 In other words, this is supposed to run, but it hasn't run now 323 00:21:37.962 --> 00:21:41.202 so it seems like it keeps popping up over my head 324 00:21:41.682 --> 00:21:43.161 So what is the problem? 325 00:21:43.161 --> 00:21:47.161 We need to learn a little about lifecycle functions to do this now 326 00:21:47.161 --> 00:21:51.521 Because the InitUIWidget function is 327 00:21:51.521 --> 00:21:54.201 called in BeginPlay 328 00:21:55.082 --> 00:21:59.481 Then, if there is a PlayerController, it shouldn't float above your head 329 00:21:59.481 --> 00:22:01.941 but since there is no PlayerController, it seems to float above your head 330 00:22:02.682 --> 00:22:06.462 Then, I think there's something wrong 331 00:22:06.462 --> 00:22:11.362 with the point where the PlayerController do Possess it and the point at which this BeginPlay is now called 332 00:22:11.362 --> 00:22:17.021 So for this we need to look up this order of lifecycle functions 333 00:22:17.041 --> 00:22:19.601 To make printing easier 334 00:22:19.601 --> 00:22:22.162 we will move to the NetTPS header file 335 00:22:22.162 --> 00:22:25.082 and add some macros 336 00:22:25.082 --> 00:22:27.362 to make printing easier 337 00:22:27.362 --> 00:22:29.442 In addition to this part, we have also added a NetMode 338 00:22:29.442 --> 00:22:32.892 Now, I'm trying to be able to take a picture like this to see 339 00:22:32.892 --> 00:22:34.681 whether it's running on the server or the client 340 00:22:34.681 --> 00:22:39.901 Enter it in define, leave a space, and write NetMode 341 00:22:39.901 --> 00:22:45.321 Leave a space and get GetNetMode like this 342 00:22:45.321 --> 00:22:51.401 If this function is a client in this NetMode 343 00:22:52.621 --> 00:22:55.602 we put a client 344 00:22:57.002 --> 00:22:59.241 in that NetMode like this 345 00:23:00.901 --> 00:23:03.162 When you call this NetMode 346 00:23:03.162 --> 00:23:05.562 it internally checks to see if you are the client 347 00:23:05.562 --> 00:23:08.701 and then passes the client character 348 00:23:08.723 --> 00:23:10.743 Otherwise 349 00:23:10.761 --> 00:23:14.461 get NetMode, which may not necessarily be a server 350 00:23:14.461 --> 00:23:17.242 Since it can simply run in Standalone mode 351 00:23:17.242 --> 00:23:24.082 it may be NM_Standalone in this NetMode 352 00:23:24.082 --> 00:23:27.441 In cases like this, TEXT 353 00:23:28.643 --> 00:23:30.963 Standalone 354 00:23:32.881 --> 00:23:35.601 Did I put it here like this? 355 00:23:35.601 --> 00:23:37.601 Since it is a ternary operator, let's put it like this 356 00:23:37.601 --> 00:23:40.162 To make it a little easier to see 357 00:23:40.162 --> 00:23:42.041 at NetMode, it's cliented like get 358 00:23:42.041 --> 00:23:44.761 Let's make this with a line break 359 00:23:44.761 --> 00:23:49.041 I've placed a newline character 360 00:23:49.041 --> 00:23:49.981 won symbol 361 00:23:51.081 --> 00:23:54.321 and a backlash And then do line-break 362 00:23:54.321 --> 00:23:58.481 If get NetMode is like this, Standalone 363 00:23:58.481 --> 00:24:00.642 Otherwise, it's called Server 364 00:24:02.783 --> 00:24:03.983 Server 365 00:24:04.522 --> 00:24:06.401 If I close one more horizontal line 366 00:24:06.401 --> 00:24:10.801 NetMode will be done with the opening and closing parentheses here 367 00:24:10.801 --> 00:24:12.162 So what happens then? 368 00:24:12.162 --> 00:24:14.722 First, get the NetMode 369 00:24:14.722 --> 00:24:16.762 and this NetMode value is the client 370 00:24:16.762 --> 00:24:20.622 Then, there was a server window and a client window 371 00:24:20.622 --> 00:24:23.442 With that, you can tell whether 372 00:24:23.442 --> 00:24:25.162 it is currently running in the server 373 00:24:25.162 --> 00:24:27.521 or client window 374 00:24:27.521 --> 00:24:31.162 At that time, I put the string 'client' in this NetMode 375 00:24:32.442 --> 00:24:35.681 and when I looked later, I saw that the NetMode was Standalone 376 00:24:35.681 --> 00:24:38.162 It's not like it's either the client or the server 377 00:24:38.162 --> 00:24:42.681 Standalone, in other words, when we press play in Unreal Editor 378 00:24:42.681 --> 00:24:45.241 there are three NetMode options 379 00:24:45.241 --> 00:24:48.722 There is Standalone, there is a server, and there is a client 380 00:24:48.722 --> 00:24:53.041 In other words, when we run it for the first time, we run it in Standalone mode 381 00:24:53.041 --> 00:24:56.641 In that case, we might just be Standalone 382 00:24:56.641 --> 00:25:00.743 Right, so there are cases where we need to determine 383 00:25:00.743 --> 00:25:07.163 whether it is a Standalone, server, or client in NetMode 384 00:25:07.202 --> 00:25:09.901 So now, when it's a client, it's a client 385 00:25:09.921 --> 00:25:12.282 and when it's a Standalone, it's a Standalone 386 00:25:12.282 --> 00:25:15.002 If not, it was the server, right? Let's say server 387 00:25:15.002 --> 00:25:17.401 Shall we set this to lowercase? 388 00:25:17.401 --> 00:25:20.722 We'll put it like this: Client Standalone Server 389 00:25:20.722 --> 00:25:26.502 Okay, then, let's create another defined macro function this time 390 00:25:27.582 --> 00:25:32.201 I'll put the information on how this is called here 391 00:25:33.361 --> 00:25:37.401 In other words, to display where the function is called 392 00:25:37.401 --> 00:25:40.101 and what line it is on 393 00:25:40.101 --> 00:25:43.761 we will add a macro called callinfo 394 00:25:43.762 --> 00:25:48.361 In this callinfo, open the parenthesis, enter FString 395 00:25:48.361 --> 00:25:53.742 and then insert _ _ function _ _ like this 396 00:25:55.282 --> 00:25:57.401 There are two _ below 397 00:25:57.401 --> 00:26:00.681 Put it like this and then add it 398 00:26:00.681 --> 00:26:04.282 The operator of this FString is overloaded 399 00:26:04.282 --> 00:26:08.361 and the overloading is related to addition 400 00:26:08.361 --> 00:26:13.002 You can link it here by adding additional text 401 00:26:13.002 --> 00:26:16.442 You can leave the text and paste it all with FString 402 00:26:16.442 --> 00:26:21.581 but I will add the text here and then open the parentheses like this 403 00:26:23.921 --> 00:26:27.282 Let's add an opening parenthesis character to the text 404 00:26:27.282 --> 00:26:31.681 Now, let's add a letter after this 405 00:26:31.681 --> 00:26:33.002 FString 406 00:26:36.763 --> 00:26:39.203 Let's do FromInt_ _ 407 00:26:39.203 --> 00:26:43.361 This time, we will place the line number line _ _ like this 408 00:26:43.361 --> 00:26:45.322 Then, even the line number is printed 409 00:26:45.322 --> 00:26:49.722 Next, I'll close the text I'll close the parenthesis 410 00:26:49.722 --> 00:26:53.602 It happened like this 411 00:26:53.602 --> 00:26:56.782 Then, if you close one more parenthesis, you'll end up here 412 00:26:57.482 --> 00:27:01.282 You need to open the parenthesis in CALLINFO, close the parenthesis after it 413 00:27:01.282 --> 00:27:04.762 CALLINFO, and leave this macro floating 414 00:27:04.762 --> 00:27:07.202 That's the only way for him to receive this price 415 00:27:07.202 --> 00:27:12.442 If you paste it, it will be called a function, so you must put it like this 416 00:27:12.442 --> 00:27:14.121 Now, let's begin 417 00:27:14.121 --> 00:27:18.401 creating a macro function that prints this 418 00:27:18.401 --> 00:27:21.961 and add the content to be printed using these 419 00:27:21.961 --> 00:27:24.081 Do #define 420 00:27:24.081 --> 00:27:26.661 This time, let's say PRINTLOG 421 00:27:27.841 --> 00:27:32.041 This doesn't have to be left out by one space You have to add it to open the parenthesis 422 00:27:32.042 --> 00:27:34.602 Then, put it in fmt format 423 00:27:34.602 --> 00:27:39.641 Next, we process it to receive the variable argument like this 424 00:27:41.081 --> 00:27:42.562 Next, a space 425 00:27:42.562 --> 00:27:47.181 called UE_LOG can be displayed in the console window in Unreal Engine 426 00:27:47.181 --> 00:27:50.762 Please enter LogTemp here 427 00:27:50.762 --> 00:27:54.921 Let's put a warning, and to display the screen in yellow 428 00:27:54.921 --> 00:27:59.041 open the text brackets 429 00:27:59.041 --> 00:28:03.361 and print the NetMode value here first 430 00:28:03.361 --> 00:28:08.361 It comes in as a %s string value, so put it like this 431 00:28:08.361 --> 00:28:12.662 Next, let's add call info information to see where it is currently being called 432 00:28:13.722 --> 00:28:17.421 Put %s again 433 00:28:18.741 --> 00:28:21.482 Then, some content is output on a few lines in a certain function 434 00:28:21.482 --> 00:28:24.241 So, I'll put a colon like this 435 00:28:24.241 --> 00:28:29.182 and next to it, I'll put the content we want to add, like %s 436 00:28:30.442 --> 00:28:35.522 Now, add NetMode here with a comma 437 00:28:36.962 --> 00:28:41.061 I entered NetMode and will add call info information using a comma 438 00:28:41.061 --> 00:28:43.962 Since CALLINFO is passed as a FString type 439 00:28:43.962 --> 00:28:47.601 we put a pointer to pass the value 440 00:28:47.681 --> 00:28:49.642 CALLINFO 441 00:28:49.642 --> 00:28:54.941 Then, we have to finally display the value here, which came in as a variable argument value, on the screen 442 00:28:54.941 --> 00:28:56.642 So, put it in like this 443 00:28:56.642 --> 00:29:01.762 and use %s string printf 444 00:29:01.762 --> 00:29:07.041 fmt, ##_ _ 445 00:29:07.041 --> 00:29:11.681 VA_ARGS, like this 446 00:29:11.681 --> 00:29:12.921 Then, _ _ 447 00:29:14.263 --> 00:29:15.803 Close the parentheses and do that 448 00:29:16.721 --> 00:29:20.521 Shall we add one more of these? I tried to search with LogTemp 449 00:29:20.522 --> 00:29:22.962 but I don't like this category either 450 00:29:22.963 --> 00:29:26.581 I don't like this log category either, so I'll put it at the top like this 451 00:29:26.601 --> 00:29:28.842 so we can use our own log category 452 00:29:28.842 --> 00:29:32.801 To declare, DECLARE_LOG_ 453 00:29:32.801 --> 00:29:36.361 CATEGORY_EXTERN 454 00:29:36.361 --> 00:29:38.721 Then, we can add our categories 455 00:29:38.721 --> 00:29:45.642 We created our Netlog category 456 00:29:45.642 --> 00:29:50.041 to put everything into the log like this 457 00:29:50.041 --> 00:29:51.263 Are you done? 458 00:29:51.263 --> 00:29:56.281 Insert Netlog in place of the LogTemp 459 00:29:56.801 --> 00:30:00.022 Then, on the screen, we will name this netlog 460 00:30:00.022 --> 00:30:04.183 as a category, and the name we want to record will be displayed in the console window 461 00:30:04.183 --> 00:30:05.223 Done 462 00:30:05.223 --> 00:30:09.283 Now, we added a new NetLog like this, it's DECLARE 463 00:30:09.283 --> 00:30:11.283 This is in the declaration part, so we need one more define 464 00:30:11.283 --> 00:30:16.123 It is at NetTPS.cpp 465 00:30:16.123 --> 00:30:18.303 You need to add it 466 00:30:20.083 --> 00:30:24.424 to the NetTPS.cpp file DEFINE_LOG_CATEGORY 467 00:30:24.424 --> 00:30:28.603 In this way, we have to add a NetLog here 468 00:30:28.603 --> 00:30:33.943 so that the NetLog added to the header is defined and can be used 469 00:30:33.964 --> 00:30:40.244 Now, NetLog is added to NetTPSh and can be used 470 00:30:40.246 --> 00:30:45.326 Now, let's check and process what is being output normally 471 00:30:45.326 --> 00:30:51.342 Now, I told you there was a problem with the lifecycle function when respawning 472 00:30:51.342 --> 00:30:53.892 So, when a character is created 473 00:30:53.892 --> 00:30:58.144 InitUIWidget is called in BeginPlay 474 00:30:58.144 --> 00:31:01.344 but at this time, PlayerController is not Possessing 475 00:31:02.901 --> 00:31:06.401 Right? Currently, the PlayerController is not Possessed 476 00:31:07.363 --> 00:31:10.513 so the lower part of the HUD is disabled 477 00:31:10.513 --> 00:31:14.343 and the part that makes it invisible was not executed 478 00:31:14.363 --> 00:31:20.042 So, let's look at a few lifecycle functions and deal with them 479 00:31:20.042 --> 00:31:23.142 First, if you have Possess 480 00:31:23.142 --> 00:31:26.503 PlayerController will be here 481 00:31:26.523 --> 00:31:30.824 That's right, this NetTPSCharacter is a Pawn 482 00:31:30.824 --> 00:31:34.664 This Pawn is spawned, and when it is spawned, BeginPlay will be called 483 00:31:34.664 --> 00:31:37.514 Right, what should we do at this time? 484 00:31:37.514 --> 00:31:42.022 PlayerController should have been spawned first 485 00:31:42.044 --> 00:31:47.484 I think there is a PlayerController when this is called, so the structure below is displayed 486 00:31:47.504 --> 00:31:51.424 When being Possessed 487 00:31:51.424 --> 00:31:54.174 we need to check 488 00:31:54.174 --> 00:31:56.902 whether this character was Possessed first 489 00:31:56.902 --> 00:31:59.202 or when BeginPlay was called first 490 00:31:59.202 --> 00:32:03.642 So, let's add this to the bottom of 491 00:32:05.162 --> 00:32:07.903 this NetTPSCharacter, public 492 00:32:10.564 --> 00:32:14.384 From PlayerController 493 00:32:15.543 --> 00:32:19.163 when it is Possessed 494 00:32:19.163 --> 00:32:24.444 there is a function We will implement that first 495 00:32:24.444 --> 00:32:28.104 and figure out whether it or BeginPlay is called first 496 00:32:28.822 --> 00:32:32.022 if BeginPlay is called even though Possess is not possible 497 00:32:32.022 --> 00:32:34.263 then there is no PlayerController 498 00:32:35.083 --> 00:32:40.383 Let's check if that's the case 499 00:32:40.383 --> 00:32:42.803 There's a function virtual void PossedBy 500 00:32:44.762 --> 00:32:46.743 It is overrided 501 00:32:48.484 --> 00:32:51.864 Now, let's add the implementation part 502 00:32:51.884 --> 00:32:55.284 Let's declare PossessedBy 503 00:32:55.342 --> 00:32:57.082 and add it to .cpp 504 00:32:58.882 --> 00:33:02.782 To make things a little easier 505 00:33:02.782 --> 00:33:06.363 I will take this to the BeginPlay location 506 00:33:06.363 --> 00:33:09.713 Now, there is an Init widget in BeginPlay 507 00:33:09.713 --> 00:33:13.022 Let's put it below the InitUIWidget 508 00:33:16.723 --> 00:33:19.723 Then, you can check whether PossessedBy 509 00:33:19.723 --> 00:33:23.203 or InitUIWidget is called first 510 00:33:23.203 --> 00:33:26.453 And where is InitUIWidget being called now? 511 00:33:26.453 --> 00:33:29.022 It's being called from BeginPlay 512 00:33:31.624 --> 00:33:34.284 If you call it from BeginPlay 513 00:33:34.284 --> 00:33:36.234 you only need a PlayerController now 514 00:33:36.234 --> 00:33:39.202 But since there isn't one, let's check it here 515 00:33:39.202 --> 00:33:44.943 Add PossessedBy to Super 516 00:33:44.943 --> 00:33:51.962 Now, let's do it in front, PRINTLOG 517 00:33:51.962 --> 00:33:56.243 Let's go to PRINTLOG NetTPS.h and put 'begin' here 518 00:33:58.523 --> 00:34:02.523 Then, I'll put an end to this 519 00:34:04.944 --> 00:34:07.494 What happens in this PossessedBy? 520 00:34:07.494 --> 00:34:10.304 If you put a controller in PossessedBy in Super 521 00:34:10.304 --> 00:34:14.322 and go to the current character class here 522 00:34:14.322 --> 00:34:17.672 you also put PossessedBy's new controller in Super 523 00:34:17.672 --> 00:34:20.042 If you go into this too 524 00:34:21.764 --> 00:34:24.444 what is this doing as the owner of the Pawn? 525 00:34:24.444 --> 00:34:28.944 PlayerController, this is being passed on to the owner of the controller handed over here 526 00:34:28.944 --> 00:34:30.063 Set it up like this 527 00:34:30.763 --> 00:34:32.563 Well, how about that? 528 00:34:32.563 --> 00:34:36.343 Let's put this newly created controller in the controller like this 529 00:34:36.343 --> 00:34:39.164 In PossessedBy 530 00:34:39.164 --> 00:34:46.123 Now, this is how it should be so that the lower part is executed since the value is 531 00:34:46.123 --> 00:34:48.923 entered into the controller in InitUIWidget 532 00:34:48.923 --> 00:34:52.943 But PossessedBy currently has begin 533 00:34:54.964 --> 00:34:58.384 and end rather than begin 534 00:34:58.384 --> 00:35:02.203 If begin play is called before these two, a problem may arise 535 00:35:02.203 --> 00:35:05.924 PlayerController is called even though it is not yet Possessed 536 00:35:05.924 --> 00:35:08.404 So, let's try printing it here as well 537 00:35:10.523 --> 00:35:14.473 I also add 'begin' to InitUIWidget here 538 00:35:14.473 --> 00:35:16.243 I think you just need to click it 539 00:35:16.243 --> 00:35:20.404 Then, wouldn't it be okay to just check if there's a current controller? 540 00:35:20.404 --> 00:35:23.204 Now, let's determine whether there is a player or a controller 541 00:35:23.204 --> 00:35:30.243 so let's set it to begin and put %s at the front 542 00:35:30.243 --> 00:35:32.684 If there is a player here right now 543 00:35:32.684 --> 00:35:36.224 you can mark it as that player 544 00:35:36.224 --> 00:35:38.284 and do Begin 545 00:35:38.284 --> 00:35:43.243 Then, put comma, and if there's that controller 546 00:35:43.245 --> 00:35:46.963 what if it's not null? At TEXT 547 00:35:46.963 --> 00:35:49.702 I'll put PLAYER 548 00:35:51.384 --> 00:35:52.564 Or not 549 00:35:55.643 --> 00:35:57.542 No Player 550 00:36:00.442 --> 00:36:02.692 If you have a controller here, you have a player 551 00:36:02.692 --> 00:36:05.523 Otherwise, let's put it like this: No Player 552 00:36:05.523 --> 00:36:07.123 Oh, I forgot TEXT here 553 00:36:07.123 --> 00:36:09.373 Please enter TEXT 554 00:36:09.373 --> 00:36:12.523 You need to add a macro 555 00:36:12.523 --> 00:36:13.643 This is missing 556 00:36:16.363 --> 00:36:19.183 Now, put the text up here 557 00:36:21.042 --> 00:36:26.063 and at the bottom like this 558 00:36:26.063 --> 00:36:31.064 I forgot the text macro here 559 00:36:32.184 --> 00:36:35.963 so you have to pass it to the text part like this 560 00:36:35.963 --> 00:36:40.004 You have to wrap it once because it is put into 561 00:36:40.004 --> 00:36:42.654 the T character type in Unreal Editor 562 00:36:42.654 --> 00:36:45.264 so you can't pass the FString 563 00:36:46.950 --> 00:36:49.330 Now, let's build it like this 564 00:36:50.462 --> 00:36:54.823 Let's go to Unreal Editor and open the output log window 565 00:36:54.823 --> 00:36:57.373 If you look at the Outpool Log window in the Windows menu 566 00:36:57.373 --> 00:36:59.922 You will see Outpool Log Let me open this 567 00:37:01.523 --> 00:37:07.502 Then, I will search for the NetLog in the filter section 568 00:37:07.504 --> 00:37:09.724 Now, let's play it first 569 00:37:10.662 --> 00:37:15.004 Now, how does it pop up? 570 00:37:15.004 --> 00:37:19.564 If you look at everything here, it's floating on the server PossessedBy begin is displayed on the server 571 00:37:19.564 --> 00:37:21.764 Then PossessedBy End appeared, right? 572 00:37:21.764 --> 00:37:24.803 Then, the PlayerController has been Possessed 573 00:37:24.824 --> 00:37:27.424 Then, begin 574 00:37:27.424 --> 00:37:29.904 Here, in InitUIWidget 575 00:37:29.904 --> 00:37:32.554 PLAYER Begin appears, which means there is a player 576 00:37:32.554 --> 00:37:36.183 In other words, it is called 577 00:37:36.184 --> 00:37:39.164 after PossessedBy is set 578 00:37:39.944 --> 00:37:42.144 It means there are players 579 00:37:42.144 --> 00:37:45.244 Then, let's add a client 580 00:37:45.244 --> 00:37:47.763 Let's add a client play 581 00:37:49.203 --> 00:37:51.453 Now, what are you going to print next? 582 00:37:51.453 --> 00:37:56.163 Let's print the result when it respawns 583 00:37:56.163 --> 00:38:01.844 In this case, when the server shoots the client 584 00:38:01.844 --> 00:38:03.394 I'll press the retry button 585 00:38:03.394 --> 00:38:07.024 I will try the clear log again here and print it again here 586 00:38:07.024 --> 00:38:14.123 Retry, now, what happens? It happens when InitUIWidget is called 587 00:38:14.123 --> 00:38:16.234 This means that the begin play comes first 588 00:38:16.234 --> 00:38:18.484 What happens next? 589 00:38:18.484 --> 00:38:22.883 No Player, there is no player Then the server calls this 590 00:38:24.224 --> 00:38:25.783 What happens? 591 00:38:25.783 --> 00:38:28.522 Possess, there's no problem with this, why? 592 00:38:30.083 --> 00:38:31.883 What do you do first on the server? 593 00:38:31.883 --> 00:38:36.303 You ask to respawn to Server RPC Then I'll first spawn and create it on the server 594 00:38:36.304 --> 00:38:40.354 Just like spawning an actor, since this Pawn is also an actor 595 00:38:40.354 --> 00:38:42.984 So I created this Pawn because it is also an actor 596 00:38:42.984 --> 00:38:47.603 At that time, BeginPlay was called first, and InitUIWidget was called first 597 00:38:47.604 --> 00:38:48.664 At that time, what? 598 00:38:49.244 --> 00:38:50.764 Player, does this have a player? 599 00:38:50.764 --> 00:38:52.844 No, of course not 600 00:38:53.324 --> 00:38:55.924 Then, PossessedBy is called from the server 601 00:38:55.924 --> 00:38:57.663 These are all being called from the server now 602 00:38:57.663 --> 00:39:00.443 PossessedBy is called now 603 00:39:02.965 --> 00:39:07.325 Then, this finally gets a controller, at this pawn 604 00:39:07.325 --> 00:39:10.702 Then, when the client looks at the end, the client 605 00:39:11.885 --> 00:39:13.002 this one 606 00:39:13.002 --> 00:39:15.452 this client becomes spawn 607 00:39:15.452 --> 00:39:19.723 Then, what happens when BeginPlay is called? InitUIWidget is called 608 00:39:19.723 --> 00:39:23.573 This is because the server has already Possessed by when BeginPlay is called 609 00:39:23.573 --> 00:39:24.942 so what about this case? 610 00:39:26.125 --> 00:39:29.785 There's a controller 611 00:39:29.822 --> 00:39:36.443 Now, let's change it and run the server from the client 612 00:39:36.443 --> 00:39:38.405 Bang, bang, bang 613 00:39:39.843 --> 00:39:44.423 Okay, so the above 4 are the contents from before 614 00:39:44.425 --> 00:39:46.745 If things go as planned, what should happen? 615 00:39:48.143 --> 00:39:50.843 BeginPlay is called first at the server 616 00:39:50.843 --> 00:39:54.843 then PossessedBy, and then again called at client 617 00:39:54.843 --> 00:39:56.603 It's like that 618 00:39:57.625 --> 00:39:59.865 Now, let's press the retry button this time 619 00:40:00.575 --> 00:40:04.455 Now, I will clear the log for easier viewing and press the retry button on the server 620 00:40:05.004 --> 00:40:09.244 Click, the UI HUD now appears on the screen 621 00:40:09.244 --> 00:40:15.164 Let's take a quick look at the structure What happens the same way? 622 00:40:15.164 --> 00:40:17.964 InitUIWidget is called 623 00:40:17.964 --> 00:40:22.444 In other words, BeginPlay was called because it had to be spawned first 624 00:40:22.444 --> 00:40:23.694 BeginPlay 625 00:40:23.694 --> 00:40:26.944 But this BeginPlay calls InitUIWidget 626 00:40:26.944 --> 00:40:30.164 So what about this when BeginPlay is called? 627 00:40:30.185 --> 00:40:31.185 Right now 628 00:40:31.964 --> 00:40:34.285 Is controller Possessed or not? 629 00:40:34.285 --> 00:40:35.585 It's not 630 00:40:35.585 --> 00:40:39.465 First, it has to be spawned, and the actor has to be created 631 00:40:39.484 --> 00:40:42.804 Then, what happens is PossessedBy on the server 632 00:40:44.245 --> 00:40:48.445 Then the controller tries to own it like this 633 00:40:48.445 --> 00:40:51.895 In other words, at the point when BeginPlay is called 634 00:40:51.895 --> 00:40:55.765 when BeginPlay is called for this server character, is there a controller present or not? 635 00:40:55.765 --> 00:40:56.885 Does not exist 636 00:40:58.164 --> 00:41:03.647 Then, the PossessedBy controller finally catches it 637 00:41:03.647 --> 00:41:07.217 Only then is the owner relationship established, and a controller is created 638 00:41:08.084 --> 00:41:08.784 Right 639 00:41:08.784 --> 00:41:11.334 And then, at the end, the client is called 640 00:41:11.334 --> 00:41:14.225 At this client, this one at the back 641 00:41:14.225 --> 00:41:19.165 PossessedBy is on this, but what about this? 642 00:41:19.165 --> 00:41:23.444 This thing doesn't have a controller, so it comes out like this normally 643 00:41:23.444 --> 00:41:30.484 In other words, what we can see here is that there is a problem 644 00:41:30.484 --> 00:41:35.185 Not all children start the lifecycle the same way 645 00:41:35.185 --> 00:41:39.365 Looking at it now, when initUI is called on this server, in this case, the UI pops up 646 00:41:39.404 --> 00:41:42.354 because the controller does not Possess it 647 00:41:42.354 --> 00:41:45.303 Then why did it appear in the first place? Let's look at the beginning 648 00:41:47.643 --> 00:41:50.964 Now, I'll leave this as is, and press play 649 00:41:54.863 --> 00:42:00.105 Okay, if you check at this time, from this point to this point 650 00:42:00.105 --> 00:42:03.665 this is the result of the previous execution 651 00:42:03.665 --> 00:42:06.865 What happened when his HUD came out this wrong? 652 00:42:06.865 --> 00:42:10.684 We did respawn, then InitUI, in other words, when BeginPlay was called first 653 00:42:10.684 --> 00:42:13.534 there was no PlayerController, so the UI was displayed above 654 00:42:13.534 --> 00:42:16.364 Then PossessedBy happened 655 00:42:16.364 --> 00:42:17.864 Now, let's take a look below 656 00:42:17.864 --> 00:42:21.225 Now, the three below are newly executed 657 00:42:21.225 --> 00:42:25.165 and in this case, PossessedBy is created first 658 00:42:25.165 --> 00:42:26.615 How do I make a call? 659 00:42:26.615 --> 00:42:28.165 PossessedBy is called first 660 00:42:28.165 --> 00:42:32.464 and then InitUIWidget is called from the server 661 00:42:33.524 --> 00:42:34.903 The flow is different 662 00:42:36.324 --> 00:42:39.224 What should have happened the way it was supposed to? 663 00:42:39.224 --> 00:42:42.124 InitUI should have been called first 664 00:42:42.125 --> 00:42:44.125 InitUI should have been called first 665 00:42:44.125 --> 00:42:46.084 When you run it for the first time, what happens first? 666 00:42:46.084 --> 00:42:48.784 It started with PossessedBy and then InitUI 667 00:42:48.784 --> 00:42:51.143 So, it's like this, as if there's no problem 668 00:42:52.123 --> 00:42:56.123 So the lifecycle function comes in like this 669 00:42:56.123 --> 00:42:59.144 so we have to take this into consideration and implement it 670 00:42:59.964 --> 00:43:03.864 Now, let's take a look at an implementation that considers this 671 00:43:03.864 --> 00:43:06.643 Now, come to Visual Studio 672 00:43:06.643 --> 00:43:12.285 and go to BeginPlay, which is the InitUIWidget part 673 00:43:12.286 --> 00:43:18.103 At this time, I think we shouldn't just use one like this; we should verify it and move on 674 00:43:18.123 --> 00:43:23.563 Now, let's analyze when InitUIWidget is called 675 00:43:23.563 --> 00:43:29.563 This is not called unconditionally during BeginPlay, but when do you want it to be called? 676 00:43:29.563 --> 00:43:31.045 When the client is 677 00:43:33.084 --> 00:43:34.903 the main character 678 00:43:38.004 --> 00:43:39.483 I wish it would be called 679 00:43:41.125 --> 00:43:44.225 Why? What does InitUIWidget do? 680 00:43:44.225 --> 00:43:47.825 You only want to work on the UI when there is a PlayerController 681 00:43:47.825 --> 00:43:50.904 In other words, when there is a PlayerController 682 00:43:52.145 --> 00:43:55.365 On the client side, there is also a client on the server 683 00:43:55.365 --> 00:43:57.965 Where do you want this server's client done? 684 00:43:57.965 --> 00:44:02.645 At this Possessed, why? There was a problem running this on the server 685 00:44:02.645 --> 00:44:05.195 There's the problem when run it on server 686 00:44:05.195 --> 00:44:08.164 When you run it for the first time, Possessed happens first 687 00:44:08.164 --> 00:44:11.714 and then InitUI, right? 688 00:44:11.714 --> 00:44:14.804 But how about respawning if you run it again on the server? 689 00:44:14.804 --> 00:44:16.804 At that time, it becomes BeginPlay 690 00:44:16.804 --> 00:44:21.184 So, I want to handle this part where InitUIWidget is called 691 00:44:21.185 --> 00:44:25.664 on the server to run only in PossessedBy on the server 692 00:44:25.664 --> 00:44:27.964 It doesn't matter to the client right now 693 00:44:27.964 --> 00:44:32.644 But since the problem is on the server, InitUIWidget 694 00:44:32.644 --> 00:44:38.744 has to be executed in PossessedBy on the server, and then PossessedBy 695 00:44:38.744 --> 00:44:42.525 has to be like this, and then InitUI has to happen, right? 696 00:44:42.525 --> 00:44:44.983 Now, let's handle it like this 697 00:44:47.403 --> 00:44:54.044 Now, this part called InitUIWidget is called only by the client 698 00:44:54.044 --> 00:44:59.185 If this is locally controlled 699 00:44:59.185 --> 00:45:02.985 but since this locally controlled server also has a client 700 00:45:02.985 --> 00:45:06.805 its character will also be called 701 00:45:06.805 --> 00:45:10.555 But if there's no controller, it doesn't happen 702 00:45:10.555 --> 00:45:15.385 So this part with authority explicitly means that when this is false 703 00:45:15.385 --> 00:45:19.935 I will run the InitUIWidget in this BeginPlay 704 00:45:19.935 --> 00:45:23.765 that does not exist on the client 705 00:45:23.765 --> 00:45:24.615 And on the server 706 00:45:24.615 --> 00:45:29.083 it doesn't matter if you run it in BeginPlay on the client, right? 707 00:45:29.083 --> 00:45:31.783 Where on the server? 708 00:45:31.783 --> 00:45:35.064 It would be a good idea to do it this way so that PossessedBy can run it 709 00:45:35.884 --> 00:45:40.684 And in this PossessedBy, it only runs 710 00:45:40.684 --> 00:45:45.963 At the server 711 00:45:45.963 --> 00:45:50.113 So, in a function that runs only on the server 712 00:45:50.113 --> 00:45:53.385 InitUI is called after PossessedBy 713 00:45:53.385 --> 00:45:56.035 and then in BeginPlay, only on the client 714 00:45:56.035 --> 00:45:59.105 conditions are created 715 00:45:59.105 --> 00:46:01.155 and this has to be executed on the client as well 716 00:46:01.155 --> 00:46:03.185 Next, since it needs to run even when it is a server 717 00:46:03.185 --> 00:46:06.665 let's classify it into two categories 718 00:46:08.104 --> 00:46:10.854 I categorized these two 719 00:46:10.854 --> 00:46:13.983 into server and client and put them like this 720 00:46:13.983 --> 00:46:17.133 So where in PossessedBy? 721 00:46:17.133 --> 00:46:20.323 After the end 722 00:46:20.345 --> 00:46:24.425 the Begin here ends, and after PossessedBy, the controller is created here 723 00:46:24.425 --> 00:46:28.925 In this part, if it is IsLocallyControlled 724 00:46:28.925 --> 00:46:31.884 when it is the main character I am controlling internally 725 00:46:31.884 --> 00:46:36.034 please do InitUIWidget 726 00:46:36.034 --> 00:46:39.343 You can handle it like this Now, build 727 00:46:40.924 --> 00:46:43.474 Okay, I'll go and take another look 728 00:46:43.474 --> 00:46:45.985 This cannot be considered completely resolved 729 00:46:45.985 --> 00:46:49.025 since there may be other problems 730 00:46:50.445 --> 00:46:53.963 Do another one 731 00:46:53.963 --> 00:46:58.685 Now go to the server and bang, bang, bang 732 00:46:58.685 --> 00:47:01.085 Then, when the client plays again 733 00:47:01.085 --> 00:47:03.445 you can see that it appears normally 734 00:47:03.445 --> 00:47:06.495 Now, let's go ahead and shoot the character on the server from the client window 735 00:47:06.495 --> 00:47:08.844 Bang, bang, bang 736 00:47:08.844 --> 00:47:11.144 Then, when you press the retry button on the character on the server 737 00:47:11.144 --> 00:47:13.403 you can see the HPbar above your head like this 738 00:47:13.403 --> 00:47:15.484 but when you press the retry button, it is no longer visible 739 00:47:17.004 --> 00:47:21.225 Now, let's take a look at shooting a gun 740 00:47:21.225 --> 00:47:26.025 When you shoot a gun at the client, press the R key to reload 741 00:47:26.025 --> 00:47:30.164 Look at the UI Is the UI being updated properly now? 742 00:47:32.083 --> 00:47:35.033 This part is not being updated 743 00:47:35.033 --> 00:47:37.706 Should we just keep shooting now? No 744 00:47:37.706 --> 00:47:40.156 Then, let's go to the server and take a look 745 00:47:40.156 --> 00:47:43.126 I will also grab a gun and shoot on the server 746 00:47:43.126 --> 00:47:44.876 Is it working properly on the server? 747 00:47:44.876 --> 00:47:47.624 This isn't working either What's the problem? 748 00:47:50.283 --> 00:47:54.183 We keep calling this UI, InitUI 749 00:47:54.183 --> 00:47:59.484 Since it is called every time it dies, what is being done in this InitUI 750 00:47:59.484 --> 00:48:04.146 creates a new mainUI widget 751 00:48:04.146 --> 00:48:07.846 Then, add it to the viewport and print it on the screen 752 00:48:07.846 --> 00:48:11.096 In other words, there is already an existing UI 753 00:48:11.096 --> 00:48:14.323 but it is continuously overwritten on top of it 754 00:48:14.843 --> 00:48:17.543 It looks like this on the screen 755 00:48:17.543 --> 00:48:22.384 Let's say I already have a widget like this 756 00:48:22.386 --> 00:48:24.466 Like this, right? 757 00:48:24.466 --> 00:48:27.566 On top of this, when it respawns 758 00:48:27.566 --> 00:48:30.764 the player kills it, but on screen, it was blown away by destroying 759 00:48:30.764 --> 00:48:36.846 But this mainUI widget didn't blow up the mainUI 760 00:48:36.846 --> 00:48:40.506 So what else on top? Adding it again like this 761 00:48:41.163 --> 00:48:45.606 If you turn it off and on again, when it dies 762 00:48:45.606 --> 00:48:50.086 you put it back on top again, and so on 763 00:48:50.086 --> 00:48:54.486 So, if we look at this at the top, no matter how hard I try to delete this 764 00:48:54.486 --> 00:48:57.204 it still stays behind 765 00:48:57.204 --> 00:49:02.084 So it looks like it doesn't shrink, as if it doesn't exist 766 00:49:02.084 --> 00:49:06.326 By keep adding more and more, did you understand? 767 00:49:06.326 --> 00:49:12.406 Because of this problem, the existing afterimage remains even though the UI is normally reduced 768 00:49:12.406 --> 00:49:16.486 If there are remnants, what should I do? 769 00:49:16.486 --> 00:49:22.186 This problem is because it keeps creating the MainUI widget if it already exists 770 00:49:22.186 --> 00:49:26.524 If it already exists, we will take care of not creating it 771 00:49:28.723 --> 00:49:34.846 So, because this part is in NetTPSCharacter, it is a problem, why? 772 00:49:34.846 --> 00:49:39.406 This goes along with the lifecycle of NetTPSCharacter 773 00:49:39.424 --> 00:49:45.644 In what way? When NetTPSCharacter is created, the MainUI also exists 774 00:49:45.644 --> 00:49:50.604 Then, when NetTPSCharacter disappears, the reference to it disappears 775 00:49:50.604 --> 00:49:52.454 It's not that the object itself disappears from the world 776 00:49:52.454 --> 00:49:55.324 but the value of this variable that it has disappears 777 00:49:55.324 --> 00:49:57.774 Right? If this thing is reborn 778 00:49:57.774 --> 00:50:00.764 we must keep making it again because it has no value 779 00:50:00.764 --> 00:50:06.764 So, let's try to make this MainUI maintain the data in this variable 780 00:50:06.764 --> 00:50:09.764 instead of going back and forth 781 00:50:09.764 --> 00:50:15.504 So, where are you going to put this MainUI variable? I'm going to move it 782 00:50:15.504 --> 00:50:21.446 Now, we have the MainUI in NetTPSCharacter 783 00:50:21.446 --> 00:50:24.286 There are widget classes to use 784 00:50:24.286 --> 00:50:27.386 I will press Ctrl+C 785 00:50:27.386 --> 00:50:33.564 to import all of this into NetPlayerCharacter.h by pressing Ctrl+C 786 00:50:35.721 --> 00:50:38.421 I'll make it public and paste everything 787 00:50:39.423 --> 00:50:46.723 Next, I will save the ones that were originally in NetTPSCharacter 788 00:50:46.723 --> 00:50:49.823 Let's just leave the variable as is to make it easier to use 789 00:50:49.823 --> 00:50:51.146 The one above 790 00:50:51.146 --> 00:50:53.746 the MainUI widget, will not be received here 791 00:50:53.746 --> 00:50:58.326 but will be assigned to PlayerController 792 00:50:59.363 --> 00:51:03.363 Then, where do this move? 793 00:51:03.363 --> 00:51:07.964 At NetTPSCharacter.cpp file, and here's InitUIWidget 794 00:51:07.964 --> 00:51:12.646 ere, we are importing APlayerController into this PlayerController 795 00:51:12.646 --> 00:51:17.806 which changes our PlayerController to ALetPlayerController 796 00:51:18.606 --> 00:51:22.326 Okay? Change ALetPlayerController, and there is this if part 797 00:51:22.326 --> 00:51:25.644 I don't want to touch this logic part, so I'm putting it like this 798 00:51:25.644 --> 00:51:32.084 When there is a MainUI widget in PlayerController, I try to handle it like this 799 00:51:32.084 --> 00:51:34.734 Change it to the one in PlayerController 800 00:51:34.734 --> 00:51:38.524 Then below this, MainUI 801 00:51:38.524 --> 00:51:44.325 What if the mainUI on PC is 802 00:51:44.345 --> 00:51:48.921 nullptr, we will make this 803 00:51:48.921 --> 00:51:54.041 We're just adding a new value to the MainUI 804 00:51:54.041 --> 00:52:00.043 And how do we put the value in this MainUI as the Main UI on the PC? 805 00:52:00.043 --> 00:52:04.844 And here's the MainUI widget on the PC 806 00:52:05.444 --> 00:52:09.466 Then, the MainUI now has the MainUI on the PC 807 00:52:09.466 --> 00:52:12.466 And then, let's not change all of these main on the PC like this 808 00:52:12.466 --> 00:52:14.906 for ease of use 809 00:52:14.906 --> 00:52:18.256 put the MainUI on the PC as the MainUI 810 00:52:18.256 --> 00:52:22.163 Then, you can just write the following as it is 811 00:52:22.163 --> 00:52:24.413 so let's build it 812 00:52:24.413 --> 00:52:27.743 I'll turn off and turn on the Unreal Editor 813 00:52:29.363 --> 00:52:33.863 I should include this NetPlayerController as well 814 00:52:33.863 --> 00:52:37.844 I'll do #include at the top 815 00:52:37.844 --> 00:52:43.944 and add NetPlayerController.h here 816 00:52:46.324 --> 00:52:48.974 The Unreal Editor appears 817 00:52:48.974 --> 00:52:52.584 Double-click on the BPNetPlayerController to open it 818 00:52:55.164 --> 00:53:00.683 I'll search for MainUI, MainUIWidget 819 00:53:00.683 --> 00:53:04.533 The MainUI widget doesn't have a value right now 820 00:53:04.533 --> 00:53:08.363 so I'll choose the WBP Main UI 821 00:53:09.764 --> 00:53:14.866 BPNetPlayerController used the Main UI widget as the WBP MainUI 822 00:53:14.866 --> 00:53:18.646 Press Compilation, and then go back and run it again 823 00:53:20.783 --> 00:53:22.283 Play 824 00:53:22.283 --> 00:53:24.333 Then it's normal 825 00:53:24.333 --> 00:53:26.764 and now let's see if other users come out 826 00:53:26.764 --> 00:53:28.583 It comes out from far away 827 00:53:30.123 --> 00:53:35.884 I'll grab the gun and shoot the user It's dead now 828 00:53:35.884 --> 00:53:39.984 And then, I'll press the retry button in the client window 829 00:53:39.984 --> 00:53:42.404 Then it respawns again 830 00:53:42.404 --> 00:53:47.043 Okay, let's grab the gun and shoot the gun 831 00:53:47.043 --> 00:53:48.793 So what do you think? 832 00:53:48.793 --> 00:53:53.363 It keeps being added to the side of the screen right now 833 00:53:53.363 --> 00:53:54.783 Added to the side 834 00:53:56.404 --> 00:54:02.844 That means the UI has not yet been updated 835 00:54:04.204 --> 00:54:09.166 The part that resets the UI is missing when it's being answered 836 00:54:09.166 --> 00:54:11.946 So, let's go to InitUIWidget 837 00:54:14.084 --> 00:54:16.884 It comes down from InitUIWidget 838 00:54:16.884 --> 00:54:19.683 which is the problem when you initialize the bullet 839 00:54:19.683 --> 00:54:24.006 If it's already there, you should not do AddBullet 840 00:54:24.006 --> 00:54:26.656 Reset this bullet first 841 00:54:26.656 --> 00:54:29.266 And do it there, but it's missing 842 00:54:29.284 --> 00:54:33.464 And I see it now when the player dies when I play 843 00:54:35.106 --> 00:54:39.666 I will shoot, then HP is all out 844 00:54:39.666 --> 00:54:44.726 Even though you press Retry, it becomes respawned, but HP is not filling up again 845 00:54:44.726 --> 00:54:46.846 In other words, HP hasn't been reset 846 00:54:47.786 --> 00:54:51.686 So, what do we do 847 00:54:51.686 --> 00:54:54.303 when InitUIWidget is called 848 00:54:55.943 --> 00:54:59.443 There's initialization of HP 849 00:54:59.443 --> 00:55:02.923 and what do we do before we do this in this bullet initialization? 850 00:55:02.923 --> 00:55:10.764 We have to remove all the bullets After it's done, we have to add the UI 851 00:55:10.764 --> 00:55:13.264 and we will blow it out and fill it up again 852 00:55:13.264 --> 00:55:17.314 As we continue filling in what was already there 853 00:55:17.314 --> 00:55:20.163 we end up overflowing this content 854 00:55:20.163 --> 00:55:27.563 So, in the health reset section, I will set the HP to MaxHP 855 00:55:27.563 --> 00:55:32.983 and in the MainUI, I will set the value to 1.0 for HP 856 00:55:36.004 --> 00:55:37.144 Was it lowercase? 857 00:55:39.106 --> 00:55:43.166 If the HP in the MainUI is 1.0 858 00:55:43.166 --> 00:55:48.363 then when InitUIWidget is called, it will be initialized and come out in a fully populated state 859 00:55:48.363 --> 00:55:51.363 Then, let's remove all the bullets 860 00:55:51.363 --> 00:55:57.243 It has a RemoveAllAmmo function that we created in the MainUI 861 00:55:57.243 --> 00:56:01.793 Then, all UI will be completely removed to Bullet UI 862 00:56:01.793 --> 00:56:05.124 and initialized again like this 863 00:56:08.363 --> 00:56:13.763 It was InitUIWidget in the cpp file of the NetTPSCharacter 864 00:56:13.763 --> 00:56:16.803 Here, I reset the health 865 00:56:16.803 --> 00:56:21.544 and then added a part to remove all bullets, build again 866 00:56:26.264 --> 00:56:28.523 Let's go ahead and run it 867 00:56:30.644 --> 00:56:33.694 I'll grab the gun and shoot the opponent 868 00:56:33.694 --> 00:56:38.123 Then, if you look at the client window, the HP has decreased 869 00:56:38.123 --> 00:56:44.683 If you press retry, it will refill Then, the user will also shoot 870 00:56:44.683 --> 00:56:46.983 When you fire a gun, the number of bullets should decrease 871 00:56:46.983 --> 00:56:50.004 If it wasn't reduced because it overlapped earlier, I'll shoot it this time 872 00:56:50.004 --> 00:56:52.844 Then, you can check that it is decreasing normally 873 00:56:55.363 --> 00:57:00.413 If you shoot the server character, he dies, and if you press retry normally 874 00:57:00.413 --> 00:57:03.243 the UI is reset to normal 875 00:57:03.243 --> 00:57:06.043 After that, the gun is dropped here, so if you hold it and shoot it 876 00:57:06.043 --> 00:57:09.903 you can see that the UI is updated properly 877 00:57:10.363 --> 00:57:14.504 Now, we have analyzed the respawn and lifecycle function 878 00:57:21.204 --> 00:57:25.143 Let's wrap up what we learned in this lecture 879 00:57:25.143 --> 00:57:26.128 Implementing respawn logic Respawn Logic 880 00:57:26.128 --> 00:57:27.188 Added PlayerStart actor to handle spawn location Create an ANetPlayerController class that inherits APlayerController and implements respawn logic 881 00:57:27.188 --> 00:57:28.187 Added PlayerStart actor to handle spawn location Create an ANetPlayerController class that inherits APlayerController and implements respawn logic 882 00:57:28.188 --> 00:57:29.147 and restarting the player to respawn is done in the GameMode class 883 00:57:29.148 --> 00:57:30.107 The Retry button and respawn logic are connected in the UI displayed when the player dies 884 00:57:30.108 --> 00:57:30.927 Respawn problem and lifecycle function analysis 885 00:57:30.928 --> 00:57:31.827 Solved the UI problem when the server character respawns in the client window by utilizing the life cycle function 886 00:57:31.827 --> 00:57:36.787 The End