WEBVTT

00:06.980 --> 00:08.210
Welcome back.

00:08.330 --> 00:16.010
Now that we have inputs and we have callbacks that we can use to respond to those inputs, we can start

00:16.010 --> 00:17.810
implementing our abilities.

00:17.930 --> 00:22.910
But additionally, we can implement other capabilities for our character.

00:22.910 --> 00:30.020
And because this is a top down project, one important gameplay mechanic we should think about is Click

00:30.020 --> 00:30.740
to Move.

00:30.770 --> 00:39.110
This is a common feature of lots of top down games and we have a unique opportunity to figure out how

00:39.110 --> 00:40.940
to make this happen for our game.

00:41.540 --> 00:47.120
Now it just so happens that Unreal Engine provides a top down template project.

00:47.420 --> 00:54.110
This gives us a nice example of how you might go about implementing a basic top down.

00:54.110 --> 00:56.570
And in this project we have click to move.

00:56.570 --> 01:04.250
If I click on the ground, the character will move to the location I clicked on and if I click and hold,

01:04.280 --> 01:08.360
then the character will follow my mouse cursor.

01:08.450 --> 01:15.710
So this is a pretty good basic click to move, But the top down templates implementation of Click to

01:15.710 --> 01:18.610
Move has one important limitation.

01:18.620 --> 01:26.110
We can quickly discover that limitation by changing our number of players to two and changing the net

01:26.150 --> 01:33.080
mode from standalone to one of the other options such as play as listen server or play as client.

01:33.080 --> 01:39.440
If I choose one of those and press play now we'll have two characters in multiplayer.

01:39.740 --> 01:47.970
Now if I click and release, my character will not move to the location that I clicked on.

01:47.970 --> 01:51.320
But if I click and hold, it'll work just fine.

01:51.330 --> 01:56.400
Now I chose to play as client, meaning both of these are clients.

01:56.400 --> 02:02.130
If I click in the main viewport and release, the character will not move.

02:02.160 --> 02:06.390
It'll move a little bit, but when I release the mouse button, it stops moving.

02:06.390 --> 02:08.580
If I hold it down though, everything works.

02:08.580 --> 02:09.450
Okay.

02:09.960 --> 02:19.050
Now if I change this to play as listen server and press play, then one of my characters is the host.

02:19.140 --> 02:20.880
The one in the big window.

02:20.910 --> 02:23.220
The one in the small window is the client.

02:23.250 --> 02:28.320
Now the client can click and release and nothing happens.

02:28.320 --> 02:34.980
But if I click and release in the big window, well then I get that nice auto run behavior.

02:36.030 --> 02:44.370
And if I show it in two windows, you'll see that I can click and release and the server character auto

02:44.370 --> 02:44.760
runs.

02:44.760 --> 02:49.700
And it works in multiplayer, but if I click and release on the client, it does not work.

02:49.710 --> 02:52.120
So why is that?

02:52.140 --> 02:58.980
It's a little disappointing that the top down project doesn't give us fully working click to move behavior

02:58.980 --> 03:03.990
just out of the box, but we can find out why by looking at the code.

03:04.020 --> 03:13.470
Here's the top down template project and the movement is set up in the top down player controller and

03:13.470 --> 03:21.270
the top down player controller has some callbacks bound to input actions Using enhanced input.

03:21.300 --> 03:29.190
We have a set destination click action and we have a number of functions bound to it.

03:29.220 --> 03:34.350
We have one for started, triggered, completed and cancelled.

03:34.860 --> 03:39.490
Now for started and all we have here is stop movement.

03:39.490 --> 03:44.560
So it's in set on destination triggered which is called every frame.

03:44.560 --> 03:48.820
As long as the input is held down that we actually start doing stuff.

03:48.850 --> 03:55.690
The very frame that we begin clicking that left mouse button, all we get is this call to stop movement.

03:55.720 --> 03:57.070
What does that do?

03:57.100 --> 04:01.260
Well, we'll find out why as soon as we analyze the code here.

04:01.270 --> 04:05.320
So on set, destination triggered is fired every frame.

04:05.320 --> 04:10.420
As long as input is held down, you'll see that we have this member variable.

04:10.420 --> 04:18.220
It's a float called follow time and it's getting the delta seconds and adding that to follow time.

04:18.220 --> 04:23.560
So in other words, we have this running time that's incrementing with Delta time, every frame.

04:23.560 --> 04:30.820
Then it creates a hit result, creates a local Boolean set to false called hit successful and then gets

