Welcome back.
So we have the ability to launch fireballs, but we have an issue.
We don't have any way to guide the fireball.
We don't know which direction to send it off in.
If we click over here.
Our fireball still goes off into the distance that way.
So that's a problem.
We also would like a direction to orient the character towards.
So I think that data that we want is basically the data of the hit result under the cursor when we click.
Now we can create an ability task that can get this data for us.
So here in GA Firebolt, as soon as we activate the ability, we could get that hit results under the
cursor or at least the location associated with it and use that here in the ability.
So we're going to make an ability task that can get that data for us and then we're going to see how
this works in multiplayer and whether or not we need to make some changes to make this work for a multiplayer
game.
So first we're going to make a new ability task class.
I'm going to go ahead and close out of some of these tabs.
I think I'll go ahead and leave the Firebolt open and we'll go into C plus plus classes.
Aura, public ability system.
And we'll make a class right here.
And I'm going to select all classes and search for ability task.
So here we have ability task notice it's based on the gameplay task class and also notice that the engine
has a number of ability tasks already built in.
So we're going to make our own and I'm going to put this in ability system slash ability tasks and I'm
going to name this target data under mouse.
So this ability task will be concerned with getting data related to that hit results under the mouse
cursor.
Now notice we're calling this target data under mouse and gas target data means something specific.
So this is a foreshadowing to a topic that we're going to get into very shortly.
But for now, let's just create our ability task.
I'm going to go ahead and close the editor and we'll let Ryder populate these folders here.
And I'm going to keep Aura Projectile spell open, but I'm going to open target data under Mouse.
There's the CP and here's the header file.
Okay.
Now we already took a look at play montage and wait and we saw that it had this static function that
created an instance of this particular class and returned it.
We're going to follow that pattern here.
So for target data under Mouse, we're going to make a static function.
It's going to be public.
So here's our public section and we're going to make a static function that returns a new target data
under mouse pointer.
So we're going to call this function create target data under mouse.
Now this function is going to receive the owning gameplay ability and that's pretty typical for these
ability tasks they take in a new gameplay ability.
And that's a pointer.
And we call this owning ability.
And whenever we call this function from within a gameplay ability, this owning ability parameter,
we would like passed in by default and we would like it to be the self.
In other words, the this pointer.
But in blueprint it's called self.
That's the current ability that's running this gameplay task.
So we're going to have a new function macro with a number of specifiers here.
So for one, it's going to be blueprint callable.
Two, it's going to have a category.
And for the category we're going to put it in ability pipe symbol tasks.
So it's an ability task.
Now in order to make this owning ability default to the self, we're going to use the meta specifiers.
So meta equals and a new set of parentheses and we're going to default to self, but we're also going
to hide the pin for owning ability so we can use hide pin equals and we specify owning ability.
We also use default to self specifying owning ability.
So by default it'll be defaulted to the self.
And this function is internal.
Right.
We're exposing this to the event graph, but this is a special internal function.
It's really just a factory that creates an instance of this task and returns it.
So what we're going to do in the meta parentheses here is we're going to say blueprint internal use
only equals true.
Now, this is called create target data under mouse.
If we want our blueprint node to have a different name, we can use a display name here in the meta
parentheses.
Let's go ahead and do that.
Let's say display name.
And that'll be equal to I'll put a comma after the quotes and we'll say target data under mouse.
So now there will be a display name as well.
So lots of new function specifiers here.
That's pretty typical for these.
So let's go ahead and implement the function.
Create a definition for it here.
And here's what we typically do.
We create a new object of type U target data under mouse.
So I'm going to copy this.
Type paste it there and we can call it anything.
It's a local pointer variable.
We're going to call this my obj for object and to create a new ability task, we use new ability task.
It's a template.
It requires the type.
We're going to make a new target data under mouse.
And when we create a new one of these, it's going to require the gameplay ability that owns it.
So we pass in the owning ability here.
Now the name is optional.
It defaults to an empty F name, so we're just going to leave it out.
And now that we've created a new object, we can simply return it here, return my obj.
Now, if we wanted to, we could set any number of variables on our target data under mouse and we could
make this function here, take in any number of input parameters and we could get those passed in from
the blueprint node and we can set those values right here on this object.
I'm just going to have none so we can see what this looks like.
If we attempt to create one of these from within our ability.
So why don't we go ahead and compile and launch the editor and try to create a target data under mouse
node in our gameplay ability?
Okay, so let's get that game play ability open and right at the beginning, as soon as we activate
the ability, I'm going to right click right here and type in target data under mouse, it's under ability
tasks.
So here it is.
Let's click on that and it's pretty bare, right?
But notice it has the little clock right there.
That means it's a latent node or an asynchronous node.
Now all it does so far is just return the instance of the target data under mouse object.
That's all it does.
And we can't really do much with that, can we?
Because it's a pretty empty class.
We can get its name, we can call get object name.
And we can print it to the screen.
That's pretty much the only thing we can do.
So let's just do it.
Print string, passing in that name, and let's go ahead and just click.
And it's a target data under mouse object.
Okay, so that's pretty boring.
But what is this task supposed to do?
It's supposed to get the target data under the mouse, right?
So I'll go ahead and hook that up.
But we need this to return something.
We need it to get some data.
So let's continue our work on this.
I'm going to close and save all, and this is now an asynchronous blueprint node, which means it can
have multiple output execution pins.
And the way that we create those multiple output execution pins is by broadcasting delegates.
So any output execution pin needs to exist as a delegate variable on this class.
So we can create a new delegate, we can declare one up here.
So I'm going to declare one.
I'm going to use declare dynamic multicast.
Delegate and I'm going to use one param because I'd like to broadcast the value of the mouse cursor
location.
Just an f vector.
Why not?
I'm going to call this delegate f mouse target data delegate.
We could also call it actually signature for the type and we're going to have it broadcast a const F
vector called data and we'll make a variable of this type.
It's going to be right here in the public section and we're going to call it valid data, and we'll
make this a new property with blueprint.
Assignable, not blueprint callable blueprint, assignable.
And this will now become an output execution pin on the node.
So now that we have this delegate, let's just compile and run the engine.
I want to see how this changes the node.
And it looks like I have a typo.
I forgot a comma.
So these types need to be separated from the name of that parameter with a comma.
For dynamic multicast delegates.
Okay, now let's compile and launch.
Okay.
Opening our ability.
Now look at this.
Not only does our node have more stuff on it, it has a valid data and it also has this data which is
an F vector.
It's a vector type by reference.
Perfect.
Now, this is never going to be executed.
Why?
Because the only way to execute this is to broadcast this delegate.
That's how these get executed.
Kind of cool, right?
So why don't we broadcast a value As soon as we broadcast valid data, then data will have some valid
data.
So the last thing I'd like to do in this video is for valid data to be broadcast and for data to give
me the location under the mouse.
Now I'm going to close the editor saving all.
And in order to do that, we have to override the ability tasks, activate function, and as soon as
this node is executed, ID activate will be called.
Now this is a private function, so we'll make a private section and this is virtual.
We're going to say virtual void activate override.
And here in Activate, let's generate the definition and we don't have to call super Activate because
if we did that would call the ability tasks activate and if we go to the ability task by the way here's
the new ability task.
If we find activate.
If we search for it, it won't be there.
But if we go all the way into the gameplay task class.
Here's virtual void activate.
So as you can see, it's not even overridden in ability task.
Now here in gameplay task CP if we search for Activate, we'll see here that it just does you vlog.
Vlog.
Basically performing a log and nothing else.
So we really we don't care about that.
Go ahead and call super if you want to, but it's not going to do much.
So I'm going to close gameplay task.
I'm going to close ability task and back to our class here is Activate.
Now we just want to get data and broadcast our delegate valid data.
So once we get the mouse hit location under the mouse and broadcast it, then we can go back into our
blueprint and see if it's working by drawing a debug sphere or something like that.
So this is going to be your quest.
I'd like you to get that valid data and broadcast it.
So this is going to involve getting the location under the mouse cursor.
There should be something you know how to do.
And once you get that broadcast, that valid data delegate and from within your ability, get that data
and draw the debug sphere at the location.
So do that from within our firebolt ability.
So pause the video and conquer this quest now.
All right.
So from here in Activate, we need to figure out how to get that player controller.
That's how we get the hit results under the mouse cursor.
So we need to figure out how we can get that from within the activate function.
So this might have been a bit of a stumping point for you, but remember, ability tasks know which
ability they belong to.
In fact, they have an ability member variable and from ability we can access the actor info with get
current actor info.
Now get current actor info returns gameplay ability actor info and that has a number of variables on
it, including the player controller so we can get it from that.
Now the player controller here is a weak object pointer, so we're going to need to call, get on that.
So this is how we can get the player controller and we can store this in an a player controller pointer.
And I'm just going to call it PC.
Now we can call get hit result on the cursor, which takes an F hit result.
So this will be called, we'll call it cursor hit and we'll take the player controller and call get
hit Results under cursor specifying a E collision channel.
I'm just going to trace against visibility.
We're going to not trace complex.
We're going to trace only against simple collision and pass in that cursor hit.
So now we should have a hit result.
So now all that's left is to broadcast that data, the location of this hit result.
And our delegate is valid data so we can call broadcast on it using our cursor hit dot location or impact
point.
Either of those will work.
So as soon as we activate this ability, then we're going to broadcast that delegate, which means that
valid data execution pin will be executed.
So it just so happens that as soon as we activate the ability, that valid data pin will be executed.
But it doesn't have to be right away.
This is an asynchronous task.
So depending on the gameplay mechanics for some ability tasks we may wish to broadcast those delegates
later in time.
But for this case, we're broadcasting right away.
So let's run in debug mode.
And we'll open up our ability.
Here's target data under mouse and we know that as soon as we reach this node, activate will be called
and valid data will be broadcast right away.
So we're not going to use this node.
We're going to use valid data because as soon as valid data is broadcast, then we should have some
valid data in that data vector.
So I'm going to draw a debug sphere or debug box or anything else really, right?
I'm going to do a sphere.
So I'm going to move these up out of the way and we're going to draw it at the location of data.
I'll give it a radius of 100 line color white.
That's fine.
For duration.
I'll give it 20s and we can test this out.
All right.
Saving all pressing play.
Clicking.
And actually, I have to click on an enemy, right?
In order to activate it.
And I can only activate it once unless I end the ability.
Now I can activate it as many times as I want.
And as long as I'm hovering over an enemy, then boom.
We're getting our target data, so this is great.
But remember, we mentioned we're going to see its limitations, particularly when it comes to multiplayer.
So if I increase the number of players to two and press play, let's see what happens.
If I do this on a client, I'm going to click and look at this on the client.
I clicked way over here.
It's probably hard to see, but I clicked way over here on an enemy and I got my debug sphere in the
correct location.
I can do it for all of these enemies here and it looks great on the client locally, but look at the
server on the server.
It's just showing the debug sphere right here for some reason and I have the sneaking suspicion that
that is the world origin and that would mean that data just has all zeros in it.
It's not being set.
So that's a problem right now.
It makes sense that the server wouldn't know where we're clicking because that's something we're doing
locally.
That's something we're doing here on the client and only the client knows where its mouse cursor is
because that's not data that's being sent up to the server.
If we want the server to know where we're clicking, we have to tell the server where we're clicking.
And so that's going to involve more concepts that we have to learn.
So we've seen the limitations of our new ability task and how yes, we can get valid data and yes,
we can draw a debug sphere for it locally, but the server does not have valid data.
It's not getting that specific piece of information.
Right.
So a couple things are happening here.
Activate ability is being executed locally on the client and because abilities are replicated, then
activate ability is being called on the server as well.
But on the server we're getting this delegate broadcast even though we don't really know where that
mouse cursor is.
So our next steps are to figure out how we can get data from the client up to the server.
And specifically we want that mouse cursor location sent up to the server.
How do we do that and how do we do it in a way that will work reliably?
That's what we're going to do next.
So I'll see you soon.