Welcome back.
Now this section is my favorite.
And this is where it's really going to start getting fun because the abilities are where it's at.
These are the heart of the gameplay ability system.
And while yes, we've already created several abilities, I'm really interested in these abilities.
My spells I want to make more than just the Firebolt, because really, this is the only spell that
we've created and we're going to make more.
But before we make more spells like implementing that electrocute spell we created, I also would like
to make leveling up our abilities meaningful.
You see, all we're doing when we spend a point on an ability is we're making it do more damage.
So if I go and, you know, level up to level two and I have my Firebolt selected and I spend a point
on it, yeah, I'm changing a couple things here that depend on the level, but that's it though.
We're just changing numerical values.
We're changing the math involved.
We're not changing anything else.
When I launch my level two Firebolt, it looks exactly the same as a level one Firebolt.
In this section, we're going to make leveling up, have a cosmetic element to it as well.
So if I'm launching a level two Firebolt look, my description even says launch is two bolts of fire.
Right?
And I want to launch two bolts of fire.
I want to deliver on this promise.
And for that reason, we're going to need a bit of a different function for our projectile.
Now, we don't want to mess with the spawn projectile function because our slingshot goblins are using
it, and it works just fine for the slingshot goblin.
It's causing damage, right?
And it's launching a single rock.
That's all it needs to do.
But for my Firebolt, it depends on the level.
And we're going to change some things with our Firebolt.
Now I want you to imagine for a second what this is going to look like.
If I press play and I pause and go ahead and detach and I look at aurra from above, I want Aurra to
launch a Firebolt.
Let's just say it's level one.
If it's level one, I want to launch a Firebolt relatively forward, or at least forward in a sense
of where we're aiming.
Where I clicked over here, I want to launch that fireball towards that direction.
Okay, now let's just say that I've leveled up my Firebolt to level two.
Well, now I'm going to have a spread.
Remember when we calculated random spawn locations for our shaman when our shaman was summoning minions?
We're going to make an algorithm similar to that, but it's going to have some differences.
For one, we're going to have a spread, and if our ability is level two, we're going to launch two
Firebolts spread out.
Now that means we're going to miss.
Right?
Well, not exactly, because our projectiles have projectile movement components.
And those can be homing missiles.
They can have a homing target.
And if we click somewhere I want to home in on it.
So even though these two firebolts are going to launch out into seemingly different directions, they're
going to come back in on what we clicked on.
They're going to home in on their target.
And if we click on a enemy, for example, then they can even home in on a moving actor as well.
So that's something we're going to implement on our firebolts.
They're now going to be homing projectiles.
Now, the first step to this is the math behind that spread.
We know an algorithm that's sort of similar to what we want, but we're going to see that this will
have a few differences as well.
And when doing these algorithms, it's always nice to be able to visualize with debug shapes and arrows
and things.
And so that's where we're going to start in this video is we're going to make a function that can start
visualizing the spread for spawning these projectiles.
And that's going to be in our Firebolt class because this is very much unique to the Firebolt.
And if I go ahead and just close all tabs.
And just open up my aura Firebolt spell.
Well, so far all it does is set the description text.
But now we're going to have a more interesting function to call.
We're going to have spawn projectiles.
Now yes, we are inheriting from Aura projectile spell which indeed has a spawn projectile function.
Right.
But this is again what we said earlier about this function is we don't really want to mess with it because,
well, our slingshot enemies are using it and it works just fine for slingshot enemies.
So we can maybe take a look at it to see how we did some things.
But Aura Firebolt is going to have a brand spanking new spawn projectile spell, and we can call it
spawn projectiles with an S because it's going to spawn multiple.
So we'll call this spawn projectiles with an S, and let's look at our projectile spell.
We'll see that it needs a projectile target location.
Yes it does.
We see that it takes a socket tag because it needs to know what socket to spawn our projectile from,
and we override the pitch.
If this is true with a pitch override, we could do those same things in our projectile if we'd like.
Those are all things that we could use, but we're also going to need a homing target, which is an
actor.
In the case where we click on an actor, in the case where we click actually on an actor specifically,
that's an enemy we have interfaces and such that we can use to check.
So if we click on something that's, say, implementing the combat interface, for example, then we
can set that as our homing target for our projectiles.
Otherwise we'll have to do something else.
So in addition to these parameters we're also going to need that actor for an optional homing target.
So we'll start by adding a const f vector called projectile target location a socket tag, and perhaps
override pitch and pitch override.
And we'll stick those in for spawn projectiles.
But it's also going to get another input of type A actor pointer.
And this will be the homing target.
So let's go ahead and generate the definition for this.
And spawn projectiles is going to be simple at first.
Now we're going to start doing some math.
And we can start by getting the locations and directions of things.
And if we look at our projectile spell, we have a pretty good example of things that we're going to
want to start with.
For one, we're going to check to see if we're on the server.
If we're not, we'll return.
And then we see that we get the socket location using the interface function, get combat socket location
passing in that socket tag.
If there is a pitch override, we override the pitch of the rotation that we got from doing some vector
math from the socket location to the target.
Now we have this rotation, right.
We can copy all this and we can paste it here in Aura Firebolt.
Now what I'd like to do is just draw a debug arrow in the direction of this rotation.
Right.
I'm going to use U Kismet system library draw debug arrow for that.
So for the world context object I'll get the avatar and pass that in.
And we need a start.
The line start will be at the socket location.
The line end is going to be at socket location plus some vector.
Well I'm just going to get rotation dot vector and multiply that by say 150, maybe just 100.
Actually for the arrow size we'll just put a five for the linear color f linear color green.
Should suffice now for the duration.
Let's give it a good 60s I want to be able to see it.
In fact, I'm going to go two minutes 100 and 20s.
And for thickness I'll put a five as well.
Now with that we can test out how this works.
Of course spawn projectiles is going to need to be blueprint callable.
So before we compile and launch we're going to make it blueprint callable.
And we can go ahead and see our debug arrow.
We're starting small.
Just a single debug arrow for now.
And in the engine.
We're going to open the Firebolt and find where we're spawning the projectile way down here at the end.
And we're going to break this node and set it aside, because now we're going to try spawn projectiles
with an S and we're going to hook in.
A projectile target location and a socket tag.
That's what we needed for spawn projectile.
So we'll go ahead and we'll duplicate these nodes right here.
We'll pass those in.
We're not going to override the pitch for now, so we'll leave that as is.
We're not using the homing target yet, so all we have now if we click.
With shift.
Then we'll see that arrow.
And I'm going to go ahead and pause and eject.
And I'll take a look at that arrow.
The thickness is a little obnoxious.
I think that that's a little bit too much.
But at least we can see what we're working with here.
Right.
And we clicked on the ground over there.
And we see that that arrow is pointing downward.
That's good information to know.
So for draw debug arrow for the thickness I'm just going to put this down to a smaller value.
We'll try to and I also want to see if I have.
A different color.
I know I can custom make a color.
I'm going to actually make the forward vector white to distinguish it from other vectors.
Okay, so we saw that we're not overwriting the pitch.
And that means our arrow is pointed downward a bit.
I think I'll go ahead and override the pitch with a pitch of zero for now.
But we can pass in a different value.
But this line can be a single line.
It's distracting me with how many lines it's taking up.
So with that, we have our pitch override line here.
Okay.
So we have the forward vector, or at least the vector pointing in the direction from the socket towards
the target.
Now we're going to make an algorithm to determine a spread so that we can spawn multiple projectiles
sort of fanned out or spread out.
Which means we're going to need to know what kind of a spread we want.
Do we want these spawned across a spread of 90 degrees, 45 degrees, what have you.
So I'm going to make a protected section.
And make a float called projectile spread.
And I'll give it a default value of 90.
But we'll make this a new property with edit defaults only.
And we'll put these variables related to this in the category of Firebolt.
Okay.
So we have a projectile spread.
We need to know how many projectiles to spawn.
Now I'm going to make a const int 32.
That's local called.
NUM projectiles.
And for Firebolt, this is going to depend on the ability level, but I want to cap this off.
So I want a maximum number of projectiles.
And this too could be a parameter.
So I'm going to make another member variable.
I use them as parameters, but I mean them not in the sense of input parameters to functions, but a
parameter of the spawning projectile phenomenon.
But the correct programming terminology is member variable, right?
This is going to be max projectiles or max num projectiles, or max number of projectiles if you want
to be very verbose.
Max num projectiles can get five, but of course it's added defaults only, so num projectiles is going
to be the minimum f math min between two values Max num projectiles and get ability level.
Whichever is lowest.
So if our ability level is one and Max num projectiles is five, we'll spawn one projectile.
So whichever is lowest, that's how many we're going to spawn.
Now if we're just spawning one projectile then we just need to spawn straight ahead.
So we're going to have an if statement checking num projectiles if num projectiles is greater than one,
because that's when we're going to do the spread algorithm.
Otherwise we'll have an else case and say single projectile, because that's a simple case that doesn't
really require any of this elaborate algorithm business.
If NUM Projectiles is greater than one, that's going to affect what we do here because we're going
to have a for loop, we're going to loop over the number of projectiles.
We're going to say for in 32 I.
We're going to start it at zero.
We're going to go until I is no longer less than num projectiles.
And we'll increment it by one each iteration.
So what are we going to do in the for loop?
Well, I'd like to do something similar to what we did in the Shaman Summon ability.
I'd first like to take my forward vector that we're drawing down here.
And of course for our debug we scaled that vector by 100.
But really, all that matters is the direction.
So we're going to have a const f vector called forward.
That's going to be rotation vector.
We're also going to have a spread.
We know how far left and right to rotate our vector.
Half of this 90 degrees here.
So we're going to have a const f vector called left of spread.
And this is going to be rotated just like in our summoned ability.
Let's just look at that.
In a summoned ability we took a left of spread right.
And we took forward rotated it by negative spread divided by two rotating about the up vector.
Right.
So we can take forward and we can call rotate angle axis rotating by negative spread its projectile
spread right divided by two.
And we can rotate about the f vector up vector.
And here in our for loop, we can do what we did or a summon ability by creating a direction, right?
We start with left of spread and we rotate by delta spread times.
I about the up vector.
Well what's delta spread.
That's going to be the spread in between each vector.
Right.
And an or a summon ability.
That delta spread was calculated as the overall spread divided by the number of minions.
In our case, it'll be the number of projectiles.
But I'm going to show you what happens when we do that.
I'm going to make a const float called Delta Spread.
It's going to be our projectile spread.
Divided by num projectiles.
In other words, it has to be below this calculation.
NUM projectiles.
But I'll show you what happens when we do this.
And to make it very clear what happens when we do this, I'm going to draw debug arrows for left of
spread and right of spread, both in different colors.
So I'm going to take this debug arrow.
Line and copy it.
And I'm going to draw another debug arrow starting from socket location but using left of spread.
So it's going to be socket location plus left of spread times 100.
And this one can be a different color.
I'm going to make it gray and I'm going to draw another one.
Only this will be socket location plus a right of spread.
So I'm just going to this is just for debug purposes.
Make a right of spread.
And instead of rotating by negative, we're going to rotate by positive projectile spread by two.
So now we should be able to see the forward vector left of spread and right of spread.
Those are going to be gray.
And let's just test this out and see those vectors.
And we'll make sure to override the pitch with zero so that these will be parallel to the ground for
ease of viewing.
Oh, it looks like num projectiles hides the class member num projectiles.
It looks like.
Or a projectile spell has num projectiles, doesn't it?
There it is.
It's five.
That's right.
We put that in there so we can just use num projectiles.
No problem.
Here in aura Firebolt, we're just going to set num projectiles instead of declaring a new one.
No problem.
So that takes care of that.
And before we go ahead and test out we just need to make sure we're using right of spread here for our
second vector.
So we have left of spread.
We have right of spread.
And for the arrow size and thickness they're looking a little bit thick.
I'm going to go ahead and just set them back down to one for all of them.
And our first debug arrow can be socket location plus forward.
And with that, let's test this out.
Okay.
So I'm going to check pitch override so that those arrows are drawn flat.
I'm going to hold shift.
And we're going to draw these.
And as you can see the middle most arrow is going forward.
These are parallel to the ground.
And here's our spread of 90 degrees.
Left of spread is here.
Right of spread is here.
And between left of spread and forward is half of 90 which is 45.
Between the forward and right of spread is again 45.
So let's think of the implications of this on our delta because we have projectile spread divided by
number of projectiles.
Now our projectile spread is 90 and our number of projectiles is five.
But imagine for a moment that num projectiles is two.
If we do what we did in Awesome Inability, then we start at left of spread and every iteration we rotate
by delta spread times I.
The first iteration it would be zero, so that gives us left of spread.
But then the next iteration gives us that vector rotated by delta spread times one, and so on.
Right.
What this does is and again imagine num projectiles is two.
We start off at left of spread.
That's when I equals one.
And then we have one more iteration of the for loop where rotating by delta spread which is only half
of 90 degrees because num projectiles is two.
So we're rotating by 45.
In other words.
Our first vector is this one, and our second vector is this one.
So we're launching two fireballs, one that way and one straight forward.
We're not getting what we expected, which was one going off to the left and one going off to the right.
So our delta spread shouldn't have been 45.
It should have been 90.
For this reason, we have to change delta spread instead of projectile spread divided by num projectiles,
we have to divide by num projectiles minus one.
Now this is very important.
We have to have more than one num projectiles.
Otherwise we're subtracting one from one and dividing by zero.
So we have to place this inside the if check after checking to see if num projectiles is greater than
one.
Otherwise there's a chance of dividing by zero, which is not what we want.
So now with this, let's see what happens if we do what we did in the sub.
Inability, we make a direction vector taking our left of spread and rotating it by delta spread times
I.
So we're going to paste this here.
We have a forward direction.
And we can simply draw an arrow for each of these.
So I'm going to get my yield draw debug arrow function and just paste it here.
I'll go ahead and put each argument on its own line.
And these arrows I'm going to draw red.
And what am I drawing?
I'm drawing at socket location for the line start and for the end socket location plus direction, and
that can be multiplied.
And I'm going to multiply it by a slightly shorter magnitude like so.
And if we really want to see these separated from these three arrows down here, we can raise them.
We can say socket location plus f vector zero, zero, 5 or 10.
And we'll use socket location plus that.
And if that's confusing as hell, I'm going to make a const f vector called start.
And that's going to be socket location plus the up.
Vector we created.
And this will be start and this will be start plus direction scaled by 75.
All right.
So let's do this.
And the only thing we need to keep in mind is we're doing this min now.
So it's only going to be one.
Our ability level is one.
So we're going to have to save that for later.
And for now just use what NUM Projectiles is set to.
And we can set that of course in the blueprint and override it.
But it's a value of five.
So we'll just save this for later.
Once we're done then we'll enforce that.
But for now let's just see our pretty little red vector arrows.
All right.
So we see that our num projectiles is five.
And if we launch all right we can see that spread.
So I'm going to pause and I'm going to zoom on in.
And we have the red vectors raised by ten as we expected.
And now look at this.
The spread is correct.
And it doesn't matter how many we have as long as it's greater than one.
If we set the NUM projectiles to two.
And we do the same thing.
Then we'll see what that spread looks like.
Where it launching them.
Like this.
So what we need is logic that will take care of these cases separately.
NUM projectiles is greater than one.
We do this algorithm if it's not greater than one.
In other words, it is one.
Then our direction is actually going to be the forward vector.
So we'll just represent that by drawing a debug arrow here starting at the socket location.
And ending at socket location, plus the forward vector scaled by 75 or so.
And we could even just do that start thing where we raise it by ten.
I'm going to raise it actually only by five while we're working.
So we have our start.
We can just use that here instead of socket location like so.
But you get the point.
We had to subtract one from num projectiles before dividing and order.
For this spread to be the exact number we need to accommodate for how many projectiles that we're launching.
So again, here's what two projectiles looks like.
And if I change the number of projectiles now to one, we should see it go straight forward right.
And if we change it to three.
We should see one in the middle and two spread out, and if we change it to seven, we'll still see
one in the middle.
There will always be one in the middle if we do a odd number right.
But at least these are looking correct.
And you'll notice with our shaman that the shaman spread seems rotated a little bit.
And that's because we're not dividing by the number of minions minus one, we're just dividing by number
of minions.
And we saw that that gives us a shift to the left.
We have to make that adjustment.
So anyway, now we have the math that we need.
We can make this any number like ten and we'll always have the correct spread.
So this is a nice algorithm because now we can adjust the parameters and really control how this works.
Right.
Because it doesn't have to be 90 degrees.
It could be 180, in which case the spread will range to.
This.
You know, we can have fireballs going straight out to the sides.
And we could even make fireballs go in 360 degrees if we wanted to go all around the world, right?
In all directions.
So now we have a pretty nice system.
I'm going to set num projectiles back to five and we'll have this behavior.
But at least now we've got that math under control.
And we have a pretty solid algorithm we could use here.
And really this would be a pretty nice algorithm to just stick in our blueprint function library.
If we ever need randomly spread rotators spread across a given angle with a given number of them, because
that's always pretty useful for gameplay mechanics and things.
All right, so now that we've got that math under control, it's time to actually start spawning multiple
projectiles.
And we'll handle that next.
Great job.
I'll see you soon.