Welcome back.
Now in this video we're going to implement our highlighting behavior.
Now we've already discussed how the player controller has the ability to get whatever actor is underneath
the cursor.
At any given point in time we're going to do this.
And this is something I'd like to do every frame, as it's not a very expensive operation to do, and
we need that sort of responsiveness when hovering the mouse cursor over various different actors.
So in our player controller dot h file, we're going to override a tick function.
We're going to say virtualvoid and we're going to override player tick.
So let's make a definition for this.
And in player tick we're going to perform our trace and handle the highlighting of any actors we hit
that implement the enemy interface.
So I'd like to make a function to call here in player tick.
And this function is going to trace under the cursor.
So I'm going to call this function cursor trace.
And I'm going to make it private.
So down at the bottom we'll make a void cursor trace I'll go ahead and implement this.
And I don't want this inlined I'm going to move it.
Back out into the player controller dot cpp file.
And the first thing I'm going to do is call cursor trace here in player tic.
Now cursor trace is going to involve getting the hit result under the cursor.
That's something that the player controller class inherently has the ability to do.
So to do that we make a hit result f hit result.
We'll call this cursor hit, and the player controller function that we're interested in is get hit
result under cursor.
And this takes a trace channel we can trace against the visibility channel.
So that's going to be a collision channel visibility or simply EQ visibility trace complex is going
to be false.
We want to trace against simple collision only.
And the hit result is our cursor hit.
So after calling this we now have a hit result.
And we can check if we got a blocking hit by checking if cursor hit dot blocking hit.
And if we don't want too much logic nested, we can simply say if not, cursor hit dot blocking, hit
return and the next lines of code won't be indented.
Okay, so what are we going to do with this hit result?
Well, we want to see if it implements the enemy interface.
And we can simply perform a cast here.
So we can say cast and we can cast to I enemy interface.
We're casting cursor hit dot get actor.
That's the actor that was hit.
If this cast succeeds, then the actor we hit implements the I enemy interface.
Now, if the actor is not an I enemy interface, it doesn't implement it.
Then this cast returns a null pointer and we can use that information.
What I'd like to do is have two pointers.
One for the actor that we're hovering over this frame, and one for the actor we hovered over last frame.
Now from frame to frame, they could be the same or they could be different.
And what we do depends on whether they're the same or different, whether they're null or not null.
So I'm going to make two pointers.
And rather than pointers to actors, I'm going to make pointers to the I enemy interface.
So I'm going to forward declare that up here at the top I'm going to say class I enemy interface.
Now I'm going to have an AI enemy interface called Last Actor and an AI enemy interface called this
actor.
Both pointers.
Hey there Steven from the future here.
Coming back with an important message for you.
In the original version of this course, we used a raw pointer for I enemy interface for a member variable,
and this is actually not the correct way to do it.
The correct way to hold a member variable to an interface is by using a special wrapper designed to
hold interfaces.
So it looks like this.
The wrapper is T script interface.
It's a template wrapper, so we have to put the interface type within pointy brackets.
That's going to be I enemy interface.
No asterisk needed.
So our last actor is going to be stored using this interface wrapper T script interface.
And the same goes for this actor.
It's going to be a T script interface as well.
So even though the lectures have raw pointers for I enemy interface, don't do that.
Do it the right way by using t script interface.
Now this does change the way we use these pointers slightly, as we'll see in just a moment.
And back here in Cursor Trace, I'm first going to set last actor equal to this actor each frame.
And then this actor will be set to the result of this cast.
I'm going to move it here.
Steven from the future here again, letting you know that this is where we differ from the video.
Once again, because we're now using a script interface, this cast is unnecessary.
We don't have to cast.
All we have to do is set this actor using cursor hit dot, get actor.
So don't perform the cast, use script interface and set this actor directly.
This script interface is pretty nice, right?
So the cast may fail, which means this actor would be currently a null pointer.
And last actor will store what this actor was before we updated it.
In other words, what it was last frame so we can check the values of these.
Now.
There are several scenarios that we need to cover, and I'd like to write these out with some pseudocode
comments.
So I'm going to make a multi-line comment actually.
And this comment will say line trace from cursor.
There are several scenarios.
So one scenario we'll call it a is that last actor is null and this actor is null.
So this is probably one of the simplest scenarios, right.
Whatever our cursor hit got last frame.
It was not an enemy interface.
It was probably a wall or a floor or something like that.
And the same for this frame.
We're not hitting an enemy interface, so we don't need to try to call the interface function.
In fact, all we have to do in this case is do nothing.
So that's the easiest scenario.
Now another scenario is that last actor is null, but this actor is valid.
In other words, this actor is an enemy interface.
So we do something in that case right?
What do we do?
Well last actor is null.
So we can't really call any functions on that.
But this actor is valid, which means that we're hovering over this actor for the first time.
We weren't hovering over this actor last frame that we know because last actor is null.
So if this is the first frame we're hovering over this actor we can call highlight on this actor.
So we're going to say highlight this actor.
Now I'm going to remove the periods there.
All right.
So that's that scenario.
Now another scenario is that last actor is valid and this actor is null.
What does that mean.
Well if that's the case then we hovered over an enemy interface last frame.
And this frame we're no longer hovering over it.
That's the case where we want to unhighlight that actor.
So we're going to say in this case, unhighlight last actor.
We were hovering over it before, and this frame were now no longer hovering over it, so unhighlight
it.
Okay, so that's three different scenarios.
Can we think of any others?
Well, what about if both actors are valid.
But last actor is not equal to this actor.
So what does that mean.
So last actor is not null.
This actor is not null.
But they're different actors.
That means we were hovering over one enemy, and then suddenly we started hovering over a different
enemy.
Which means that last actor should no longer be highlighted.
But this frames actor.
This actor was not being highlighted last frame.
We're switching from one enemy to another.
So what we need to do here is Unhighlight last actor and highlight this actor.
Right?
So that's one of those weird edge cases that can definitely happen.
Now what's the one other scenario we haven't covered?
Well, what if both actors are valid and are the same actor?
In other words, last actor is this actor.
Well, that just means we're hovering over an enemy.
And we were hovering over that same enemy last frame.
And that means we've already highlighted it because we were hovering over it last frame.
We know that the first time we hovered over it, we called Highlight Actor, so we don't need to highlight
it.
We're still hovering over it, so we don't need to unhighlight it.
So in this case we do nothing.
So these are the five scenarios that we have to handle.
And we can check the validity of last actor and this actor to know which of these functions we should
call or whether we should do nothing.
So the first thing I'm going to do is I'm going to check if last actor and I'm going to explicitly say
last actor equals null pointer.
Equals null pointer is optional.
I'm explicitly stating it as a matter of style here.
Now, if last actor is null, then we know that A is a possibility, right?
Last actor is null and this actor is null.
That's case A, and in that case we do nothing.
But if last actor is null and this actor is valid, well that's case B.
So what we can do inside this if check is check if this actor is valid.
In other words this actor is not null pointer.
So last actor is null.
This actor is valid.
This is case B.
So we're going to say case B.
And for case B we know that we have to call highlight on this actor.
So we're going to call this actor highlight actor.
Now if we have an else case here that would mean that this actor is null.
In other words we'll say both are null.
Do nothing.
And this is case A so we can have case A first let's make this neat.
We'll say case A both are null.
Do nothing.
In other words this else statement is completely unnecessary.
But I'm keeping it here just so we can show that we're checking each of these cases.
So that takes care of A and B.
Now if last actor is not a null pointer well we have an else case here then.
So I'll put a little comment here and say last actor is valid.
Now if last actor is valid but this actor is null, that's case C.
So we'll have an if in here that says if this actor is a null pointer then we can say this is case C
and what do we do in case C we unhighlight last actor.
So we're going to say last actor Unhighlight actor.
So that takes care of case C.
Now if this actor is not null, in other words we'll have an else here.
Then that means last actor is valid and this actor is valid.
In other words, we'll have a comment that says both actors are valid.
Now, if both actors are valid, we have two possibilities.
They are the same actor or they're not the same actor.
So if both actors are valid, we'll have another check that says if last actor is not equal to this
actor.
So they're both valid but they're not equal.
That's case D.
So we have to unhighlight last actor and highlight this actor.
So we're going to say case D.
Case D is Unhighlight last actor.
So we're going to say last actor Unhighlight actor and highlight this actor.
So this actor highlight actor.
And if they're not equal to each other then they're both valid and they're the same.
In other words we'll have an else here that says case E, right.
Both actors are valid and they are the same actor.
So we'll say case E do nothing.
Okay.
So this handles all the cases.
And of course, you know, the code could be more compact if we wanted to.
We have some unnecessary else statements with just comments in them.
That's just to make sure that it's understood what we're doing here.
When on a fresh pair of eyes looks at this function.
So if you want, you can remove all these comments and have a nice clean little algorithm.
I'm going to keep all the comments here because it's more descriptive.
And so now we have this cursor trace going on every frame.
And the only thing left to do is test out this system to see if it works.
And that's going to be your quest.
What I'd like you to do is show some kind of visual feedback.
Whenever we call highlight actor and unhighlight actor, draw a debug sphere, or show some kind of
message in the output log or something so that we know these functions are being called.
You may wish to create, say, a boolean and expose that to blueprint and check that in the blueprint
and draw a debug sphere, say in the tick function.
If the boolean is true, decide how you'd like to implement it and test out our highlighting system
now.
Okay, so just for debug purposes, I'm going to make a boolean here on the enemy class.
So I'm going to say bool be highlighted.
Set it to false by default.
Expose it with a u property and make it blueprint read only.
And all I'm going to do is set this to true and highlight.
Actor, so be highlighted equals true and set it to false in Unhighlight actor so be highlighted equals
false here.
And I'm going to go ahead and run and debug mode and launch the editor.
And we'll draw some debug spheres when the enemy's highlighted.
So I'm going to find my BP spear Goblin.
Open up that blueprint and simply check here.
In event tick are highlighted variable.
So I'm going to get highlighted I'm going to branch off based on this.
And if true I'm going to draw a debug sphere.
So we'll say draw debug sphere.
And we'll draw it for a single frame so duration will be zero.
I'm going to use Get Actor location for the center and I'll leave the radius at.
We'll make it 50 and I'll make it red.
Okay, now, before we test this out, and you may have gotten tripped up on this part of the challenge,
we have to make sure that our enemy is set to block the visibility channel, as that's what we're tracing
with from our player controller.
So we can select the mesh itself and we can go into the collision presets.
And by default we have it set to character mesh.
We're going to have to change that to custom and set visibility to block.
And that way we should be able.
To block the visibility channel.
With our goblin mash.
Now if I press play.
And I enter the console command show collision.
We'll see that that goblin does have a physics asset.
So that's what we should be tracing against.
I'll go ahead and remove that and hide those collision shapes.
And as I highlight I get my sphere.
So it looks like it's working.
Now what happens if we have multiple goblins.
We could test all the use cases.
So I'm going to have two goblins next to each other.
And I'm going to highlight one.
Highlight the other.
So that case is working.
So this seems to be working quite nicely.
Now we had to put this on Goblin spear.
So if we drag out the goblin slingshot for example it's not going to work.
If we drag that out and press play, well it's not doing anything.
We put that logic in event.
Tick for Goblin spear, not the goblin slingshot.
Unfortunately, that's not going to work.
Now, there may be things that we wish to implement in blueprint for all enemies, which means that
we should have an enemy base class that all enemy blueprints should be derived from.
Right now, all enemy blueprints are derived.
If we go to class settings from Aura Enemy, they should all be derived from a common enemy blueprint.
And that way when we have the situation where we want to implement something in blueprint for all enemies,
they'll all get that blueprint logic inherited.
So that's something that we're going to keep in mind and plan on doing as we continue with this project.
For now, we know that our highlighting is working, which is great as indicated by our debug spheres.
So that's awesome.
We no longer need that because we are going to highlight our enemy and a much more interesting way.
And we'll do that next.
I'll see you soon.