WEBVTT

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

00:07.950 --> 00:12.600
Now in this video we're going to implement our highlighting behavior.

00:12.630 --> 00:18.840
Now we've already discussed how the player controller has the ability to get whatever actor is underneath

00:18.840 --> 00:19.740
the cursor.

00:19.740 --> 00:23.220
At any given point in time we're going to do this.

00:23.220 --> 00:30.090
And this is something I'd like to do every frame, as it's not a very expensive operation to do, and

00:30.090 --> 00:35.970
we need that sort of responsiveness when hovering the mouse cursor over various different actors.

00:36.120 --> 00:42.360
So in our player controller dot h file, we're going to override a tick function.

00:42.360 --> 00:46.920
We're going to say virtualvoid and we're going to override player tick.

00:47.010 --> 00:49.650
So let's make a definition for this.

00:49.650 --> 00:56.550
And in player tick we're going to perform our trace and handle the highlighting of any actors we hit

00:56.550 --> 00:58.860
that implement the enemy interface.

00:58.860 --> 01:02.070
So I'd like to make a function to call here in player tick.

01:02.070 --> 01:06.210
And this function is going to trace under the cursor.

01:06.210 --> 01:08.760
So I'm going to call this function cursor trace.

01:08.760 --> 01:10.800
And I'm going to make it private.

01:10.800 --> 01:18.360
So down at the bottom we'll make a void cursor trace I'll go ahead and implement this.

01:18.360 --> 01:23.340
And I don't want this inlined I'm going to move it.

01:24.450 --> 01:28.080
Back out into the player controller dot cpp file.

01:28.080 --> 01:32.400
And the first thing I'm going to do is call cursor trace here in player tic.

01:32.910 --> 01:38.430
Now cursor trace is going to involve getting the hit result under the cursor.

01:38.430 --> 01:43.710
That's something that the player controller class inherently has the ability to do.

01:43.710 --> 01:47.700
So to do that we make a hit result f hit result.

01:48.930 --> 01:56.160
We'll call this cursor hit, and the player controller function that we're interested in is get hit

01:56.160 --> 01:57.870
result under cursor.

01:57.870 --> 02:02.640
And this takes a trace channel we can trace against the visibility channel.

02:02.640 --> 02:11.460
So that's going to be a collision channel visibility or simply EQ visibility trace complex is going

02:11.460 --> 02:12.210
to be false.

02:12.210 --> 02:15.630
We want to trace against simple collision only.

02:15.630 --> 02:18.030
And the hit result is our cursor hit.

02:18.510 --> 02:22.410
So after calling this we now have a hit result.

02:22.410 --> 02:29.010
And we can check if we got a blocking hit by checking if cursor hit dot blocking hit.

02:30.270 --> 02:38.490
And if we don't want too much logic nested, we can simply say if not, cursor hit dot blocking, hit

02:38.490 --> 02:42.540
return and the next lines of code won't be indented.

02:42.540 --> 02:46.080
Okay, so what are we going to do with this hit result?

02:46.080 --> 02:50.130
Well, we want to see if it implements the enemy interface.

02:50.130 --> 02:52.410
And we can simply perform a cast here.

02:52.410 --> 02:58.080
So we can say cast and we can cast to I enemy interface.

02:58.200 --> 03:02.370
We're casting cursor hit dot get actor.

03:02.370 --> 03:04.500
That's the actor that was hit.

03:04.950 --> 03:11.010
If this cast succeeds, then the actor we hit implements the I enemy interface.

03:11.370 --> 03:16.140
Now, if the actor is not an I enemy interface, it doesn't implement it.

03:16.140 --> 03:20.550
Then this cast returns a null pointer and we can use that information.

03:20.550 --> 03:23.460
What I'd like to do is have two pointers.

03:23.460 --> 03:31.320
One for the actor that we're hovering over this frame, and one for the actor we hovered over last frame.

03:31.320 --> 03:35.880
Now from frame to frame, they could be the same or they could be different.

03:35.880 --> 03:42.000
And what we do depends on whether they're the same or different, whether they're null or not null.

03:42.000 --> 03:43.560
So I'm going to make two pointers.

03:43.560 --> 03:49.710
And rather than pointers to actors, I'm going to make pointers to the I enemy interface.

03:49.710 --> 03:57.210
So I'm going to forward declare that up here at the top I'm going to say class I enemy interface.

03:58.260 --> 04:07.050
Now I'm going to have an AI enemy interface called Last Actor and an AI enemy interface called this

