WEBVTT

00:06.780 --> 00:07.950
Welcome back.

00:08.430 --> 00:12.260
In this video, we're going to polish up our melee combat just a bit.

00:12.270 --> 00:18.090
It's not going to be completely finished by any means, but we're going to take care of a few things.

00:18.120 --> 00:23.110
Now, one thing that I would like to fix is enemy friendly fire.

00:23.130 --> 00:26.850
Enemies can damage each other, and I don't want them to.

00:26.880 --> 00:28.910
If you're okay with that, that's fine.

00:28.920 --> 00:34.320
Perhaps you'd like a setting that you would like for enemies to have that turned on or off.

00:34.440 --> 00:39.800
I also think that we can tweak a little bit of that ghoul's movement.

00:39.810 --> 00:44.850
It's turning a little slowly, but that's something that we'll probably tweak time and again.

00:45.590 --> 00:51.800
So there are a few things that I'd like to tweak before we move on to ranged attackers.

00:51.920 --> 00:59.850
Let's first deal with that friendly fire or enemies damaging each other here in the melee attack.

00:59.870 --> 01:06.560
We're getting all the live players within the radius of a sphere and then looping through and causing

01:06.560 --> 01:07.770
damage to each one.

01:07.790 --> 01:15.710
Now, we could easily check to see if that particular actor overlapping has a specific tag we can use.

01:15.710 --> 01:21.800
Actor has tag and we could check to see if the tag is player or enemy.

01:22.310 --> 01:27.950
Now if we just say player here, then we can cause damage only to the player.

01:27.950 --> 01:36.530
But then this gar melee attack is only good for enemies and then we can't use it for maybe aura herself

01:36.530 --> 01:37.840
or a pet.

01:37.850 --> 01:39.880
Perhaps Aura would like to spawn.

01:39.890 --> 01:46.830
Instead, what I'd like to do is check the tag of the owning actor and compare it against the tag of

01:46.830 --> 01:47.970
the target actor.

01:47.970 --> 01:53.250
In other words, we could get Avatar actor from actor info.

01:53.740 --> 01:57.670
And then we could see if actor has tag.

01:58.910 --> 02:07.520
And check to see if it's player and if the owning actor has the player tag and the target actor has

02:07.520 --> 02:08.210
the player tag.

02:08.210 --> 02:09.290
We do nothing.

02:09.290 --> 02:14.900
Or if the owner actor has player tag and the target actor has enemy, then we do something.

02:14.900 --> 02:20.900
But we'd also have the case where the owning actor has the enemy tag and the target actor has the player

02:20.900 --> 02:21.370
tag.

02:21.380 --> 02:27.170
Now that's going to result in kind of a lot of nodes, so it'd be nice to have just an easy function,

02:27.170 --> 02:35.180
perhaps a static function on our ability system library that we can call just to see if the actor that

02:35.180 --> 02:42.170
we're checking and the actor we're checking against are friendly, right, If they're friends or not

02:42.170 --> 02:43.040
friends.

02:43.040 --> 02:46.970
So I'd like to make a function in our blueprint function library.

02:46.970 --> 02:49.430
I'm going to go ahead and close out of the editor.

02:49.520 --> 02:56.620
I'm going to close all tabs and I'm going to go into ability system here in the public folder and Aura

02:56.630 --> 03:01.920
ability system, library and we're going to create a function and this function can simply return a

03:01.920 --> 03:02.520
bool.

03:02.520 --> 03:07.830
And this will tell us if two actors are friends or not friends.

03:07.830 --> 03:09.450
So this will be static.

03:10.020 --> 03:18.180
It's going to return bool And I'd like this function to just be true if two actors are not friends,

03:18.180 --> 03:21.420
so we're going to say is not friend.

03:21.450 --> 03:33.390
Now this will take in an a actor pointer for first actor and an a actor pointer called second actor

03:34.800 --> 03:41.670
and we can make this blueprint callable I'm going to go ahead and put this in the same category as get

03:41.700 --> 03:48.900
live players within radius here and is not friend is going to check those tags so we're going to generate

03:48.900 --> 03:49.920
a definition.

