Welcome back.
Now, one of the last features I'd like to implement in this game project is the ability of our enemies
to spawn loot.
Now, we don't have too much by the way of loot, but we do have some things we can pick up if we go
into blueprints into actor.
We do have crystals and we have potions.
Now, a loot system can take lots of types of loot into account.
So even though we only have crystals and potions, which are pretty much the same thing, you could
use this system for any types of loot that you add to your game, and I do plan on covering more topics
in future series that have to do with more types of loot, but since we just have crystals and potions,
that's what we'll use.
But we can make a system that's flexible enough to account for other types of loot as well.
Now, I'd like my loot system to be data driven, and I'd like each enemy to be able to spawn loot.
And for this particular system, I'm going to keep it simple and just have tiers of loot that we can
have, and each type of item can have its own chance to spawn, and our enemies can just reference that
data asset.
So let's make a data asset for our loot tiers.
I'm going to go to public ability system and Data.
I have all my data assets here, so I'd kind of like my loot tiers to be here too.
So I'm going to create a new class, choose all classes and search for Data Asset and just choose Data
Asset.
And this can go in data.
And I'm going to call this loot tears tears.
Let's create this class and go ahead and close the editor and get that new class open.
So I'm going to get the cpp file open first.
It's an ability system data.
Here's loot tears.
And I'll go ahead and open up the h file.
Now for loot I'm going to want an array of some kind of structure.
So I'm going to define a new struct here struct.
And I'm going to call this f loot item.
So we can define the properties for spawning a specific item.
And this can be a u struct blueprint type.
We do need generated body.
And we'll add some variables and make sure they're initialized.
And this is reminding me that there was an error that I saw when compiling earlier, referencing an
uninitialized variable in a struct.
I believe it was in my load screen save game and it was in F saved ability.
My ability level.
I'm going to initialize that to one just to get rid of that error.
That's for variables.
Arrays can be empty.
So we're going to add a subclass of of type A actor.
And this will be the class to spawn.
We'll call this loot class.
And this one is going to be a U property with Edit Anywhere blueprint read only.
I want to be able to read this from blueprint in the event graph, and we'll give it a category of loot
tears.
Pipe symbol spawning.
Looks like I have two equal signs.
Okay, so we have the class to spawn.
We can also have a chance to spawn.
So for each loot item we can have a chance to spawn that item.
We'll give it zero by default, but we'll set this as well from our blueprint I'm going to copy this
Uproperty paste it here and use the same U property.
And we can also have a max number of these that we can spawn.
So let's say we'd like to spawn multiple items of the same type, but we need a maximum number to spawn.
So this can be an int 32.
Called max number to spawn.
And then finally we can provide an option to override the level of that loot.
Let's say we're spawning in a potion.
Potions can have their own level.
If we want to override that, then we should have that as an option.
So I'm going to have a bool called be loot level override and set this to true by default.
And if it's true, here's what I'd like to do.
I'd like to set the level of the lutte class to the level of the enemy spawning it.
But if it's false, then we'll just spawn it in without setting its level.
So whatever its level is.
So this gives us a struct, and we can give ourselves an array of these by creating a public section
in the data asset and creating a t array of this new structure f loot item.
And this can be our loot items array.
And this can be filled in in the data asset as well.
We can make it edit anywhere or edit defaults only, and we can make this blueprint read only if we'd
like to access it from the event graph and the same category as the others.
Loot tiers.
Spawning.
And finally, I'd like to handle retrieving loot items with a simple function that can return an array
of F loot items.
And this can do the math for us when it comes to checking the number of items to spawn and the chance
to spawn, we can take all that into account with a function and return a t array of items to spawn,
so we can have a t array of f loot item.
Returning function called get loot items.
And this function can do the math for us.
So we'll go ahead and generate this definition.
And all I want to do is return an array of items.
So I'll make a t array of f loot item.
I can just copy this.
Called return items and at the very end, return return items.
Kind of sounds like we're taking some items to the store with our receipt and returning them for our
money back, but we're actually just returning an array of items, and that means I want to loop over
my loot items.
So for flute item, I'm going to make that a reference called item.
For each item in loot items I'm going to perform this loop, and inside of this I'm going to loop over
for each item their max number to spawn.
So I'm at a given item here and it has its own max number to spawn.
So I'm going to use an int 32.
I started off at zero and loop as long as it's less than item dot max number to spawn and plus plus
I or I plus plus either one.
So there's a max number of items to spawn.
Let's say we have a red potion.
You can spawn a maximum of three of them when you kill an enemy, right?
So for that specific item, if you can spawn a maximum of three, then I want to loop over that number
three and three times.
I want to roll the dice and see if our chance to spawn dictates that we should spawn.
So we're going to say if and we can do a rand range like we've done before F math Rand range.
We'll get a random value between 1 and 100.
And see if it's less than item dot chance to spawn.
Now chance to spawn is a float, so if you wanted to, you could make this float math.
You could make this a float Randrange.
You could use f randrange and use one dot f and 100 dot f if you wanted to do that.
Totally up to you.
But once we've done this, then we'll know for this particular item whether we should spawn it this
particular time.
And if your luck is really good, then for each of the iterations of this loop will spawn an item.
We'll actually add it to this return items array and eventually spawn it later on down the pipeline.
But with perfect roles of this 100 sided die, the best you can do is spawn max number to spawn for
each item.
So if we pass this if check then I'll make a new f loot item called new item.
And I'll go ahead and take new item and set its loot class equal to item dot loot class.
I'll set new item dot be loot level override to item dot be loot level override and then add this to
return items.
We'll say return items dot add not unique because we're going to be adding copies of the same one depending
on max number to spawn and depending on this die roll.
So we're going to add new item as a copy potentially multiple times.
And then we'll add return items.
So if we loop over return items we could go on to spawn one actor for each of the items in here.
So each loot item, even though there might just be one in our loot tiers, loot items array depending
on the chance to spawn and the max number to spawn, there may be duplicates and return items which
results in us spawning them multiple times.
Okay, so we have a data asset.
We could fill that in in the editor.
Our next step is to go ahead and create the ability to spawn loot whenever an enemy dies.
That's something I'd like to do.
So this I'd like to handle most of it in blueprint, because it's going to be something that will be
easier to implement in blueprint, especially if we're spawning multiple items from an enemy.
Let's say we'd like to spawn them one after the other with a short delay in between.
That kind of async type stuff is easy to do in blueprint and see the flow of logic, so I'd like to
do that in blueprint, and for that reason I'm just going to go to my aura enemy class and add a blueprint
implementable event for spawning loot.
So we'll go ahead and go to the character folder into Aura Enemy and just add a function here to spawn
loot.
We can make it a protected function.
It can be a void function called Spawn loot, and we can make this a blueprint implementable event.
So blueprint implementable event.
And we can simply call this when the enemy dies.
And that can be on the server.
We can go to the enemies die function and simply call spawn loot.
We could do it right before calling super Die spawn loot.
Now when we spawn loot, even though we're implementing this in blueprint, we're going to need to access
the data asset.
It needs to exist somewhere, and since we'll be spawning loot on the server, we could put this onto
the game mode class.
And when an enemy dies, we could simply retrieve that data asset from the game mode, perhaps with
a blueprint function library function that's static.
So we could do that.
So if we're going to add it to the game mode we need to go to aura game mode base and add a new data
asset.
Let's find where we have our others.
They're up here.
We can simply copy one of these and paste it and change its type from what it was before to you.
Loot tears and add a forward declaration and call this loot tears and change the category to Loot tears.
And we can simply make a function to retrieve this in our ability system library, much like get ability
info for instance.
So I can simply copy this function, paste it beneath and call this get Loot tears and this can retrieve
a you loot tears.
Object, we can forward declare it here and generate the definition, and it can be very similar to
get ability info, we can copy its definition here where we're getting our game mode returning null
if the game mode is null.
Otherwise get the game mode and return loot tears from it.
And that's going to result in the inclusion of the correct header here, at least with writers auto
include.
And now we have a get loot tears, which we can call from blueprint to get the loot tears provided that
we check for validity, of course.
So with that, our enemies are calling spawn loots.
It's up to us to implement that.
We can implement it in blueprint and we have to create this data asset, fill it in and add it to our
aura game mode base blueprint so that we can access that.
So a couple things to do in the editor.
Let's compile and launch.
So back in the editor we can make our data asset blueprint and ability system data.
We'll make a new one here.
We'll go to miscellaneous.
Data.
Asset.
Searching for loot.
Tears.
And we'll make our da lute tears.
So what is lute tears going to contain?
Well, it has under the spawning category, lute items.
And we can click plus to add one, expand its dropdown and choose a lute class.
So for the lute class we can search for a couple things.
We can search for beep health potion and give it a chance to spawn for testing purposes.
I'm going to give it 100% chance to spawn and about three, not 33, but three max numbers to spawn,
which should result in spawning three each time.
And for lute level override, well, we can decide what to do with that boolean when we spawn it.
Now that's for the health potion.
I'm going to add another for the Mana Potion.
So choosing Mana Potion and again for testing purposes, I'll set the chance to spawn to 100 and the
number to spawn to three.
The max number.
And we'll add another one for our crystals, one for each.
We'll have a BP health crystal again 100% chance to spawn.
We'll spawn two of them and our mana crystal BP mana Crystal 100% chance to spawn and we'll spawn two
of them.
Now with these numbers, we should be spawning six potions and four crystals, all from the same enemy
100% of the time.
That's a lot to spawn in one place, so we're going to want to devise a method to separate them out
somehow, right?
So let's go into our enemy base blueprint as we're going to implement Spawn Lute so we can go to BP
enemy base and implement spawn lute.
So we're going to type spawn lute, get event spawn lute and figure out how we can spawn the lute.
What are we going to do here.
Now the first thing we need is the lute tears data asset.
Right.
That's the first thing we have to do is get that so we can right click type get lute tears.
And here's our ability system library function.
It needs a world context object.
And in hindsight I think it would have been better if we went to our ability system library and set
that world context object to default to self.
Now we do have an example of how to do that in our meta attributes up here for get spell menu widget
controller, for example, I'm going to copy that and place it right here in the U function for lute
here.
So next time we compile we'll have that defaulted to self.
But for now I can get a reference to self and pass it in.
Now we need to check to make sure this is a valid loop tiers data asset.
So is valid and will only continue if it is.
Let's give ourselves some room here.
I'm going to move this off to the side.
And let's think about what we're going to do with get loot tiers.
Remember, get loot tiers has a function to get the loot items right.
We have this get loot items.
Now we can make this blueprint callable.
And then we could call it in the Enemy Blueprint.
I'd like to do that.
So I'm going to give it a new function macro.
Give it blueprint callable.
And for that reason I'm going to recompile.
So we'll close down and come back in and we'll see those two new changes.
So let's go ahead and run this.
And let's get that blueprint back open.
Here in BP enemy base, we can go back to event spawn loot.
We no longer need to pass the self in that's defaulted to self and from our loot tiers.
If it's valid, we can call get loot items.
Notice there's a getter for the loot items array, but that's not what we want because the function
is going to return a different array that could have less or more depending on chances to spawn with.
The numbers we've configured will have more.
So we're going to get the loot items first thing, and we're going to promote this to a variable.
So we have this as a variable.
We'll call it loot items.
And at this point we can decide how to spawn these.
Now I don't want to spawn them all at the same location.
I want them spread out a little bit around the enemy.
So in the viewport, here's the enemy.
And let's say I'm spawning four items.
I'd like to spawn them spread out evenly, perhaps along these grid lines.
Right.
And we have a function that can give us evenly spaced rotators.
If we right click and type evenly spaced rotators, we have this function to give us evenly spaced rotators.
Now this requires a spread for one which 360 means spread these rotators out 360 degrees, right.
It also requires a number of rotators.
And that's going to be how many items we want to spawn.
Conveniently, that's how many items are in my loot items array.
So I need to get the number right.
We can get the length and we can hook that in to num rotators.
Now forward is going to be the enemies forward vector.
That's as good a forward vector as any.
So get actor forward vector can go in here.
And for the axis we can get the up vector.
So the vectors get up vector function requires a rotator.
There's also vector up 3D vector unreal up direction constant.
That's the one we'd like.
So we'll pass that in as the axis.
And now we have evenly spaced rotators which we can also promote to a variable.
So promoting this to a variable I'm going to have I'll just call this loot rotations.
So we have loot items, we have loot rotations and this function could easily be collapsed.
In fact, really all of this could be easily collapsed.
But I'd like to keep things very organized.
So I'm going to take this set of nodes and collapse it to a function and call this.
Get loot rotations and make this a blueprint pure function.
Now once we have our loot rotations and loot items, I would like to spawn them one after the other.
Now that means I don't want to use a for each loop.
I'd rather use a timer that I can fire an event off repeatedly, and for that I can call set timer by
event and I can create a custom event.
And this can be called spawn loot item.
And I can hook this up.
And I'd like this to be looping with a time of say 0.1.
So 0.1 seconds between each time and I can promote the timer handle to a variable called loot timer.
And I can even start this off by calling spawn loot item.
First thing.
Now each iteration of the loop, I'm going to want to keep track of how many times this has happened.
So in spawn loot item, I'm going to want to increment a loop count, and that means I'm going to need
to know how many times this loop has occurred.
So I'm going to make a new variable for this called spawn loop count.
This will be an integer.
And I'm going to first of all set it to zero.
Set spawn loop count here to zero.
And we'll increment it each time.
But first we're going to use that loop count to index our arrays.
We're going to want to spawn an actor and set some information on that actor.
We'll want to spawn it at a specific location and perhaps spread these out from the center of the enemy.
So if I spawn this for a given loot actor, I can rotate the loot actor to a given rotation so it's
facing sort of offset from the enemy's forward vector, and then I can move it in its own forward direction,
out just a little bit so that it's spread out.
And each of these can be rotated evenly.
So what I'm going to want is first of all a rotation.
And I can get that from loot rotations.
If I get loot rotations and I use get I can get a copy or a ref either one and I can get spawn loop
count.
And for the first one this will be the first rotation, the second one will give me the second, and
so on.
So this gives me a rotation and I can get the location of the enemy base with get actor location.
And I can take this actor location and I can add a vector to it in the direction of this location.
So if I take this location and call get forward vector, this is the Kismet math library function that
gives me a forward vector from a rotator, so I can get the forward vector from this rotator.
And if I add this to get actor location, I now have a location that is at the enemy's location, plus
a vector pointing outward in the direction of that rotator.
So if I add these together with vector addition, that gives me a new location, and I can even scale
this by some distance so that all these items are evenly spaced from the enemy.
For example, I can multiply this forward vector.
And I can right click on this pin to convert its type.
To afloat.
Single precision, and I can promote this to a variable and call this spawn distance.
And this means that this vector created from the rotator.
Will now be scaled by spawn distance.
And if we add that instead, well, now our spawn location is a little farther away from the enemy.
So now we have this vector that we can use for a location.
And we also have this rotator that we can use for a rotation.
And we can make a transform.
If I take this vector I can call make transform.
We'll make a transform from this vector.
And we'll use the rotation here for the rotation.
So with that.
We now have a transform.
And this depends on the spawn loop count.
So now that we have a transform, we can spawn the actor at that transform.
So that means we need to take our loot items array and we need to get.
We can call get, we can get a ref or a copy, and we can get our spawn loop count here and get this
item which is a struct.
And we can break it.
And if we break it, then we have the class the chance to spawn, the max number to spawn, and the
loot level override.
Now we don't need actually chance to spawn or max number to spawn, because those were already taken
into account when creating the loot items array.
So if we wanted to foolproof this and make sure that no one in blueprints uses these thinking that they
should use them, we could actually remove these from exposure to the event graph, and we could even
remove our array in the data asset, because we saw that we could have accessed the array directly instead
of get loot items.
And then of course our loot items would not contain the same information.
So what I'm going to do is go ahead and remove blueprint read only from chance to spawn and max number
to spawn.
So back here in loot tiers, we can take blueprint read only away from chance to spawn and max number
to spawn.
And then our loot items could be no longer blueprint read only.
And then we're forced to get loot items here, which already takes these two into account.
So in blueprint we don't need to see those that would just confuse us into thinking we should use them
here when we shouldn't.
So now that we have the loot class, we can spawn an actor.
Using spawn actor from class passing in the class we have the transform we can pass.
In the transform I'd like to use always spawn.
Ignore collisions.
And we'll hook this up to spawn loot item.
And after we've spawned a single item, we need to increase that spawn loop count.
And we also need a way to get out of this loop.
If that spawn loop count is higher, is high enough so we can take spawn loop count.
We can increment it by one using plus plus.
And before we even do anything here, we should check the spawn loop count.
So let's move these nodes a little bit over and make a branch.
And we'll check spawn loop count and make sure spawn loop count is less than.
The number of loot items so we can use.
Loot items.
Get the length and make sure that spawn loop count is always less than that before we continue.
And if it's less than, then we can spawn.
If it's not less than.
In other words, we should stop the loop.
Then we can clear and invalidate the timer so we can move these up or to the right.
And we can take our loop timer and call clear and invalidate timer by handle.
So there's our out for the loop.
So we have an asynchronous loop like so.
Now this here is basically performing a calculation that depends on spawn loop count.
So we could make this a function to get the spawn transform for loop.
So I can collapse this to a function, get loot transform and make it a blueprint pure function.
And this depends on.
Spawn loop count.
So we'll call this loop count.
And return value will be spawn.
Transform.
So that cleans up the nodes here a bit.
Now here we have loot level override.
And we can check this.
We can set the lute's level if lute level override is true.
We can just set it right here based on the enemy's level.
Now, in order to do that, we'd have to cast it.
And if our lute is, say, an effect actor, well, I believe our effect actor has a level, doesn't
it?
If we go to, let's say, a potion and open it up.
This is an effect actor, of course.
And the effect actor has a level called actor level.
So if we want to override it, we can.
I'm just going to reroute it.
And add a branch.
And if this is true, we can take the spawned actor.
I'm going to move this count for now.
We'll take the spawned actor.
I'm going to cast to aura effect actor.
And if it's valid.
And this is true.
Then I'll take Aura Effect actor and get its actor level.
And set it.
Of course we'd have to make it blueprint read write for that.
So two aura effect.
Actor two actor level.
Changing this from blueprint read only to blueprint.
Read.
Write.
And with that, we'll need to restart again.
So closing down and recompiling.
Another alternative to needing to set this would be to just restrict our loot tiers, not to actors,
but to our effect actors or some other pickup class, and only spawn those and then make their actor
level exposed on spawn using the expose on spawn meta specifier.
But either way, back to our.
Enemy base after casting as aura effect actor.
If this boolean is true, then we're going to set actor level.
And we'll set that equal to level.
We can get from character class defaults our level.
It's going to convert to a float.
Okay.
Now here's the thing.
We need to increment spawn loop count whether or not this cast succeeds and whether or not this boolean
is true.
So I'm going to set it.
Before we even try any of those things.
We'll set it right here.
With that, we should be spawning loot so we can compile and test this out.
We do need to make sure, however, that our game mode in Game Beep or Game mode has loot tiers set.
So we need to make sure that's set there.
Now it is.
And we can test this out.
So we need to slay one of these enemies.
And I can't because I didn't load in with any spells.
I have to load in through the load menu.
Now.
I'll go ahead and delete this slot and make a new one.
Will spawn some enemies and we'll go ahead and kill them.
And there we go.
Now our distance is zero, so we're not actually spawning them.
Okay, so I got an error.
Attempted to access index ten from an array loot items of length ten, which means we need to go back
to where we're checking our spawn loop count and make sure that our spawn loop count is never greater
than our loot items array.
Right?
So we do need to make sure not to have a.
Off by one error.
So let's take our loot items length and we'll use loot items length minus one.
So we don't try to access that array out of bounds.
Blueprints is more forgiving.
We get an error rather than a crash when it's in blueprint when the violation is in blueprint.
Let's try this again and make sure there are no errors.
And I'm seeing some loot.
And no errors when I stop.
Now in enemy base where pushing that spawn location and get loot transform pushing it out by spawn distance.
But this doesn't have a value if I give it a value of 50.
Now we should see that loot spawned 50 units away.
There it is.
And it spawns out in a circle.
There we go.
So it spawns out in a nice arc there.
And of course I picked up a few of them, so there's that.
But now I'm getting a blueprint.
Runtime error.
Attempted to access BP Health Potion C5 via Property Dynamic Cast.
So we're casting here, but BP Health Potion C5 is not valid pending kill.
So I picked up a potion and because I picked it up, it was pending kill, which means that attempting
to access it here to set properties on it, we were too late.
So the cast needs to also be accommodated by an invalid node as well.
So we can use is valid here and we could only continue if is valid is true.
That should fix that problem.
Of course, that means if we pick it up too soon, we won't override its level.
So for that reason, you may wish to go ahead and.
There we go.
We got that same problem.
You may wish to go ahead and use expose on spawn for those cases.
Okay.
So now that we're spawning these and we're spawning them out in a circle around the enemy, we could
also randomize that distance.
If we wanted to.
We could go to get loot transform.
And for spawn distance we can set spawn distance based on a minimum and a maximum we can say.
Random float and range.
And we can promote min and max to variables.
Promote to variable.
We'll call this.
Min spawn distance.
And we'll promote this to a variable, not local max spawn distance.
And then if we set these min spawn distance and max spawn distance variables, say minimum of 50 and
max spawn distance of 100.
Now, as soon as we create this random float, though, we are no longer going to need spawn distance
and we can simply delete that variable and use our random value, which means this multiply still needs
this one.
This input pin a float.
So we'll plug that in like so.
And now spawn distance is no longer needed.
And we can try this out.
Looks like I have some visual artifacts that I'm going to need to restart.
The editor.
Sometimes that happens in the engine but a restart will fix that.
There we go.
So we do have some randomness now.
Mean spawn distance could be, say, 25 and max spawn distance could be a bigger value, say 150, for
instance.
And that's going to give us a bigger spread.
So that gives us more randomness to it.
Now we may wish to have some nicer looking pickups.
They might need some bobbing or sinusoidal behavior so we can add that as well.
But for now we have our loot system in place and we can change our chances to spawn the number we can
spawn, and so on here in our loot tiers.
And so this gives us some options for spawning loot.
All right.
Excellent job and I'll see you in the next video.