Welcome back.
Now here's our cursor trace in our cursor trace.
As long as we're not blocked from highlighting.
If our ability system component doesn't have player block cursor trace as a tag, then we're performing
our line trace and checking to see if the hit actor implements the highlight interface.
We're casting to the eye highlight interface here, and if the hit actor successfully casts to that,
this actor will be non-null.
And we can of course call highlight and unhighlight on it.
Now, if this actor isn't null, then an ability input tag pressed.
We're setting a boolean called Be Targeting and be targeting is set to true if this actor is non null
and false otherwise.
And that way we know when we press the left mouse button whether we should auto run to that location
or whether we should cast a spell targeting this actor.
Now that this actor is a highlight interface and not an enemy interface, things are a little different.
Now.
We need to know if we're targeting an enemy or if we're targeting, say, anything else that we're highlighting.
That's not an enemy, a non enemy.
We'll say one of the things I'd like to be able to hover over is a map entrance, something that takes
us to another map.
So I don't want to have to create a boolean for each type of thing that we can highlight over.
That's kind of silly.
It would be better to have one variable that can have multiple states, like an enum.
So I'd like to have a sort of targeting status instead of a boolean be targeting.
And that way we can have targeting enemy targeting map entrance, something like that.
So for that reason I'm going to make an enum in or a player controller.
Now, this doesn't need to be exposed to blueprint or the reflection system.
It can be a plain old enum.
So I'm going to make an enum called E targeting status.
And E targeting status can be targeting enemy.
It can be targeting map entrance and it can be not targeting.
So we're hovering over something that's not implementing the highlight interface at all.
And then the other two are targeting enemy and targeting map entrance.
Allow us to do different things depending on what we're hovering over.
So we're going to need an E targeting status instead of a B targeting boolean.
So I'm going to find B targeting.
Here it is.
And we're going to replace it with an E targeting status called targeting status.
And targeting status can be set to not targeting as its default value, and we can completely remove
be targeting altogether.
That means we have to go into the player controller where we're using targeting and set it and use it
appropriately.
Now, as soon as ability input tag is pressed, if it's the left mouse button, we're checking this
actor to see if it's null.
Right now this is a highlight interface, so it could be an enemy, but it could be any other actor
that implements the highlight interface.
So we need to know if we're hovering over an enemy or not.
So what we can do is we can see if this actor is an enemy.
Now this actor is a I highlight interface.
This is an I highlight interface object.
Now we could perform an up cast, but that's not really good practice.
We don't really want to take an interface of type.
I highlight interface and cast to an I enemy interface.
That's messy and it's not what I want to do.
Now we can do something else instead in our cursor trace.
Let's just say that this actor and last actor are simply actor pointers.
If we make them actor pointers, then at any point in time we can check to see if they highlight a given
interface.
And we can always call interface functions on them.
Now if we go that route, we can make highlight and Unhighlight blueprint native events so we can call
their static execute versions.
And then we don't have to do this business of casting to an I highlight interface here in C plus.
Plus it's not that expensive of an operation.
It's totally fine that we do this every frame.
But now that I'd like last actor in this actor to just be actor pointers.
That makes it easier further down the line to check this actor for any interface.
So let's do a little bit of a rearrangement here.
First of all I'm going to go into highlight interface dot h.
And instead of being pure virtual functions I'm going to make these blueprint native events.
So we'll give them new functions.
With blueprint native event.
Both of them.
That's the first change I'm going to make.
Now, as soon as I make this change, we have to go into our enemy.
I'm going to move it over to here and make sure that we're overriding the implementation version of
each of these.
That's our next step and we have to go into the cpp file.
And find, highlight and unhighlight and change these to the implementation versions as well.
Now that we've done this, we can't call these functions directly.
We have to call execute on them.
So that's the next thing we have to do in our player controller.
So right here, instead of calling Unhighlight actor, we have to call the execute version.
And we should call the static version.
I highlight interface double colon execute on highlight actor passing in that actor last actor.
Now here's the thing.
Last actor needs to be an actor now, not an eye highlight interface.
So we'll do that too.
But let's change these to execute on highlight actor.
This one has to be this actor.
So let's get rid of those red squiggles by making last actor in this actor actor pointers.
We'll do that right here in player controller dot h.
Here's this actor and last actor.
We're going to make them actor pointers.
And we can now use t object pointer a actor for these.
So now that they're actors back here in or a player controller, these two lines are just fine.
And if we scroll down just a bit now after performing our trace, this actor doesn't need to be set
as a result of a cast anymore.
It can now be set to cursor, hit dot get actor.
But we only want to set this if this actor implements the highlight interface.
So we should check.
We can say if.
Cursor.
Hit dot.
Get actor.
But this could be a null pointer.
So we'll check is valid on it.
So first of all, we're checking to see if that is a valid actor.
We'll also check cursor hit get actor implements.
We'll call implements you highlight interface.
As well.
So if the actor is valid and if it implements the highlight interface, this actor can be set to cursor
hit dot, get actor.
So we can replace that cast and we can move this into the check.
But we also want to make sure in the else case if cursor hit dot get actor is not valid or it does not
implement the interface.
In that case, this actor can be explicitly set to null pointer.
Now we should also check to see if last actor in this actor implement the interface before calling Unhighlight
actor.
So what we can do up here is we can do what we did here, checking if they're valid and if they implement
the interface first.
So we can copy this line with the if statement.
Paste it here.
But we're going to change it a little bit.
We're going to say if is valid.
Last actor.
And last actor implements you highlight interface.
If so, then we'll go ahead and execute Unhighlight actor on it.
And we don't need to check if last actor.
We can do the same thing for this actor.
So we'll go ahead and paste this.
And change is valid to checking this actor and changing implements to checking this actor.
And then we'll call execute Unhighlight actor on this actor.
And now we don't need that line there either.
So that way if we have the player block cursor trace tag, if last actor is valid and it implements
the highlight interface we execute on highlight actor with it, and we do the same thing for this actor.
Now down here, if our cursor hit Get Actor is valid and it implements the highlight interface.
Then we can set this actor.
Equal to that actor.
Otherwise, we set this actor to null.
Next.
We're also checking if last actor is not equal to this actor, and if that's the case, we want to unhighlight
last actor and highlight this actor.
So in this case we're going to make these checks again.
I'm going to just copy these two if statements and paste them down here.
But we're going to change them a little bit for last actor we do want to call Unhighlight actor.
So this part here is what we want to do now instead.
But for this actor we want to call highlight actor.
So if this actor is valid and implements the interface we're going to call highlight Actor instead.
So now these lines can replace these two lines.
So what we're doing is going to result in the same outcome.
Even though we might have more lines of code and it might not look so clean and concise anymore.
But that's okay.
We could even refactor these because checking if the actor is valid, seeing if it implements the highlight
interface, and then executing that interface function on that actor, is all something we could do
in a refactored function that takes in an actor something called highlight actor and unhighlight actor.
We can go back to our player controller and just make two functions to do this void highlight actor,
which takes in an a actor pointer called an actor.
And we can make void Unhighlight actor which takes an a actor pointer called in actor as well.
And these can do that work for us.
And this will clean up our code a little bit.
So let's generate these function definitions.
Now writer gave me inline definitions.
That's okay.
I'm going to cut them and move them into here I'll put them right here just over cursor trace and remove
the inline keywords.
And they're going to do these lines here.
So highlight actor is going to check if the in actor is valid.
And the end actor implements the highlight interface.
And then it calls I highlight interface execute.
And we're going to execute highlight actor on it passing in the in actor there.
And then Unhighlight actor is going to do the same thing almost.
It's going to check if the in actor is valid.
It's going to see if it implements the interface and then it will execute Unhighlight actor on it.
So now we have these really easy to use functions that even could be static.
Let's go ahead and make them static while we're at it.
Static void highlight and Unhighlight.
And now that we have these static functions, these lines of code can be cleaned up quite a bit.
We can call Unhighlight actor on last actor here.
I like that a lot better.
And we can call Unhighlight actor on this actor here.
That makes me feel a lot better because it looks great and we can do the same thing here, right here.
If last actor is not this actor, it'll look a lot less confusing if we just call Unhighlight actor
on Last Actor.
And we call highlight actor on this actor.
And now our code is more readable, looks better, and these functions take care of the interface function
calls.
Okay.
So all of that work for the same result, right?
But the thing is, it was worth it because now this actor and last actor are simply actors.
And when we execute ability input tag pressed, we can now set our targeting status, which is now an
enum.
It's called targeting status.
It's an E targeting status.
And we want to know if we're highlighting an enemy or if we're highlighting anything else.
We know that this actor implements the highlight interface, otherwise it would be null.
We're checking that up here.
If cursor hit Get actor implements highlight interface, we set this actor.
Otherwise we set this actor explicitly to a null pointer.
So we know that it's a highlight interface, but it's in the form of an actor, which is great because
we can see if it implements the enemy interface.
If it does, well, that's a different story, right?
That means our targeting status should be set to.
Targeting enemy.
Otherwise we can set it to targeting map entrance.
Or we could just have targeting Non-enemy because we may have something that's not a map entrance.
Let's just set this to targeting Non-enemy.
And that will cover everything that's not an enemy.
And in that case, right here where we're setting targeting, we're going to do things a little differently
here.
We're going to say.
Targeting status equals.
And we'll check this actor, which is now an actor we can call implements on it.
And we can see if it implements the you enemy interface.
Now.
If it does question mark, then targeting status can be set equal to targeting enemy.
And if it's not, we can set it to targeting Non-enemy.
See how that works.
So now we have targeting status which is an enum rather than a boolean.
And if we're targeting an enemy we can have the same exact behavior.
But if we're targeting a non enemy we could do something else.
So now we don't need this be targeting here.
But we do need to find where we're using it.
And right here on the scroll bar we can see the three places where the code is broken.
Due to using be targeting because it doesn't exist anymore.
So we can scroll down to those.
The first thing here is an ability input tag released.
We're saying if we're not targeting and we're not holding the shift key, well in that case we should
auto run.
But now we should auto run if we're not targeting an enemy.
So instead of checking not be targeting, we can now check targeting status is not equal to targeting
enemy if we're not targeting an enemy.
Then we can auto run if we're not holding the shift key.
And by the end of this auto run, we reset be targeting to false before.
But now, rather than setting it to false, we can set targeting status to not targeting.
Now let's scroll down to where we're using targeting here.
This is an ability input tag held.
We're seeing if we are targeting or the shift key is held down.
We're calling the ability system component ability input tag held.
So we can pass that input tag through.
And this is really only if we're still targeting or we're still holding the shift key.
That means we're targeting an enemy.
So we're going to say if targeting status is equal to targeting enemy.
And by the way, if you're uneasy about using these enum constants like this without fully qualifying
them with the enum name, you could switch over to a scoped enum.
You could make this enum class, and we could even specify this to be a uint eight based enum.
And then we'll have to use targeting status double colon.
Everywhere we're using these enum constant names, and in general that's a little bit better practice
as this makes it clear where these constants are coming from.
So while we're at it, we might as well go up to each of those places where we're using them.
Again, looking at the scroll bar here where I see red and coming back and fixing these like this.
Okay, so now we've made a few changes and we should expect to see the same exact behavior as before.
The only difference now is that this actor and last actor are actors, which we can use to check to
see if they implement any interface, including the enemy interface or the highlight interface.
If they're not highlight interfaces, they're null.
That's a requirement that we make up here in Cursor Trace.
If they're not a highlight interface, they're set to null explicitly.
So all we should do at this point is check to see if everything still works.
We expect at least the same behavior as before.
Otherwise we've made a regression, which is something we would have to then fix.
But if all works the same as before, we now have the additional option to check.
Okay, so player controller line 93 looks like I forgot to fully qualify not targeting right there.
So with that we can compile.
So as long as everything still works as before, this is great because instead of a boolean be targeting,
we now have an enum targeting status, and we can check to see if that status is targeting an enemy
or targeting a non enemy.
And if we're targeting a non enemy.
We can have different behavior whenever we click.
We don't have to cast a spell targeting that thing.
That thing being this actor we could do something else such as run over into a dungeon entrance.
Right.
So let's make sure everything still works.
I'm going to go ahead.
And go to my maps into the load menu and press play.
I'm going to go ahead and delete this slot and make a new one.
Call it Stephen new slot, select slot, press play and I have a fireball and I can highlight highlighting
works.
And if I left click.
I'm targeting it if I left click elsewhere.
Oh, I got a exception.
Ability input tag pressed is checking this actor, which is actually null.
Now, if we're checking if it implements the enemy interface, we actually have to make sure that it's
valid as well, something that can easily be overlooked.
So I'm going to hit stop and just make sure we place that check before accessing this actor.
We are explicitly setting it to null, aren't we.
So what I'm going to say is if is valid this actor that's important.
So we'll do that first.
And if so, then we'll do these two lines.
Like this.
Okay, so with that, let's try this again.
This is why it's good to run in debug mode, because rather than an uninformative crash, we get pointed
to exactly where the problem is.
And this can make your development process much faster and much less painful.
Okay.
We're back.
I'm going to go back to the loading menu and load in.
And let's see, I can highlight I can Unhighlight, I can target, I can click elsewhere.
Okay.
So I should be only doing this if I'm pressing.
The shift key, right?
So we need to auto run if this actor is a null pointer.
That's going to be an ability input tag released.
So we're checking to see if we're not targeting an enemy.
Right.
And we're not holding the shift key down.
Our targeting status should be set to not targeting if we're not highlighting over an enemy.
Now, if this actor is null, then we still need to set.
So we're checking if this actor is valid before setting the targeting status.
Now, this doesn't account for the case when this actor is not valid.
So we do need an else case here.
And if this actor is not valid of course we're not targeting.
So in this case targeting status should be set to targeting status not targeting.
So with that we can compile and let's see how this works.
Okay.
So I'm going to go to the load menu and press play and select a slot and load it in.
And the first thing I'm going to try is clicking.
Now I don't have my nav mesh bounds volume over here.
So clicking is not going to give me auto running behavior unless I go into where my nav mesh bounds
volume is.
And then I can click and get auto run behavior, which is exactly what I expect and want.
Now if I hold shift and left click.
Then I cast my spell and that's the behavior I want as well.
And then the last, of course thing to check would be firing at a enemy target.
So now when we're highlighting something, we know if we're highlighting an enemy and we know if we're
not highlighting an enemy, and this is extremely useful for us if we want to be able to highlight non
enemies, because in that case I would like to still highlight them.
But in addition to that, I would like to do other things.
For example, if I have a Non-enemy object that I'm highlighting and I click on it, I'd like to do
something else.
So in order to start developing that, we're going to need to create new actors that implement the highlight
interface that do additional things.
And we'll do that next.
I'll see you soon.