03:50.010 --> 03:58.680
Now we always know if an actor has a tag we can always call actor has tag on it so we can say first

03:58.680 --> 03:59.490
actor.

04:00.580 --> 04:04.510
Actor has tag and we can check the name.

04:04.750 --> 04:05.650
Player.

04:07.720 --> 04:12.400
So we know if the first actor is a player or on the player's team.

04:12.400 --> 04:20.020
So we can say const bool first is player and we can make a const bool.

04:21.730 --> 04:26.830
Second is player and take the second actor.

04:28.710 --> 04:33.210
And call the same function actor has tag passing in player.

04:34.440 --> 04:40.110
Now if these two are both true, then we know that they're both on the same team, right?

04:40.140 --> 04:43.740
First is player is true and second is player is true.

04:44.580 --> 04:49.020
However, if they're both false, then they're also on the same team.

04:49.050 --> 04:53.430
Let's see if we can figure out how to return the correct boolean here.

04:53.670 --> 04:57.420
We could make a const bool first is enemy.

04:59.000 --> 05:01.640
And call actor has tag.

05:01.790 --> 05:06.200
But we can pass an enemy instead and we can do the same thing.

05:06.200 --> 05:07.440
Const bool.

05:07.460 --> 05:14.480
Second is enemy and we can call the same function only on second actor.

05:14.600 --> 05:19.340
Second actor actor has tag enemy and basically their friends.

05:19.340 --> 05:24.010
If both are player or both are enemy.

05:24.020 --> 05:35.360
So we can make a const bool called both are players and that would be first is player and second is

05:35.360 --> 05:41.690
player and we can have a bool called both are enemies.

05:43.070 --> 05:49.400
And that would be first is enemy and second is enemy.

05:50.270 --> 05:54.000
So if either of those are true, then they're friends.

05:54.020 --> 06:03.650
Const bool friends equals both are players or both are enemies.

06:04.820 --> 06:05.600
Right.

06:05.600 --> 06:08.540
And then this function is called is not friend.

06:08.540 --> 06:11.810
So we would return not friends.

06:12.480 --> 06:15.870
So this is just a way that I like to map out logic.

06:15.900 --> 06:21.030
It's usually longer than it needs to be, but you could always refactor it down.

06:21.060 --> 06:24.750
For instance, first is player and second is player.

06:24.780 --> 06:27.600
These are unnecessary booleans, of course.

06:27.600 --> 06:27.870
Right?

06:27.870 --> 06:31.620
Because first actor actor has tag is the same thing.

06:31.620 --> 06:37.410
So if I really wanted to get rid of first as player, I could copy this and replace it here.

06:37.440 --> 06:41.040
I could also take second is player and replace it here.

06:42.780 --> 06:50.250
And I could take first as enemy, take that line here and replace that and take second is enemy.

06:50.280 --> 06:52.200
Replace it with that line.

06:53.250 --> 06:55.680
And then we don't need these booleans here.

06:56.670 --> 07:05.280
And of course, you could go further and replace this line with this one in parentheses and replace

07:05.280 --> 07:08.970
both our enemies with this line in parentheses.

07:08.970 --> 07:13.020
But I find it better to not eliminate too much code.

07:13.020 --> 07:18.720
You don't want one long cryptic line that's really hard to understand and hard to debug if it doesn't

07:18.720 --> 07:19.530
work right.

07:19.740 --> 07:26.670
I think at this point is not friend is pretty much understandable and it looks good.

07:26.880 --> 07:33.120
And you might have noticed that I didn't prefix these with the lowercase B as you typically do with

07:33.120 --> 07:34.680
booleans in unreal engine.

07:34.680 --> 07:41.640
So by all means put that B there if you'd like it to look nice with those B's.

07:41.880 --> 07:45.070
And now we have an is not friend function.

07:45.090 --> 07:47.640
Now this could be actually blueprint pure.

07:47.640 --> 07:50.580
So we don't really need an execution pin for it.

07:50.580 --> 07:55.930
Let's make this a blueprint, pure function and let's go ahead and use it.

07:55.930 --> 07:57.520
Let's run in debug mode.

07:59.080 --> 08:04.090
And we can go ahead and get that ability opened back up a melee attack.