04:07.050 --> 04:07.560
actor.

04:07.560 --> 04:08.880
Both pointers.

04:09.330 --> 04:11.760
Hey there Steven from the future here.

04:11.760 --> 04:14.610
Coming back with an important message for you.

04:14.610 --> 04:22.080
In the original version of this course, we used a raw pointer for I enemy interface for a member variable,

04:22.080 --> 04:25.890
and this is actually not the correct way to do it.

04:25.890 --> 04:34.080
The correct way to hold a member variable to an interface is by using a special wrapper designed to

04:34.080 --> 04:35.490
hold interfaces.

04:35.490 --> 04:37.650
So it looks like this.

04:37.650 --> 04:41.070
The wrapper is T script interface.

04:41.160 --> 04:46.980
It's a template wrapper, so we have to put the interface type within pointy brackets.

04:46.980 --> 04:49.740
That's going to be I enemy interface.

04:50.460 --> 04:52.200
No asterisk needed.

04:52.200 --> 04:59.220
So our last actor is going to be stored using this interface wrapper T script interface.

04:59.220 --> 05:01.290
And the same goes for this actor.

05:01.290 --> 05:04.170
It's going to be a T script interface as well.

05:04.170 --> 05:10.110
So even though the lectures have raw pointers for I enemy interface, don't do that.

05:10.110 --> 05:13.560
Do it the right way by using t script interface.

05:13.590 --> 05:21.270
Now this does change the way we use these pointers slightly, as we'll see in just a moment.

05:22.460 --> 05:30.380
And back here in Cursor Trace, I'm first going to set last actor equal to this actor each frame.

05:30.830 --> 05:35.600
And then this actor will be set to the result of this cast.

05:35.600 --> 05:36.800
I'm going to move it here.

05:37.510 --> 05:43.990
Steven from the future here again, letting you know that this is where we differ from the video.

05:43.990 --> 05:49.720
Once again, because we're now using a script interface, this cast is unnecessary.

05:49.720 --> 05:51.400
We don't have to cast.

05:51.400 --> 05:57.130
All we have to do is set this actor using cursor hit dot, get actor.

05:57.220 --> 06:04.210
So don't perform the cast, use script interface and set this actor directly.

06:04.210 --> 06:06.610
This script interface is pretty nice, right?

06:06.610 --> 06:11.920
So the cast may fail, which means this actor would be currently a null pointer.

06:11.920 --> 06:17.800
And last actor will store what this actor was before we updated it.

06:17.800 --> 06:22.990
In other words, what it was last frame so we can check the values of these.

06:23.410 --> 06:23.950
Now.

06:23.950 --> 06:29.620
There are several scenarios that we need to cover, and I'd like to write these out with some pseudocode

06:29.620 --> 06:30.550
comments.

06:30.550 --> 06:33.760
So I'm going to make a multi-line comment actually.

06:34.030 --> 06:38.710
And this comment will say line trace from cursor.

06:40.380 --> 06:43.470
There are several scenarios.

06:44.510 --> 06:56.030
So one scenario we'll call it a is that last actor is null and this actor is null.

06:56.300 --> 07:00.830
So this is probably one of the simplest scenarios, right.

07:00.830 --> 07:04.190
Whatever our cursor hit got last frame.

07:04.190 --> 07:06.140
It was not an enemy interface.

07:06.140 --> 07:10.490
It was probably a wall or a floor or something like that.

07:10.490 --> 07:12.140
And the same for this frame.

07:12.140 --> 07:17.120
We're not hitting an enemy interface, so we don't need to try to call the interface function.

07:17.120 --> 07:20.480
In fact, all we have to do in this case is do nothing.

07:20.810 --> 07:24.350
So that's the easiest scenario.

07:24.350 --> 07:32.780
Now another scenario is that last actor is null, but this actor is valid.

07:32.780 --> 07:37.400
In other words, this actor is an enemy interface.

07:37.400 --> 07:39.680
So we do something in that case right?

07:39.680 --> 07:40.490
What do we do?

07:40.490 --> 07:42.740
Well last actor is null.

07:42.740 --> 07:45.440
So we can't really call any functions on that.

07:45.440 --> 07:52.640
But this actor is valid, which means that we're hovering over this actor for the first time.

07:52.640 --> 07:57.830
We weren't hovering over this actor last frame that we know because last actor is null.

07:57.830 --> 08:04.280
So if this is the first frame we're hovering over this actor we can call highlight on this actor.