04:30.820 --> 04:32.590
the hit results under finger.

04:32.590 --> 04:39.970
If B is touch is true, this is for mobile, otherwise it just gets the hit result under the cursor

04:39.970 --> 04:42.070
and fills in that hit result.

04:42.100 --> 04:47.680
Now if there was a successful hit, it takes that hit result and caches that location.

04:47.680 --> 04:54.220
So wherever we click if we got a successful hit result under that cursor we store that world location

04:54.220 --> 04:57.310
in this cached destination vector.

04:57.490 --> 05:04.090
Now, after storing that location, we see the comments as move towards mouse pointer or touch where

05:04.090 --> 05:05.380
it gets the pawn.

05:05.380 --> 05:13.090
As long as it's not a null pointer, it calculates a direction by taking that cached destination and

05:13.090 --> 05:16.750
getting the controlled pawns actor location.

05:16.870 --> 05:23.200
And if we take the subtraction between these two vectors, we get a vector from the controlled pawn

05:23.200 --> 05:25.480
to the cached destination.

05:25.480 --> 05:27.610
It then calls get safe normal.

05:27.610 --> 05:33.280
So we have a normalized vector pointing in the direction that we want to run in.

05:33.280 --> 05:36.940
And then we call add movement input passing in that direction.

05:36.940 --> 05:38.760
Now this is called every frame.

05:38.770 --> 05:44.470
So as long as this function is being called, we're calling add movement input here.

05:44.470 --> 05:52.150
And this is called as long as input is being held down and add movement input moves the pawn through

05:52.150 --> 05:54.280
the Pawns movement component.

05:54.460 --> 05:55.110
Right?

05:55.150 --> 06:00.040
We can right click and go to the definition for add movement component.

06:00.040 --> 06:07.450
And as we can see, it takes whatever movement component is assigned to this pawn and it uses that to

06:07.450 --> 06:08.320
add movement.

06:08.350 --> 06:14.650
Now the cool thing about movement components is that they have built in replication capabilities already.

06:14.650 --> 06:20.740
So that's why if we're holding our mouse cursor down, we get replicated movement.

06:20.770 --> 06:25.750
We can hold the mouse cursor down on the client and the server sees that movement.

06:25.750 --> 06:29.650
Just to kind of show you that again, here's the client.

06:29.650 --> 06:33.520
And if I click and hold down, you can see that the movement looks good.

06:33.520 --> 06:38.080
It's smooth, everything works fine, but it's that auto run.

06:38.080 --> 06:43.600
If I click and release that auto run doesn't work, but it works on the server, I can click and release

06:43.600 --> 06:46.630
on the server and auto run works.

06:46.780 --> 06:51.040
So there must be something that is done differently in that case.

06:51.130 --> 06:58.600
Well, we have on set destination released and as we saw in setup input component here, this function

06:58.600 --> 07:03.850
is bound to our input action with completed for the trigger event.

07:03.850 --> 07:09.640
That means when we release our input, then this callback will be executed.

07:09.670 --> 07:15.550
Now we see that the comment says if it was a short press and we checked that follow time and see if

07:15.550 --> 07:17.710
it's less than a short press threshold.

07:17.710 --> 07:24.640
So there's some amount of time that we want to consider a short press because at the end of this function

07:24.640 --> 07:26.560
we see that we reset, follow time.

07:26.560 --> 07:32.770
So as we release the mouse button, that follow time is going to be reset to zero.

07:32.770 --> 07:34.870
But before that, we check to see if.

07:34.900 --> 07:36.520
It was a short press.

07:36.670 --> 07:42.370
Otherwise, we've been holding the mouse cursor down for some time, which means we've been doing this

07:42.370 --> 07:48.910
functionality here and set destination triggered, which is basically moving the pawn with add movement

07:48.910 --> 07:49.180
input.

07:49.210 --> 07:55.810
We see that this works in multiplayer just fine, but if it was a short press, we're handling movement

07:55.810 --> 07:57.080
in a different way.

07:57.100 --> 08:05.860
It's using UI, Blueprint Helper library calling simple move to location, passing in that cached destination.

08:05.860 --> 08:15.010
And it also handles spawning a system, a Niagara system at the location that cached destination location.

08:15.010 --> 08:20.110
And that system is what we see here with these little arrows.

08:20.110 --> 08:21.640
That's a Niagara system.

08:21.640 --> 08:27.120
We're spawning that right here as we call simple move to location.

08:27.130 --> 08:30.640
Now simple move to location is an AI function.