08:04.090 --> 08:12.670
And as we loop through each of the overlapping live players we can use is not friend.

08:12.700 --> 08:14.050
Let's get that.

08:14.050 --> 08:16.480
And we have to test against our first actor.

08:16.480 --> 08:19.630
We're going to get Avatar actor from actor info.

08:19.660 --> 08:26.170
That's going to be the first actor and then we're going to check against the second actor and we'll

08:26.170 --> 08:27.280
have a branch.

08:29.210 --> 08:34.160
And only if they're not friends are we going to cause damage.

08:34.250 --> 08:37.880
So that makes things a little bit easier for us.

08:37.910 --> 08:41.840
Let's just make sure that they're not damaging each other.

08:42.450 --> 08:45.300
So let's get those goblins over here.

08:46.040 --> 08:47.630
Get them all close together.

08:56.210 --> 09:01.000
And it's a little hard to tell if we're getting friendly fire or not.

09:01.010 --> 09:05.680
So I think as we cause damage, I'm going to take that other actor.

09:05.690 --> 09:10.040
So we're going to get object name and print that string.

09:15.160 --> 09:18.160
So that way we know exactly who's getting damaged.

09:18.760 --> 09:22.480
Let's go ahead and press play, get these all together.

09:25.800 --> 09:29.790
We damaged aura looks like we damaged aura a couple times.

09:30.270 --> 09:32.400
Always damaging aura.

09:32.430 --> 09:33.330
Okay.

09:36.790 --> 09:37.300
All right.

09:37.300 --> 09:39.610
So they're no longer damaging each other.

09:40.030 --> 09:43.120
So that was what I wanted to prevent.

09:43.240 --> 09:46.000
So our code is working.

09:46.390 --> 09:47.410
Excellent.

09:48.070 --> 09:51.160
Okay, so that was one thing I'd like to fix.

09:51.160 --> 09:52.870
I feel like that's fixed.

09:53.080 --> 10:00.130
Now, another thing is it's really hard to test death for our enemies because their health is really

10:00.130 --> 10:00.760
high.

10:00.880 --> 10:03.190
Now, that has to do with their attributes.

10:03.190 --> 10:06.850
And health is related to the vigor attribute, which is a primary attribute.

10:06.850 --> 10:13.240
So if we go to blueprints, ability system, gameplay effects, default attributes enemy.

10:13.270 --> 10:19.630
Well, we have our primary attributes for each of the classes right now.

10:19.630 --> 10:25.930
If we look at these, we'll see that looking at their primary attributes, we're setting them based

10:25.930 --> 10:27.280
on curve tables.

10:27.280 --> 10:34.030
These are scalable floats and we can take a look at our primary attributes curve tables.

10:34.030 --> 10:38.800
We have three of them, one for each type of character class.

10:38.810 --> 10:45.410
And if we open the warrior and look at vigor, here's what that curve looks like.

10:45.440 --> 10:49.160
And if we select the first one, the value is 11.

10:49.160 --> 10:56.570
So if the value of vigor is 11, we can take a look at what our max health should be by going to gameplay

10:56.570 --> 11:01.490
effects, default attributes and secondary attributes enemy.

11:01.490 --> 11:06.500
And we can see what that max health is in relation to vigor.

11:06.500 --> 11:09.080
So here it is, max health.

11:09.110 --> 11:15.590
It's actually based on not only the vigor but also the level because we're using an MSQ for it.

11:15.680 --> 11:17.450
Now what is our MSI do?

11:17.450 --> 11:22.640
Well, if we go to ability system Mod mag Calc Max health, we can see the formula.

11:22.640 --> 11:31.100
We have a baseline of 80 health plus 2.5 times vigor and ten times player level.

11:31.310 --> 11:33.500
That's going to be quite high.

11:33.680 --> 11:41.210
Now if we wanted we could parameterize that base 80 or we could also scale this.

11:41.240 --> 11:47.550
If we think that our enemies are a little op, we could scale the result of the MSI.

11:47.570 --> 11:58.430
So whatever that value is, we could scale it by say, 0.25 and then our enemies will have a lot less