08:04.280 --> 08:08.240
So we're going to say highlight this actor.

08:09.440 --> 08:11.510
Now I'm going to remove the periods there.

08:11.540 --> 08:12.230
All right.

08:12.230 --> 08:14.090
So that's that scenario.

08:14.930 --> 08:23.480
Now another scenario is that last actor is valid and this actor is null.

08:23.660 --> 08:25.130
What does that mean.

08:25.250 --> 08:31.730
Well if that's the case then we hovered over an enemy interface last frame.

08:31.730 --> 08:34.400
And this frame we're no longer hovering over it.

08:34.400 --> 08:38.030
That's the case where we want to unhighlight that actor.

08:38.030 --> 08:42.500
So we're going to say in this case, unhighlight last actor.

08:43.270 --> 08:48.760
We were hovering over it before, and this frame were now no longer hovering over it, so unhighlight

08:48.760 --> 08:49.270
it.

08:49.300 --> 08:52.120
Okay, so that's three different scenarios.

08:52.120 --> 08:53.980
Can we think of any others?

08:54.580 --> 08:57.610
Well, what about if both actors are valid.

08:59.770 --> 09:04.960
But last actor is not equal to this actor.

09:05.980 --> 09:07.330
So what does that mean.

09:07.330 --> 09:10.240
So last actor is not null.

09:10.240 --> 09:11.890
This actor is not null.

09:11.890 --> 09:14.230
But they're different actors.

09:14.230 --> 09:19.930
That means we were hovering over one enemy, and then suddenly we started hovering over a different

09:19.930 --> 09:20.500
enemy.

09:20.500 --> 09:24.520
Which means that last actor should no longer be highlighted.

09:24.520 --> 09:26.620
But this frames actor.

09:26.620 --> 09:30.160
This actor was not being highlighted last frame.

09:30.160 --> 09:32.620
We're switching from one enemy to another.

09:32.620 --> 09:40.720
So what we need to do here is Unhighlight last actor and highlight this actor.

09:41.980 --> 09:42.280
Right?

09:42.280 --> 09:46.240
So that's one of those weird edge cases that can definitely happen.

09:46.240 --> 09:49.210
Now what's the one other scenario we haven't covered?

09:49.210 --> 09:55.030
Well, what if both actors are valid and are the same actor?

09:55.030 --> 09:57.970
In other words, last actor is this actor.

09:57.970 --> 10:00.670
Well, that just means we're hovering over an enemy.

10:00.670 --> 10:04.690
And we were hovering over that same enemy last frame.

10:04.690 --> 10:10.390
And that means we've already highlighted it because we were hovering over it last frame.

10:10.390 --> 10:16.060
We know that the first time we hovered over it, we called Highlight Actor, so we don't need to highlight

10:16.060 --> 10:16.360
it.

10:16.360 --> 10:19.780
We're still hovering over it, so we don't need to unhighlight it.

10:19.780 --> 10:22.210
So in this case we do nothing.

10:22.780 --> 10:26.440
So these are the five scenarios that we have to handle.

10:27.100 --> 10:32.890
And we can check the validity of last actor and this actor to know which of these functions we should

10:32.890 --> 10:35.290
call or whether we should do nothing.

10:35.290 --> 10:42.070
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

10:42.070 --> 10:44.050
last actor equals null pointer.

10:45.370 --> 10:47.590
Equals null pointer is optional.

10:47.590 --> 10:51.550
I'm explicitly stating it as a matter of style here.

10:51.550 --> 10:56.680
Now, if last actor is null, then we know that A is a possibility, right?

10:56.680 --> 10:59.680
Last actor is null and this actor is null.

10:59.680 --> 11:03.220
That's case A, and in that case we do nothing.

11:03.220 --> 11:07.570
But if last actor is null and this actor is valid, well that's case B.

11:07.570 --> 11:14.440
So what we can do inside this if check is check if this actor is valid.

11:14.440 --> 11:17.440
In other words this actor is not null pointer.

11:18.440 --> 11:20.390
So last actor is null.

11:20.390 --> 11:22.070
This actor is valid.

11:22.070 --> 11:23.450
This is case B.

11:23.600 --> 11:26.360
So we're going to say case B.

11:26.360 --> 11:31.070
And for case B we know that we have to call highlight on this actor.

11:31.070 --> 11:35.540
So we're going to call this actor highlight actor.

11:35.630 --> 11:41.210
Now if we have an else case here that would mean that this actor is null.

11:41.210 --> 11:44.750
In other words we'll say both are null.

