Welcome to your next quest.
Okay, so you finished your last assignment, and the project manager says, that's great, excellent
thinking when it came to taking the level into account.
Now, I have another task for you.
I'd like you to bind to that on XP change delegate we created in the player state and I'd like you to
bind to it on the widget controller for the overlay.
Why?
Because our overlay is going to have an XP bar, so it needs to know when our XP changes.
Now as a bonus, see if you can figure out what that percent will be for the XP bar.
Now this is deceptively a little bit more difficult than you might think as the way that we calculate
the percent depends on what level we are.
So see if you can figure that out.
And as a hint, does the overlay widget controller need level up info because level up info contains
those level up requirements for XP for each level.
So see if that's something you're going to need.
And if you do, where should level up info go?
We created the data asset blueprint, but we never set that on any of our classes, right?
So try to think about where a good spot for that level up info should go and try to keep in mind that
we may need that level up info in our widget controller, both on clients and server.
And just remember that the game mode doesn't exist on the clients, it only exists on the server.
So right away I'm telling you it probably shouldn't go in the game mode.
So give it a little bit of thought, see if you can figure that out.
And if you're up for a bonus challenge, give that a try.
But either way, I'd like you to bind to on XP changed in the overlay widget controller.
So pause the video and conquer this quest now.
Now we're going to need to bind to our on XP change delegate in our widget controller.
Now it's possible that we may decide to bind to on level changed as well.
So that's something to keep in mind.
But when we bind to on XP change, we're going to want to figure out what that XP bar percent is going
to be.
So we'll keep that in mind as well.
Well, the first thing we should do is go to our widget controller class that's going to be in UI widget
controller and it's our overlay widget controller.
Here is where we're going to want to bind to the delegate.
Right?
So I would like to make a callback function to bind because it's going to be a little bit more complicated
than just passing through that value up to widgets.
We're going to be performing some calculations, right?
So I'm going to make a function in the protected section, and this is going to be for when our XP changes,
we're going to bind it to the delegate on the player state called on XP change delegate, which can
only have functions bound to it that can receive an INT 32.
So here in overlay widget controller, we're going to make a function.
It's going to be void and I'm going to call this on XP changed, which will take an INT 32 called New
XP and we'll go ahead and generate the definition for it.
And before we figure out what to do in this function, we're going to bind it.
So we need to go up to our bind callbacks to dependencies where we're binding to all of our callbacks
and we have a few bound to the ability system component.
We have some bound to the attributes set, but we don't have anything bound to the player state.
So we're going to have to get our player state, aren't we?
So up here as we're getting our aura attributes set, I'd like to cast to the Aura player state as well.
So I'm going to go ahead and create a const pointer to it and it's going to be of type A or a player
state.
And I'm going to go ahead and include the header file.
So alt enter and writer will do that for me.
Adding the include for it to send player slash player state and I'm going to call this or player state.
Now I'm going to use cast checked for this.
By the way, in a package build, this compiles as a static cast.
So you have that bit of optimization there too.
Now, this will be an A or a player state and we're going to cast our player state variable that all
widget controllers have.
Now we know that we have the aura player state and we can bind callbacks to the aura player state.
I'm going to actually move this aura player state local variable up to the first line and the first
thing we'll do is bind to the player state callbacks and then the attribute set will be after that.
So after getting the aura player state, we can take Aura player state and we can access that public
on XRP changed delegate and we can bind to it.
We're going to use Add new object.
And with the user object being this and the function being the address of you overlay widget controller
on XP changed.
And of course we're going to get red squiggles here because now we're actually changing something on
the player state so it can't be a const pointer anymore.
If we're going to want to bind callbacks to delegates.
Okay.
So now we have a callback bound to on Change delegate so we'll know in the widget controller when our
XP has changed.
Now we need to broadcast something up to our progress bar in Blueprint.
But the thing is, we can't just broadcast the total amount of XP.
We need to find out what the percentage of that bar should be and that's going to involve the use of
our level up info.
So what class should have the level up info?
Well, I think the player state is probably a good class to keep that level up info as it is the class
that has the level stored on it and also the player state is replicated.
So if we access the player state in our widget controller on the client, we can get level up info from
there.
So I think a great place for it is here on Aura Player State right here in the public section.
So I'm going to go ahead and declare a public variable.
It's going to be a T object pointer.
Now its type will be you level up info and we can forward declare that we don't have to include level
up info up here.
We can just say class.
You level up info here and we can create this variable called level up info and we can make it a new
property with edit defaults only so we can set it in our player state blueprint.
So we'll remember to do that.
And in our widget controller we can access that when determining how much our percent bar should show.
And that's going to be all in on XRP changed.
And once we figure out that percent, we can broadcast something up to our widgets.
Now what are we going to broadcast?
Well, we're going to broadcast a float for the percent of our XRP bar.
And if we want to broadcast a float, we have an on attribute change signature we can use.
It's not an attribute.
Our percentage for the XRP bar technically isn't a gameplay attribute, but we can still use this same
delegate.
If the name bothered us, we could create a new delegate with a different name capable of broadcasting
a float.
But that's a little bit silly.
I think we could just use this delegate signature and create a new delegate.
In fact, I'm going to give it a new property so I can take one of our other attribute changed signatures
and copy it.
And down here at the bottom of it in the public section, I'm going to paste, but change the name and
this will be on XRP.
Percent changed and we'll tack on delegate at the end there and this will be gas slash XRP for the category.
So now we have a delegate to broadcast.
Now we just need to figure out what that percent should be and broadcast that in on XRP changed.
Now here in on XRP changed.
We're going to want the level up info and we know that that exists on the player state so we can access
the player state with a cast checked.
We'll go ahead and copy that line and paste it there.
Don't worry about using cast checked each time our XRP changes.
As I mentioned, this turns into a static cast in builds, so this is not something to worry about performance
wise.
So in on XRP changed, What exactly are we going to do here?
Well, we need to get our level up info, so I'm just going to say you level up info, we'll get a pointer
to that.
We'll call this level up info and we'll use our aura player state.
And get that level up info.
Public variable.
And what we'll do with our level up info is we're going to perform lookups for data from it.
So this can be a const pointer and if we like, we can perform a check on that.
We can say check level up info and we can even use a check f if we want a descriptive message.
When level up info is a null pointer, we can say unable to find level up info.
Please fill out.
Or a player state blueprint.
So we'll definitely know if we forgot that we'll get a crash or an assertion in debug mode with that
message.
So how are we going to know what our percent bar should be when we know what our XP is?
Well, level up info has a handy function that can tell us what level we should be for a given experience
value.
We can take our level up info and call that function.
It's called find level for XP.
It takes an int 32 we can pass in new XP.
Now we made level up info, const and find level for XP could be const, so one has to change.
Either this function can be const and then our pointer can be const or the function can not be const.
But if it's not const then our pointer can't be const.
Well, if it's just a lookup to find level for XP, it could easily be const.
We can go into our public ability system data folder and to level up info and we can make find level
for XP a const function.
We'll have to do it in the header and the CPP file.
And once we do that, then here in the widget controller we can call this function no problem.
So this gives us the level.
It's an int 32 returning function so we can create an int 32 called level and now we know what our level
is.
Now that's important because we need to know how much XP to get from the beginning of one level to the
end of that level.
In other words, that Delta XP requirement.
And if we're going to be finding Delta XP from one level to another, we should know what that max level
is.
So we should have a const int 32 called max level.
I said const, we'll make it constant, a second max level equals and we'll get our level up info and
access that level up information and get the number of elements in it.
So the number of elements in it should be the max level possible.
So these both can be constants.
So we know our current level.
We know the max level possible.
We need to figure out that delta between the amount of XP when we first reached our current level to
the amount of XP at the end of that level.
In other words, it's level requirement.
So first I'm going to place a check here that says if level is less than or equal to max level, that
way, we're in range here.
But I'm also going to have an and and make sure level is greater than zero.
Because it's a zero level doesn't make sense either.
Now, the level requirement for our current level that can be accessed from our level up information.
So I'm going to make a const int 32 called level requirement or level up requirement.
And that's going to be our level up info.
Accessing level up information from it and indexing it at our current level.
But that's a struct which contains the level up requirements, so we have to access that.
Now I want to know the previous level requirement.
In other words, the XP that we were at at the very beginning of our current level, that's the level
up requirement for the previous level.
So I'm going to make a const int 32 called previous level up requirement and that's going to be level
up info.
Accessing level up information, indexing it at level minus one, the previous level, and then getting
that level up requirement.
So now we have both of those.
Now we can see the difference between them.
A delta, we can say const int 32, Delta level requirement, we'll call it, and that's simply going
to be the current level requirement minus the previous one.
And it's actually level up requirement minus previous level up requirement.
The reason we need this delta is so that we know what to divide our current exp value by.
Now current exp remember is a cumulative total.
What we need is how much more from the beginning of our current level we have.
In other words, if we're level two and we had to reach 300 to reach level two and now we have 400 then
to calculate the percent and the level up requirement for level two is, say, 900.
In other words, 600 more, then our percent would be 100 divided by 600.
It would be our current Exp minus last level's requirement.
So we're going to say const int 32 exp for this level equals and it's going to be new exp.
That's our current value minus previous level up requirement.
And finally we can calculate the exp bar percent and this is going to be a float because we're going
to do a division, a const float.
We're going to call this exp bar percent.
And this will be our XP for this level.
Divided by the Delta level requirement.
Now, we don't want to perform integer division.
We want to perform float division so we can static cast these to floats.
Or we could have done an implicit cast and created floats for these variables.
Either one, it doesn't really matter.
We were working with integers up to this point, so this is the point at which we need them to be floats
so we can say static cast.
And this is just a regular old C plus plus static cast to float.
We're going to static cast both of these to floats.
So we'll static cast this one and we'll static cast this one and now we have exp bar percent we can
broadcast we can take our on exp changed and actually our delegate is called on exp percent changed
delegate will broadcast this.
This can broadcast a float.
We're going to broadcast exp bar percent.
All right.
Well, that was quite an experience.
And if you did this all on your own, I'm pretty impressed because that was quite a problem to find
a solution to.
Pretty nice.
And by the way, this could be a const function or a player state could be a const variable.
We can just make these const correct on exp changed in the header file can be made const here.
And with that, we now have our widget controller responding to XRP changes.
Now it'd be great to just go into our XRP bar in Blueprint and bind to this on XRP Percent Change Delegate.
You can see that I just compiled while I was talking there.
But we're never actually changing our XRP.
That's going to be something we have to do when we accomplish something like Slay an Enemy or whatever.
So there's really nothing we can do at this point to test it unless we just change our XRP on the player
state willy nilly.
But that's okay.
We'll test it soon enough.
We're going to implement this in stages.
For now, we have our callback.
We even have this code to find out the XRP bar percent.
We'll see very soon if that's correct.
And things are looking good.
So excellent job with this quest and we'll continue in the next video.
I'll see you soon.