11:58.430 --> 12:01.700
max health based on their vigor and their level.

12:01.880 --> 12:06.020
So that's going to knock them down a couple notches, isn't it?

12:06.050 --> 12:09.380
Now, we're not using the secondary attributes.

12:09.380 --> 12:11.240
We're using a test attribute.

12:11.240 --> 12:18.950
I believe if we go to ability system data and character class info, well for our secondary attributes,

12:18.950 --> 12:21.410
we're using secondary attributes test.

12:21.410 --> 12:28.910
If we go back to secondary attributes Enemy now, we'll be using that formula and we should see a lot

12:28.910 --> 12:31.160
less and indeed we do.

12:32.390 --> 12:35.870
It looks like I just obliterated that ghoul.

12:35.870 --> 12:40.700
And it's a lot easier now to kill these other dudes.

12:42.740 --> 12:44.810
And already it's a lot more fun.

12:44.960 --> 12:45.560
Cool.

12:45.560 --> 12:54.350
So because we just scaled their health by 0.25, they're a lot less op and aura can still be a little

12:54.350 --> 12:56.090
bit stronger as it should be.

12:56.090 --> 13:02.990
I think Aura should be a little stronger when it comes to health because no one likes to die right away.

13:02.990 --> 13:05.480
But of course, make the game as hard as you like.

13:05.510 --> 13:11.330
Now, another thing is we're dissolving the enemy with a dissolved material.

13:11.330 --> 13:17.270
We're not doing that with our ghoul, but we could we can edit BP goal.

13:17.300 --> 13:24.020
We can take the mesh, we can browse to it, and we can go into its materials folder.

13:24.020 --> 13:26.030
And here's M Ghoul.

13:26.060 --> 13:31.910
Now if I double click M Ghoul to open it up, it's a pretty simple material, but we're going to want

13:31.910 --> 13:40.160
to add some things to it so we can dissolve it and we can get those things we need by opening a dissolve

13:40.160 --> 13:45.480
material that we have in our project now my tabs are getting a little crazy, so I'm going to go ahead

13:45.480 --> 13:47.310
and close some of them.

13:47.310 --> 13:49.890
I'm going to leave BP Ghoul open.

13:50.130 --> 13:56.970
I'm going to close my secondary attributes and my curve table and my primary attributes, my behavior

13:56.970 --> 14:07.440
tree, my hit React gameplay ability SQM ghoul attack our enemy base BT attack and I'm going to leave

14:07.470 --> 14:08.910
melee attack open.

14:08.910 --> 14:12.570
Let's just get our ghoul dissolving real quick.

14:13.020 --> 14:13.560
Okay.

14:13.560 --> 14:21.120
So now that my tabs are under control, I'm going to go into assets materials and this m dissolve effect

14:21.120 --> 14:22.230
is what I want.

14:22.380 --> 14:29.730
You see this m dissolve effect has some nodes that can be copied and added to any material.

14:29.730 --> 14:35.670
And it's these right here and you can even see the comment says properties that the material needs.

14:36.480 --> 14:39.960
And there are two wires that need to be connected.

14:39.960 --> 14:43.500
And there are comments that say where they're supposed to go.

14:43.500 --> 14:46.290
So this is actually pretty easy to use.

14:46.290 --> 14:52.050
We can just copy this whole part here and go to our Google and paste it in Ctrl V.

14:52.050 --> 14:53.880
Now let's look at this comment.

14:53.880 --> 14:57.630
It says Dissolve effect surface, masked default lit.

14:57.630 --> 15:00.870
So what we need to do is select the Google.

15:01.140 --> 15:04.320
And first it says Surface.

15:04.320 --> 15:05.790
That's the material domain.

15:05.790 --> 15:07.380
It's already set to surface.

15:07.410 --> 15:08.820
Next, it says Masked.

15:08.820 --> 15:10.140
That's the blend mode.

15:10.140 --> 15:12.990
We need to change it from opaque to masked.

15:12.990 --> 15:14.580
And then it says default lit.

15:14.610 --> 15:15.930
That's the shading model.

15:15.930 --> 15:17.640
It's already set to that.