11:45.050 --> 11:46.490
Do nothing.

11:47.510 --> 11:52.850
And this is case A so we can have case A first let's make this neat.

11:52.850 --> 11:55.880
We'll say case A both are null.

11:55.880 --> 11:56.480
Do nothing.

11:56.480 --> 12:00.530
In other words this else statement is completely unnecessary.

12:00.530 --> 12:06.080
But I'm keeping it here just so we can show that we're checking each of these cases.

12:06.080 --> 12:08.450
So that takes care of A and B.

12:08.780 --> 12:13.970
Now if last actor is not a null pointer well we have an else case here then.

12:14.510 --> 12:19.490
So I'll put a little comment here and say last actor is valid.

12:19.490 --> 12:25.280
Now if last actor is valid but this actor is null, that's case C.

12:25.490 --> 12:33.950
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

12:34.670 --> 12:38.120
and what do we do in case C we unhighlight last actor.

12:38.120 --> 12:42.920
So we're going to say last actor Unhighlight actor.

12:43.400 --> 12:45.620
So that takes care of case C.

12:45.650 --> 12:50.000
Now if this actor is not null, in other words we'll have an else here.

12:50.000 --> 12:54.320
Then that means last actor is valid and this actor is valid.

12:54.320 --> 12:58.370
In other words, we'll have a comment that says both actors are valid.

12:58.370 --> 13:02.000
Now, if both actors are valid, we have two possibilities.

13:02.000 --> 13:05.270
They are the same actor or they're not the same actor.

13:05.270 --> 13:12.800
So if both actors are valid, we'll have another check that says if last actor is not equal to this

13:12.800 --> 13:13.550
actor.

13:15.630 --> 13:18.390
So they're both valid but they're not equal.

13:18.390 --> 13:19.530
That's case D.

13:19.680 --> 13:23.460
So we have to unhighlight last actor and highlight this actor.

13:23.460 --> 13:25.350
So we're going to say case D.

13:26.940 --> 13:30.000
Case D is Unhighlight last actor.

13:30.000 --> 13:36.150
So we're going to say last actor Unhighlight actor and highlight this actor.

13:36.150 --> 13:40.110
So this actor highlight actor.

13:40.380 --> 13:45.300
And if they're not equal to each other then they're both valid and they're the same.

13:45.300 --> 13:49.350
In other words we'll have an else here that says case E, right.

13:49.350 --> 13:52.770
Both actors are valid and they are the same actor.

13:52.770 --> 13:55.620
So we'll say case E do nothing.

13:57.000 --> 13:57.570
Okay.

13:57.570 --> 13:58.950
So this handles all the cases.

13:58.950 --> 14:04.290
And of course, you know, the code could be more compact if we wanted to.

14:04.290 --> 14:08.430
We have some unnecessary else statements with just comments in them.

14:08.430 --> 14:14.070
That's just to make sure that it's understood what we're doing here.

14:14.070 --> 14:17.430
When on a fresh pair of eyes looks at this function.

14:17.430 --> 14:22.920
So if you want, you can remove all these comments and have a nice clean little algorithm.

14:22.920 --> 14:26.040
I'm going to keep all the comments here because it's more descriptive.

14:26.040 --> 14:30.210
And so now we have this cursor trace going on every frame.

14:30.210 --> 14:34.680
And the only thing left to do is test out this system to see if it works.

14:34.680 --> 14:36.510
And that's going to be your quest.

14:36.720 --> 14:40.800
What I'd like you to do is show some kind of visual feedback.

14:40.800 --> 14:48.180
Whenever we call highlight actor and unhighlight actor, draw a debug sphere, or show some kind of

14:48.180 --> 14:53.160
message in the output log or something so that we know these functions are being called.

14:53.160 --> 15:00.330
You may wish to create, say, a boolean and expose that to blueprint and check that in the blueprint

15:00.330 --> 15:03.810
and draw a debug sphere, say in the tick function.

15:03.810 --> 15:10.440
If the boolean is true, decide how you'd like to implement it and test out our highlighting system

15:10.440 --> 15:11.370
now.

15:14.370 --> 15:22.020
Okay, so just for debug purposes, I'm going to make a boolean here on the enemy class.

15:22.020 --> 15:25.590
So I'm going to say bool be highlighted.

15:26.850 --> 15:28.890
Set it to false by default.

15:28.890 --> 15:34.800
Expose it with a u property and make it blueprint read only.

15:35.490 --> 15:40.140
And all I'm going to do is set this to true and highlight.

