Welcome back.
Now that we have inputs and we have callbacks that we can use to respond to those inputs, we can start
implementing our abilities.
But additionally, we can implement other capabilities for our character.
And because this is a top down project, one important gameplay mechanic we should think about is Click
to Move.
This is a common feature of lots of top down games and we have a unique opportunity to figure out how
to make this happen for our game.
Now it just so happens that Unreal Engine provides a top down template project.
This gives us a nice example of how you might go about implementing a basic top down.
And in this project we have click to move.
If I click on the ground, the character will move to the location I clicked on and if I click and hold,
then the character will follow my mouse cursor.
So this is a pretty good basic click to move, But the top down templates implementation of Click to
Move has one important limitation.
We can quickly discover that limitation by changing our number of players to two and changing the net
mode from standalone to one of the other options such as play as listen server or play as client.
If I choose one of those and press play now we'll have two characters in multiplayer.
Now if I click and release, my character will not move to the location that I clicked on.
But if I click and hold, it'll work just fine.
Now I chose to play as client, meaning both of these are clients.
If I click in the main viewport and release, the character will not move.
It'll move a little bit, but when I release the mouse button, it stops moving.
If I hold it down though, everything works.
Okay.
Now if I change this to play as listen server and press play, then one of my characters is the host.
The one in the big window.
The one in the small window is the client.
Now the client can click and release and nothing happens.
But if I click and release in the big window, well then I get that nice auto run behavior.
And if I show it in two windows, you'll see that I can click and release and the server character auto
runs.
And it works in multiplayer, but if I click and release on the client, it does not work.
So why is that?
It's a little disappointing that the top down project doesn't give us fully working click to move behavior
just out of the box, but we can find out why by looking at the code.
Here's the top down template project and the movement is set up in the top down player controller and
the top down player controller has some callbacks bound to input actions Using enhanced input.
We have a set destination click action and we have a number of functions bound to it.
We have one for started, triggered, completed and cancelled.
Now for started and all we have here is stop movement.
So it's in set on destination triggered which is called every frame.
As long as the input is held down that we actually start doing stuff.
The very frame that we begin clicking that left mouse button, all we get is this call to stop movement.
What does that do?
Well, we'll find out why as soon as we analyze the code here.
So on set, destination triggered is fired every frame.
As long as input is held down, you'll see that we have this member variable.
It's a float called follow time and it's getting the delta seconds and adding that to follow time.
So in other words, we have this running time that's incrementing with Delta time, every frame.
Then it creates a hit result, creates a local Boolean set to false called hit successful and then gets
the hit results under finger.
If B is touch is true, this is for mobile, otherwise it just gets the hit result under the cursor
and fills in that hit result.
Now if there was a successful hit, it takes that hit result and caches that location.
So wherever we click if we got a successful hit result under that cursor we store that world location
in this cached destination vector.
Now, after storing that location, we see the comments as move towards mouse pointer or touch where
it gets the pawn.
As long as it's not a null pointer, it calculates a direction by taking that cached destination and
getting the controlled pawns actor location.
And if we take the subtraction between these two vectors, we get a vector from the controlled pawn
to the cached destination.
It then calls get safe normal.
So we have a normalized vector pointing in the direction that we want to run in.
And then we call add movement input passing in that direction.
Now this is called every frame.
So as long as this function is being called, we're calling add movement input here.
And this is called as long as input is being held down and add movement input moves the pawn through
the Pawns movement component.
Right?
We can right click and go to the definition for add movement component.
And as we can see, it takes whatever movement component is assigned to this pawn and it uses that to
add movement.
Now the cool thing about movement components is that they have built in replication capabilities already.
So that's why if we're holding our mouse cursor down, we get replicated movement.
We can hold the mouse cursor down on the client and the server sees that movement.
Just to kind of show you that again, here's the client.
And if I click and hold down, you can see that the movement looks good.
It's smooth, everything works fine, but it's that auto run.
If I click and release that auto run doesn't work, but it works on the server, I can click and release
on the server and auto run works.
So there must be something that is done differently in that case.
Well, we have on set destination released and as we saw in setup input component here, this function
is bound to our input action with completed for the trigger event.
That means when we release our input, then this callback will be executed.
Now we see that the comment says if it was a short press and we checked that follow time and see if
it's less than a short press threshold.
So there's some amount of time that we want to consider a short press because at the end of this function
we see that we reset, follow time.
So as we release the mouse button, that follow time is going to be reset to zero.
But before that, we check to see if.
It was a short press.
Otherwise, we've been holding the mouse cursor down for some time, which means we've been doing this
functionality here and set destination triggered, which is basically moving the pawn with add movement
input.
We see that this works in multiplayer just fine, but if it was a short press, we're handling movement
in a different way.
It's using UI, Blueprint Helper library calling simple move to location, passing in that cached destination.
And it also handles spawning a system, a Niagara system at the location that cached destination location.
And that system is what we see here with these little arrows.
That's a Niagara system.
We're spawning that right here as we call simple move to location.
Now simple move to location is an AI function.
It's in the AI Blueprint Helper library.
And I moved.
Two functions are not designed to be called from a client.
They're meant to be called on the server for AI characters.
So if you have an AI character controlled by the server, you can use simple move to location and if
that AI character has its movement replicated, it'll replicate down to all clients, no problem.
But for a human controlled character, this is not going to work.
It works locally, but there's no multiplayer replication built in.
It's not like calling add movement input on a movement component.
This has all sorts of lag compensation built in.
It has prediction, we can call it client side and we'll see our character move immediately and the
server will receive that information through the movement component.
It's all very nice and smooth but simple.
Move to location is not going to do that.
It's not going to work.
So we can't use simple move to location.
If we want to just click and have our character move automatically to a location and just expect that
to work in multiplayer.
And that's a shame because when we are using that functionality, we get some interesting pathfinding
capabilities.
Let's say that I want to click on the other side of this pillar.
Well, if I do it on the server, we're using simple move to location that works on the server so I
can click and it'll go around the pillar.
It finds its way around it.
It's using pathfinding.
That's because there's a nav mesh bounds volume.
If we press P, we can see it that's required for any of these.
I move to type functions to work, but again, it's a shame that we can't just use it and expect this
to work in multiplayer as we see the top down template doesn't even do this.
We can click and nothing very exciting happens.
We have to click and hold to move our clients in Multiplayer.
So that's a limitation of the top down template.
And that means we can't just use the top down template code.
We have to do something a little bit more sophisticated than that.
So what we've learned from this analysis is that the top down template uses simple move to location.
If it was a short press, it does not work in multiplayer only AI controlled characters on the server
or the hosting player.
Now it does use add movement input if input is held down.
And in that case this works in multiplayer as we've seen, and it requires constant input.
In other words, we're calling add movement input every single frame passing in a movement direction.
Now this works locally.
If we're on a client and we see that movement on the server and it is indeed replicated to all other
clients, even though we didn't test out that case, it does work with multiple other clients as well.
So for Argus Project, how are we going to get the best of both worlds?
How are we going to get Click to Move behavior where we can click and do a short press?
In other words, click and release immediately and see our character move to that location.
Even taking advantage of pathfinding, moving around obstacles and things like that.
That's what we want to do.
Well, we know that we must use add movement input.
We have to do this on a per frame basis.
As long as our character is moving, we have to constantly pass in the correct direction and add movement.
Input will work in multiplayer if we can manage to do that.
So we need a direction each frame.
So let's think about how we can do this.
We have our pawn and we have our mouse cursor.
Now our pawn has a location and our mouse cursor has a world location.
If we get the hit, result underneath it.
Now if we have our character's location and the mouse cursor hit result location, this forms a straight
line provided there are no obstacles in the way.
So it's fairly easy to take these two locations and calculate a direction and then we can call add movement
input every frame as long as we're supposed to be moving.
And that's going to move our character in the correct direction, and that works in multiplayer.
Now this is a pretty easy case to take care of, but there are complications in multiplayer.
We may have things in the way.
Let's say we have a pillar or some obstacle, right?
And the straight line between our character and the mouse cursor world hit location would go through
that obstacle.
Now we can't just move in that direction.
Our character is going to hit that wall and still continue trying to move through the wall and it's
going to look very bad.
We need some sort of pathfinding.
We need a way to find points that will create a path for us that will go around the pillar.
Now, there are functions that we can use to generate this type of path.
That's not a problem, but this can result in some less than desirable behavior as well.
You see, we can easily calculate a direction from our character's current location to the next point
on this path.
That's a straight line and we can move that character in that straight line until it reaches that second
point, at which point the direction changes.
And as soon as we've reached that second point, we change the character's direction and continue calling
add movement input until we reach the next point, at which point the direction changes again.
Now, this can result in an abrupt change in orientation and that can be less than desirable.
Consider the case where we're looking down on our character from directly above and we can see the top
of the obstacle that's in the way, and we generate a path to find our way around it.
Now our direction will be from the character's location to that second point in the path, and that's
a straight line.
And as soon as we hit that point, the change in orientation is abrupt.
We might even see it appear to snap.
And as soon as we reach the next point, our change in direction is abrupt yet again, and this can
be less than desirable.
It would be nice if we could have a smooth curve for our path.
Now this is possible.
We can create a spline out of a system of points that we've generated and a spline is going to smooth
itself out and have a nice round curve like this.
Now, if we can generate a spline from some path points, then we have the ability to get the forward
direction on the spline at any point and it's always going to be tangent to that spline curve.
So as we're moving along the spline, that direction can be smooth.
We'll never have an abrupt change.
We'll always have a smooth, continuous change in direction as we're moving along the spline.
So this is a nice solution to this problem if we can manage to generate a path to move along.
We can create a spline that will be a nice smooth curve that will allow us to navigate around obstacles
in a smooth manner.
And that's the approach I'd like to take for our click to move mechanic in this game.
So we'll get started with implementing this method in the next video.
I'll see you soon.