15:17.730 --> 15:21.480
Now we need this part to go to Emissive color.

15:21.480 --> 15:26.550
So we're going to click and drag all the way down and stick it into Emissive color.

15:26.670 --> 15:31.020
And then we need this wire to go to Opacity mask.

15:31.020 --> 15:33.690
So we'll plug that into Opacity mask.

15:33.690 --> 15:41.800
And now our goal is as a dissolvable material and in order to dissolve it, we have to make a material

15:41.800 --> 15:43.600
instance based on it.

15:43.600 --> 15:50.590
So browsing back to Google, we can right click on Google, Create material instance, and I'm going

15:50.590 --> 15:54.580
to call this my underscore Google Dissolve.

15:55.090 --> 16:00.790
And now we have a dissolve material and we can go back to Google and search for Dissolve.

16:00.790 --> 16:03.640
We do have to click on Google Self.

16:03.640 --> 16:06.220
And here's the Dissolve material instance.

16:06.220 --> 16:07.990
We're going to set that to me.

16:08.170 --> 16:12.490
Google Dissolve Now it doesn't have a weapon, so don't worry about that.

16:12.490 --> 16:19.120
But now our Google can dissolve and if we save all and play test, we should be able to see that happen

16:19.120 --> 16:20.590
if we kill the Google.

16:27.650 --> 16:28.400
Didn't work.

16:28.400 --> 16:31.160
So let's find out why.

16:32.280 --> 16:36.450
I'm going to open up my base enemy blueprint class.

16:36.450 --> 16:44.460
So go to character BP enemy base and we have event start, dissolve timeline and we're setting a scalar

16:44.460 --> 16:46.860
parameter value called dissolve.

16:47.160 --> 16:54.900
So we do need to go back to school and click apply or just save, and then we can open my Google dissolve

16:54.900 --> 16:57.120
and see that those are parameters there.

16:57.150 --> 17:03.360
Now that they are, then our BP enemy base should be able to set those scalar parameter values.

17:03.360 --> 17:07.890
And of course we do see that it's already partially dissolved.

17:07.890 --> 17:13.710
So the mesh is now partially dissolved, which means we have to go to Google and make sure that this

17:13.710 --> 17:18.840
dissolve value is at a value that does not have it dissolved at all.

17:18.840 --> 17:22.710
So that's going to be about negative two is pretty safe.

17:22.710 --> 17:26.520
So I'm going to set this to negative two and apply.

17:26.520 --> 17:29.490
And now we see that our BP Google is all there.

17:29.490 --> 17:32.590
And now we can test out death.

17:32.590 --> 17:34.390
So let's go ahead and kill it.

17:34.800 --> 17:36.090
With fire.

17:36.120 --> 17:37.590
And there it goes.

17:37.590 --> 17:39.450
It dissolves and it's gone.

17:39.750 --> 17:41.340
Perfect.

17:42.210 --> 17:42.930
All right.

17:42.930 --> 17:45.960
So things are starting to look pretty good.

17:45.990 --> 17:49.320
We've taken care of our enemies being too powerful.

17:49.320 --> 17:52.460
We've taken care of our enemies, damaging each other.

17:52.470 --> 17:55.890
So the rest of the things we can tweak as we go.

17:55.920 --> 17:59.930
I do think my ghoul could be turning a little bit faster.

17:59.940 --> 18:05.130
I can check out its rotation rate on the character movement component.

18:06.170 --> 18:08.920
I might bump this up to 200 again.

18:08.930 --> 18:11.570
That might be something I'll go back and forth on.

18:11.570 --> 18:17.360
But another thing that I'd also like to point out is that sometimes our enemies get jumbled up against

18:17.360 --> 18:17.870
each other.

18:17.870 --> 18:20.060
They can kind of get caught on each other.

18:20.060 --> 18:24.200
And for that reason, we may wish to do something about that.

18:24.200 --> 18:27.260
Now, one option is to use avoidance.

18:27.290 --> 18:37.490
If we go to our blueprints character BP enemy base and we search for avoidance or just avoid, we can

18:37.490 --> 18:39.890
see that there's RVO avoidance.