08:30.640 --> 08:33.520
It's in the AI Blueprint Helper library.

08:33.520 --> 08:35.120
And I moved.

08:35.120 --> 08:39.440
Two functions are not designed to be called from a client.

08:39.470 --> 08:43.370
They're meant to be called on the server for AI characters.

08:43.370 --> 08:51.350
So if you have an AI character controlled by the server, you can use simple move to location and if

08:51.350 --> 08:57.680
that AI character has its movement replicated, it'll replicate down to all clients, no problem.

08:57.680 --> 09:02.150
But for a human controlled character, this is not going to work.

09:02.180 --> 09:06.290
It works locally, but there's no multiplayer replication built in.

09:06.290 --> 09:10.760
It's not like calling add movement input on a movement component.

09:10.790 --> 09:14.180
This has all sorts of lag compensation built in.

09:14.180 --> 09:21.710
It has prediction, we can call it client side and we'll see our character move immediately and the

09:21.710 --> 09:25.520
server will receive that information through the movement component.

09:25.520 --> 09:28.310
It's all very nice and smooth but simple.

09:28.310 --> 09:31.040
Move to location is not going to do that.

09:31.040 --> 09:32.480
It's not going to work.

09:32.660 --> 09:35.570
So we can't use simple move to location.

09:35.570 --> 09:43.310
If we want to just click and have our character move automatically to a location and just expect that

09:43.310 --> 09:44.660
to work in multiplayer.

09:44.660 --> 09:51.050
And that's a shame because when we are using that functionality, we get some interesting pathfinding

09:51.050 --> 09:52.190
capabilities.

09:52.190 --> 09:55.460
Let's say that I want to click on the other side of this pillar.

09:55.460 --> 10:00.980
Well, if I do it on the server, we're using simple move to location that works on the server so I

10:00.980 --> 10:03.770
can click and it'll go around the pillar.

10:03.770 --> 10:05.930
It finds its way around it.

10:05.930 --> 10:07.880
It's using pathfinding.

10:08.360 --> 10:10.850
That's because there's a nav mesh bounds volume.

10:10.850 --> 10:15.950
If we press P, we can see it that's required for any of these.

10:15.980 --> 10:22.340
I move to type functions to work, but again, it's a shame that we can't just use it and expect this

10:22.340 --> 10:26.990
to work in multiplayer as we see the top down template doesn't even do this.

10:26.990 --> 10:31.490
We can click and nothing very exciting happens.

10:31.490 --> 10:36.730
We have to click and hold to move our clients in Multiplayer.

10:36.730 --> 10:40.840
So that's a limitation of the top down template.

10:40.840 --> 10:44.800
And that means we can't just use the top down template code.

10:44.800 --> 10:48.580
We have to do something a little bit more sophisticated than that.

10:48.790 --> 10:54.970
So what we've learned from this analysis is that the top down template uses simple move to location.

10:54.970 --> 11:02.260
If it was a short press, it does not work in multiplayer only AI controlled characters on the server

11:02.260 --> 11:04.480
or the hosting player.

11:04.510 --> 11:08.860
Now it does use add movement input if input is held down.

11:08.860 --> 11:14.830
And in that case this works in multiplayer as we've seen, and it requires constant input.

11:14.830 --> 11:21.280
In other words, we're calling add movement input every single frame passing in a movement direction.

11:21.280 --> 11:23.140
Now this works locally.

11:23.140 --> 11:29.830
If we're on a client and we see that movement on the server and it is indeed replicated to all other

11:29.830 --> 11:36.920
clients, even though we didn't test out that case, it does work with multiple other clients as well.

11:36.920 --> 11:41.390
So for Argus Project, how are we going to get the best of both worlds?

11:41.390 --> 11:46.670
How are we going to get Click to Move behavior where we can click and do a short press?

11:46.670 --> 11:52.850
In other words, click and release immediately and see our character move to that location.

11:52.850 --> 11:57.950
Even taking advantage of pathfinding, moving around obstacles and things like that.

11:57.950 --> 11:59.450
That's what we want to do.

11:59.810 --> 12:02.780
Well, we know that we must use add movement input.

12:02.810 --> 12:05.840
We have to do this on a per frame basis.

12:05.870 --> 12:12.800
As long as our character is moving, we have to constantly pass in the correct direction and add movement.

12:12.800 --> 12:16.610
Input will work in multiplayer if we can manage to do that.

12:17.270 --> 12:19.820
So we need a direction each frame.