15:41.000 --> 15:50.360
Actor, so be highlighted equals true and set it to false in Unhighlight actor so be highlighted equals

15:50.360 --> 15:51.200
false here.

15:51.200 --> 15:55.940
And I'm going to go ahead and run and debug mode and launch the editor.

15:55.940 --> 16:00.440
And we'll draw some debug spheres when the enemy's highlighted.

16:00.770 --> 16:05.000
So I'm going to find my BP spear Goblin.

16:05.000 --> 16:09.440
Open up that blueprint and simply check here.

16:09.440 --> 16:12.920
In event tick are highlighted variable.

16:12.920 --> 16:18.110
So I'm going to get highlighted I'm going to branch off based on this.

16:18.110 --> 16:20.840
And if true I'm going to draw a debug sphere.

16:20.840 --> 16:23.150
So we'll say draw debug sphere.

16:25.090 --> 16:29.740
And we'll draw it for a single frame so duration will be zero.

16:29.770 --> 16:36.910
I'm going to use Get Actor location for the center and I'll leave the radius at.

16:36.910 --> 16:40.180
We'll make it 50 and I'll make it red.

16:41.920 --> 16:48.010
Okay, now, before we test this out, and you may have gotten tripped up on this part of the challenge,

16:48.010 --> 16:54.400
we have to make sure that our enemy is set to block the visibility channel, as that's what we're tracing

16:54.400 --> 16:56.290
with from our player controller.

16:56.290 --> 17:04.270
So we can select the mesh itself and we can go into the collision presets.

17:04.270 --> 17:07.600
And by default we have it set to character mesh.

17:07.600 --> 17:12.520
We're going to have to change that to custom and set visibility to block.

17:12.520 --> 17:14.740
And that way we should be able.

17:15.640 --> 17:17.710
To block the visibility channel.

17:18.470 --> 17:20.330
With our goblin mash.

17:21.220 --> 17:23.110
Now if I press play.

17:25.210 --> 17:28.780
And I enter the console command show collision.

17:28.780 --> 17:32.140
We'll see that that goblin does have a physics asset.

17:32.140 --> 17:34.420
So that's what we should be tracing against.

17:34.960 --> 17:40.210
I'll go ahead and remove that and hide those collision shapes.

17:40.210 --> 17:46.480
And as I highlight I get my sphere.

17:46.900 --> 17:48.580
So it looks like it's working.

17:48.580 --> 17:51.940
Now what happens if we have multiple goblins.

17:51.940 --> 17:53.350
We could test all the use cases.

17:53.350 --> 17:55.870
So I'm going to have two goblins next to each other.

17:56.200 --> 17:58.270
And I'm going to highlight one.

17:58.480 --> 17:59.650
Highlight the other.

17:59.650 --> 18:01.300
So that case is working.

18:01.300 --> 18:03.790
So this seems to be working quite nicely.

18:03.790 --> 18:06.400
Now we had to put this on Goblin spear.

18:06.400 --> 18:11.770
So if we drag out the goblin slingshot for example it's not going to work.

18:11.770 --> 18:17.020
If we drag that out and press play, well it's not doing anything.

18:17.020 --> 18:18.520
We put that logic in event.

18:18.550 --> 18:23.560
Tick for Goblin spear, not the goblin slingshot.

18:23.560 --> 18:26.140
Unfortunately, that's not going to work.

18:26.140 --> 18:32.530
Now, there may be things that we wish to implement in blueprint for all enemies, which means that

18:32.530 --> 18:39.010
we should have an enemy base class that all enemy blueprints should be derived from.

18:39.010 --> 18:42.010
Right now, all enemy blueprints are derived.

18:42.010 --> 18:50.470
If we go to class settings from Aura Enemy, they should all be derived from a common enemy blueprint.

18:50.470 --> 18:56.710
And that way when we have the situation where we want to implement something in blueprint for all enemies,

18:56.710 --> 19:00.310
they'll all get that blueprint logic inherited.

19:00.310 --> 19:07.540
So that's something that we're going to keep in mind and plan on doing as we continue with this project.

19:07.540 --> 19:15.280
For now, we know that our highlighting is working, which is great as indicated by our debug spheres.

19:15.280 --> 19:16.570
So that's awesome.

19:16.570 --> 19:22.060
We no longer need that because we are going to highlight our enemy and a much more interesting way.

19:22.060 --> 19:23.500
And we'll do that next.

19:23.980 --> 19:25.000
I'll see you soon.