18:39.920 --> 18:44.810
Now keep in mind this only runs on the server, so it's good for AI only.

18:44.810 --> 18:50.240
But if we check that checkbox, then the enemies are going to try to avoid each other.

18:50.270 --> 18:52.190
They're going to run around each other.

18:52.220 --> 18:55.670
The only problem with this is that they kind of strafe.

18:55.670 --> 19:02.360
So notice that this goblin here is going to constantly keep its eye on the target, and that's even

19:02.360 --> 19:05.000
if your orienting rotation to movement.

19:05.000 --> 19:12.630
So if your enemies don't really have a strafing animation, then this isn't going to work very well.

19:12.630 --> 19:13.980
It's not going to look very good.

19:13.980 --> 19:14.640
It'll work.

19:14.640 --> 19:17.010
It just doesn't look as good as it should.

19:17.010 --> 19:18.540
But something to be aware of.

19:18.570 --> 19:23.040
You could use avoidance and the enemies will attempt to avoid each other.

19:23.070 --> 19:24.690
They won't clutter up.

19:24.690 --> 19:30.930
They'll kind of move around each other and we can really see that with 2 or 3 different spear enemies

19:30.930 --> 19:32.040
in the world.

19:32.750 --> 19:40.580
Like so and I'm going to go ahead and move my ghoul a little closer to the goblins and my player start

19:40.610 --> 19:42.200
a little closer as well.

19:43.300 --> 19:44.920
So pressing play here.

19:44.920 --> 19:49.960
Now we have a bunch of these little spear guys and notice that they're all avoiding each other.

19:49.960 --> 19:56.860
So it does look a little funny in that they're kind of strafing without a strafe animation.

19:56.860 --> 20:02.980
So they kind of slide, but at least they avoid each other and run around each other and they don't

20:02.980 --> 20:04.360
get too bunched up.

20:05.630 --> 20:10.550
So something to be aware of, if you like, that you can use that avoidance.

20:10.550 --> 20:16.910
But of course, just keep in mind that they don't orient in the rotation of their movement and that

20:16.910 --> 20:18.860
can look a little bit funny.

20:18.890 --> 20:27.260
Even if we go into our enemy on our character movement component and search for Orient rotation to movement,

20:27.260 --> 20:28.400
if we check that.

20:28.400 --> 20:32.000
Even so, they will not orient to movement.

20:32.000 --> 20:40.190
If I go to my goblin spear and check that here as well, make sure that it is checked and it is, then

20:40.190 --> 20:42.830
we'll see that as they're moving.

20:44.090 --> 20:45.410
They still kind of strafe.

20:45.410 --> 20:51.410
So something to be aware of with avoidance, but it can be a nice effect if you do want them to kind

20:51.410 --> 20:54.830
of move around each other and not get too caught.

20:55.740 --> 20:58.730
I'm going to keep it off, but I thought I'd let you know about it.

20:58.740 --> 21:06.120
So going back to BP enemy base, I'm going to uncheck Orient rotation to movement and I'm going to turn

21:06.120 --> 21:08.130
off that avoidance.

21:10.870 --> 21:13.780
Okay, so things are looking pretty good.

21:13.810 --> 21:21.640
We still have some work to do, but Malay is basically there and we now need our ranged attackers to

21:21.640 --> 21:22.300
attack.

21:22.300 --> 21:25.000
And that's going to involve some challenges in itself.

21:25.000 --> 21:25.490
Right?

21:25.510 --> 21:27.900
The slingshot is a unique weapon.

21:27.910 --> 21:28.810
It morphs.

21:28.810 --> 21:30.570
It changes, it stretches.

21:30.580 --> 21:32.530
It's a lot like a bow and arrow weapon.

21:32.530 --> 21:40.750
So the same techniques that we employ for the slingshot can be used for bow and arrow weapons or crossbows

21:40.780 --> 21:42.970
or other similar types of weapons.

21:42.970 --> 21:48.910
So that'll be a good skill set for you to learn if you haven't implemented those types of weapons before.

21:48.910 --> 21:52.960
And we'll do that in the next section when we implement ranged attacks.

21:53.140 --> 21:56.770
So excellent job and I'll see you in the next video.
