Welcome back.
In this video, we're going to polish up our melee combat just a bit.
It's not going to be completely finished by any means, but we're going to take care of a few things.
Now, one thing that I would like to fix is enemy friendly fire.
Enemies can damage each other, and I don't want them to.
If you're okay with that, that's fine.
Perhaps you'd like a setting that you would like for enemies to have that turned on or off.
I also think that we can tweak a little bit of that ghoul's movement.
It's turning a little slowly, but that's something that we'll probably tweak time and again.
So there are a few things that I'd like to tweak before we move on to ranged attackers.
Let's first deal with that friendly fire or enemies damaging each other here in the melee attack.
We're getting all the live players within the radius of a sphere and then looping through and causing
damage to each one.
Now, we could easily check to see if that particular actor overlapping has a specific tag we can use.
Actor has tag and we could check to see if the tag is player or enemy.
Now if we just say player here, then we can cause damage only to the player.
But then this gar melee attack is only good for enemies and then we can't use it for maybe aura herself
or a pet.
Perhaps Aura would like to spawn.
Instead, what I'd like to do is check the tag of the owning actor and compare it against the tag of
the target actor.
In other words, we could get Avatar actor from actor info.
And then we could see if actor has tag.
And check to see if it's player and if the owning actor has the player tag and the target actor has
the player tag.
We do nothing.
Or if the owner actor has player tag and the target actor has enemy, then we do something.
But we'd also have the case where the owning actor has the enemy tag and the target actor has the player
tag.
Now that's going to result in kind of a lot of nodes, so it'd be nice to have just an easy function,
perhaps a static function on our ability system library that we can call just to see if the actor that
we're checking and the actor we're checking against are friendly, right, If they're friends or not
friends.
So I'd like to make a function in our blueprint function library.
I'm going to go ahead and close out of the editor.
I'm going to close all tabs and I'm going to go into ability system here in the public folder and Aura
ability system, library and we're going to create a function and this function can simply return a
bool.
And this will tell us if two actors are friends or not friends.
So this will be static.
It's going to return bool And I'd like this function to just be true if two actors are not friends,
so we're going to say is not friend.
Now this will take in an a actor pointer for first actor and an a actor pointer called second actor
and we can make this blueprint callable I'm going to go ahead and put this in the same category as get
live players within radius here and is not friend is going to check those tags so we're going to generate
a definition.
Now we always know if an actor has a tag we can always call actor has tag on it so we can say first
actor.
Actor has tag and we can check the name.
Player.
So we know if the first actor is a player or on the player's team.
So we can say const bool first is player and we can make a const bool.
Second is player and take the second actor.
And call the same function actor has tag passing in player.
Now if these two are both true, then we know that they're both on the same team, right?
First is player is true and second is player is true.
However, if they're both false, then they're also on the same team.
Let's see if we can figure out how to return the correct boolean here.
We could make a const bool first is enemy.
And call actor has tag.
But we can pass an enemy instead and we can do the same thing.
Const bool.
Second is enemy and we can call the same function only on second actor.
Second actor actor has tag enemy and basically their friends.
If both are player or both are enemy.
So we can make a const bool called both are players and that would be first is player and second is
player and we can have a bool called both are enemies.
And that would be first is enemy and second is enemy.
So if either of those are true, then they're friends.
Const bool friends equals both are players or both are enemies.
Right.
And then this function is called is not friend.
So we would return not friends.
So this is just a way that I like to map out logic.
It's usually longer than it needs to be, but you could always refactor it down.
For instance, first is player and second is player.
These are unnecessary booleans, of course.
Right?
Because first actor actor has tag is the same thing.
So if I really wanted to get rid of first as player, I could copy this and replace it here.
I could also take second is player and replace it here.
And I could take first as enemy, take that line here and replace that and take second is enemy.
Replace it with that line.
And then we don't need these booleans here.
And of course, you could go further and replace this line with this one in parentheses and replace
both our enemies with this line in parentheses.
But I find it better to not eliminate too much code.
You don't want one long cryptic line that's really hard to understand and hard to debug if it doesn't
work right.
I think at this point is not friend is pretty much understandable and it looks good.
And you might have noticed that I didn't prefix these with the lowercase B as you typically do with
booleans in unreal engine.
So by all means put that B there if you'd like it to look nice with those B's.
And now we have an is not friend function.
Now this could be actually blueprint pure.
So we don't really need an execution pin for it.
Let's make this a blueprint, pure function and let's go ahead and use it.
Let's run in debug mode.
And we can go ahead and get that ability opened back up a melee attack.
And as we loop through each of the overlapping live players we can use is not friend.
Let's get that.
And we have to test against our first actor.
We're going to get Avatar actor from actor info.
That's going to be the first actor and then we're going to check against the second actor and we'll
have a branch.
And only if they're not friends are we going to cause damage.
So that makes things a little bit easier for us.
Let's just make sure that they're not damaging each other.
So let's get those goblins over here.
Get them all close together.
And it's a little hard to tell if we're getting friendly fire or not.
So I think as we cause damage, I'm going to take that other actor.
So we're going to get object name and print that string.
So that way we know exactly who's getting damaged.
Let's go ahead and press play, get these all together.
We damaged aura looks like we damaged aura a couple times.
Always damaging aura.
Okay.
All right.
So they're no longer damaging each other.
So that was what I wanted to prevent.
So our code is working.
Excellent.
Okay, so that was one thing I'd like to fix.
I feel like that's fixed.
Now, another thing is it's really hard to test death for our enemies because their health is really
high.
Now, that has to do with their attributes.
And health is related to the vigor attribute, which is a primary attribute.
So if we go to blueprints, ability system, gameplay effects, default attributes enemy.
Well, we have our primary attributes for each of the classes right now.
If we look at these, we'll see that looking at their primary attributes, we're setting them based
on curve tables.
These are scalable floats and we can take a look at our primary attributes curve tables.
We have three of them, one for each type of character class.
And if we open the warrior and look at vigor, here's what that curve looks like.
And if we select the first one, the value is 11.
So if the value of vigor is 11, we can take a look at what our max health should be by going to gameplay
effects, default attributes and secondary attributes enemy.
And we can see what that max health is in relation to vigor.
So here it is, max health.
It's actually based on not only the vigor but also the level because we're using an MSQ for it.
Now what is our MSI do?
Well, if we go to ability system Mod mag Calc Max health, we can see the formula.
We have a baseline of 80 health plus 2.5 times vigor and ten times player level.
That's going to be quite high.
Now if we wanted we could parameterize that base 80 or we could also scale this.
If we think that our enemies are a little op, we could scale the result of the MSI.
So whatever that value is, we could scale it by say, 0.25 and then our enemies will have a lot less
max health based on their vigor and their level.
So that's going to knock them down a couple notches, isn't it?
Now, we're not using the secondary attributes.
We're using a test attribute.
I believe if we go to ability system data and character class info, well for our secondary attributes,
we're using secondary attributes test.
If we go back to secondary attributes Enemy now, we'll be using that formula and we should see a lot
less and indeed we do.
It looks like I just obliterated that ghoul.
And it's a lot easier now to kill these other dudes.
And already it's a lot more fun.
Cool.
So because we just scaled their health by 0.25, they're a lot less op and aura can still be a little
bit stronger as it should be.
I think Aura should be a little stronger when it comes to health because no one likes to die right away.
But of course, make the game as hard as you like.
Now, another thing is we're dissolving the enemy with a dissolved material.
We're not doing that with our ghoul, but we could we can edit BP goal.
We can take the mesh, we can browse to it, and we can go into its materials folder.
And here's M Ghoul.
Now if I double click M Ghoul to open it up, it's a pretty simple material, but we're going to want
to add some things to it so we can dissolve it and we can get those things we need by opening a dissolve
material that we have in our project now my tabs are getting a little crazy, so I'm going to go ahead
and close some of them.
I'm going to leave BP Ghoul open.
I'm going to close my secondary attributes and my curve table and my primary attributes, my behavior
tree, my hit React gameplay ability SQM ghoul attack our enemy base BT attack and I'm going to leave
melee attack open.
Let's just get our ghoul dissolving real quick.
Okay.
So now that my tabs are under control, I'm going to go into assets materials and this m dissolve effect
is what I want.
You see this m dissolve effect has some nodes that can be copied and added to any material.
And it's these right here and you can even see the comment says properties that the material needs.
And there are two wires that need to be connected.
And there are comments that say where they're supposed to go.
So this is actually pretty easy to use.
We can just copy this whole part here and go to our Google and paste it in Ctrl V.
Now let's look at this comment.
It says Dissolve effect surface, masked default lit.
So what we need to do is select the Google.
And first it says Surface.
That's the material domain.
It's already set to surface.
Next, it says Masked.
That's the blend mode.
We need to change it from opaque to masked.
And then it says default lit.
That's the shading model.
It's already set to that.
Now we need this part to go to Emissive color.
So we're going to click and drag all the way down and stick it into Emissive color.
And then we need this wire to go to Opacity mask.
So we'll plug that into Opacity mask.
And now our goal is as a dissolvable material and in order to dissolve it, we have to make a material
instance based on it.
So browsing back to Google, we can right click on Google, Create material instance, and I'm going
to call this my underscore Google Dissolve.
And now we have a dissolve material and we can go back to Google and search for Dissolve.
We do have to click on Google Self.
And here's the Dissolve material instance.
We're going to set that to me.
Google Dissolve Now it doesn't have a weapon, so don't worry about that.
But now our Google can dissolve and if we save all and play test, we should be able to see that happen
if we kill the Google.
Didn't work.
So let's find out why.
I'm going to open up my base enemy blueprint class.
So go to character BP enemy base and we have event start, dissolve timeline and we're setting a scalar
parameter value called dissolve.
So we do need to go back to school and click apply or just save, and then we can open my Google dissolve
and see that those are parameters there.
Now that they are, then our BP enemy base should be able to set those scalar parameter values.
And of course we do see that it's already partially dissolved.
So the mesh is now partially dissolved, which means we have to go to Google and make sure that this
dissolve value is at a value that does not have it dissolved at all.
So that's going to be about negative two is pretty safe.
So I'm going to set this to negative two and apply.
And now we see that our BP Google is all there.
And now we can test out death.
So let's go ahead and kill it.
With fire.
And there it goes.
It dissolves and it's gone.
Perfect.
All right.
So things are starting to look pretty good.
We've taken care of our enemies being too powerful.
We've taken care of our enemies, damaging each other.
So the rest of the things we can tweak as we go.
I do think my ghoul could be turning a little bit faster.
I can check out its rotation rate on the character movement component.
I might bump this up to 200 again.
That might be something I'll go back and forth on.
But another thing that I'd also like to point out is that sometimes our enemies get jumbled up against
each other.
They can kind of get caught on each other.
And for that reason, we may wish to do something about that.
Now, one option is to use avoidance.
If we go to our blueprints character BP enemy base and we search for avoidance or just avoid, we can
see that there's RVO avoidance.
Now keep in mind this only runs on the server, so it's good for AI only.
But if we check that checkbox, then the enemies are going to try to avoid each other.
They're going to run around each other.
The only problem with this is that they kind of strafe.
So notice that this goblin here is going to constantly keep its eye on the target, and that's even
if your orienting rotation to movement.
So if your enemies don't really have a strafing animation, then this isn't going to work very well.
It's not going to look very good.
It'll work.
It just doesn't look as good as it should.
But something to be aware of.
You could use avoidance and the enemies will attempt to avoid each other.
They won't clutter up.
They'll kind of move around each other and we can really see that with 2 or 3 different spear enemies
in the world.
Like so and I'm going to go ahead and move my ghoul a little closer to the goblins and my player start
a little closer as well.
So pressing play here.
Now we have a bunch of these little spear guys and notice that they're all avoiding each other.
So it does look a little funny in that they're kind of strafing without a strafe animation.
So they kind of slide, but at least they avoid each other and run around each other and they don't
get too bunched up.
So something to be aware of, if you like, that you can use that avoidance.
But of course, just keep in mind that they don't orient in the rotation of their movement and that
can look a little bit funny.
Even if we go into our enemy on our character movement component and search for Orient rotation to movement,
if we check that.
Even so, they will not orient to movement.
If I go to my goblin spear and check that here as well, make sure that it is checked and it is, then
we'll see that as they're moving.
They still kind of strafe.
So something to be aware of with avoidance, but it can be a nice effect if you do want them to kind
of move around each other and not get too caught.
I'm going to keep it off, but I thought I'd let you know about it.
So going back to BP enemy base, I'm going to uncheck Orient rotation to movement and I'm going to turn
off that avoidance.
Okay, so things are looking pretty good.
We still have some work to do, but Malay is basically there and we now need our ranged attackers to
attack.
And that's going to involve some challenges in itself.
Right?
The slingshot is a unique weapon.
It morphs.
It changes, it stretches.
It's a lot like a bow and arrow weapon.
So the same techniques that we employ for the slingshot can be used for bow and arrow weapons or crossbows
or other similar types of weapons.
So that'll be a good skill set for you to learn if you haven't implemented those types of weapons before.
And we'll do that in the next section when we implement ranged attacks.
So excellent job and I'll see you in the next video.