12:19.970 --> 12:22.550
So let's think about how we can do this.

12:23.300 --> 12:26.210
We have our pawn and we have our mouse cursor.

12:26.330 --> 12:31.910
Now our pawn has a location and our mouse cursor has a world location.

12:31.910 --> 12:34.400
If we get the hit, result underneath it.

12:34.430 --> 12:41.390
Now if we have our character's location and the mouse cursor hit result location, this forms a straight

12:41.390 --> 12:44.400
line provided there are no obstacles in the way.

12:44.420 --> 12:51.500
So it's fairly easy to take these two locations and calculate a direction and then we can call add movement

12:51.500 --> 12:54.920
input every frame as long as we're supposed to be moving.

12:54.920 --> 13:00.320
And that's going to move our character in the correct direction, and that works in multiplayer.

13:00.350 --> 13:07.400
Now this is a pretty easy case to take care of, but there are complications in multiplayer.

13:07.400 --> 13:09.820
We may have things in the way.

13:09.830 --> 13:13.210
Let's say we have a pillar or some obstacle, right?

13:13.220 --> 13:20.210
And the straight line between our character and the mouse cursor world hit location would go through

13:20.210 --> 13:21.470
that obstacle.

13:21.470 --> 13:23.910
Now we can't just move in that direction.

13:23.910 --> 13:29.610
Our character is going to hit that wall and still continue trying to move through the wall and it's

13:29.610 --> 13:31.110
going to look very bad.

13:31.140 --> 13:33.120
We need some sort of pathfinding.

13:33.120 --> 13:39.150
We need a way to find points that will create a path for us that will go around the pillar.

13:39.360 --> 13:43.710
Now, there are functions that we can use to generate this type of path.

13:43.710 --> 13:49.790
That's not a problem, but this can result in some less than desirable behavior as well.

13:49.800 --> 13:57.480
You see, we can easily calculate a direction from our character's current location to the next point

13:57.480 --> 13:58.830
on this path.

13:59.220 --> 14:05.700
That's a straight line and we can move that character in that straight line until it reaches that second

14:05.700 --> 14:09.450
point, at which point the direction changes.

14:09.450 --> 14:15.210
And as soon as we've reached that second point, we change the character's direction and continue calling

14:15.240 --> 14:20.580
add movement input until we reach the next point, at which point the direction changes again.

14:20.760 --> 14:27.180
Now, this can result in an abrupt change in orientation and that can be less than desirable.

14:27.210 --> 14:33.180
Consider the case where we're looking down on our character from directly above and we can see the top

14:33.180 --> 14:38.790
of the obstacle that's in the way, and we generate a path to find our way around it.

14:38.880 --> 14:46.560
Now our direction will be from the character's location to that second point in the path, and that's

14:46.560 --> 14:47.460
a straight line.

14:47.460 --> 14:52.170
And as soon as we hit that point, the change in orientation is abrupt.

14:52.200 --> 14:55.140
We might even see it appear to snap.

14:55.290 --> 15:01.470
And as soon as we reach the next point, our change in direction is abrupt yet again, and this can

15:01.470 --> 15:03.060
be less than desirable.

15:03.300 --> 15:08.940
It would be nice if we could have a smooth curve for our path.

15:08.970 --> 15:10.560
Now this is possible.

15:10.560 --> 15:18.780
We can create a spline out of a system of points that we've generated and a spline is going to smooth

15:18.780 --> 15:22.260
itself out and have a nice round curve like this.

15:22.710 --> 15:30.900
Now, if we can generate a spline from some path points, then we have the ability to get the forward

15:30.900 --> 15:38.730
direction on the spline at any point and it's always going to be tangent to that spline curve.

15:38.760 --> 15:43.590
So as we're moving along the spline, that direction can be smooth.

15:43.620 --> 15:45.780
We'll never have an abrupt change.

15:45.780 --> 15:53.410
We'll always have a smooth, continuous change in direction as we're moving along the spline.

15:53.430 --> 16:01.420
So this is a nice solution to this problem if we can manage to generate a path to move along.

16:01.440 --> 16:09.150
We can create a spline that will be a nice smooth curve that will allow us to navigate around obstacles

16:09.150 --> 16:10.560
in a smooth manner.

16:10.710 --> 16:16.310
And that's the approach I'd like to take for our click to move mechanic in this game.

16:16.320 --> 16:21.420
So we'll get started with implementing this method in the next video.

16:21.720 --> 16:22.870
I'll see you soon.
