The Spell
class will ultimately do damage to all the monsters. Towards that end, we need to contain both a particle system and a bounding box inside the Spell
class actor. When a Spell
class is cast by the avatar, the Spell
object will be instantiated into the level and start Tick()
functioning. On every Tick()
of the Spell
object, any monster contained inside the spell's bounding volume will be affected by that Spell
.
The Spell
class should look something like the following code:
UCLASS() class GOLDENEGG_API ASpell : public AActor { GENERATED_UCLASS_BODY() // box defining volume of damage UPROPERTY(VisibleDefaultsOnly, BlueprintReadOnly, Category = Spell) TSubobjectPtr<UBoxComponent> ProxBox; // the particle visualization of the spell UPROPERTY(VisibleDefaultsOnly, BlueprintReadOnly, Category = Spell) TSubobjectPtr<UParticleSystemComponent> Particles; // How much damage the spell does per second UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Spell) float DamagePerSecond; // How long the spell lasts UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Spell) float Duration; // Length of time the spell has been alive in the level float TimeAlive; // The original caster of the spell (so player doesn't // hit self) AActor* Caster; // Parents this spell to a caster actor void SetCaster( AActor* caster ); // Runs each frame. override the Tick function to deal damage // to anything in ProxBox each frame. virtual void Tick( float DeltaSeconds ) override; };
There are only three functions we need to worry about implementing, namely the ASpell::ASpell()
constructor, the ASpell::SetCaster()
function, and the ASpell::Tick()
function.
Open the Spell.cpp
file. Add a line to include the Monster.h
file, so we can access the definition of Monster
objects inside the Spell.cpp
file, as shown in the following line of code:
#include "Monster.h"
First, the constructor, which sets up the spell and initializes all components is shown in the following code:
ASpell::ASpell(const class FPostConstructInitializeProperties& PCIP) : Super(PCIP) { ProxBox = PCIP.CreateDefaultSubobject<UBoxComponent>(this, TEXT("ProxBox")); Particles = PCIP.CreateDefaultSubobject<UParticleSystemComponent>(this, TEXT("ParticleSystem")); // The Particles are the root component, and the ProxBox // is a child of the Particle system. // If it were the other way around, scaling the ProxBox // would also scale the Particles, which we don't want RootComponent = Particles; ProxBox->AttachTo( RootComponent ); Duration = 3; DamagePerSecond = 1; TimeAlive = 0; PrimaryActorTick.bCanEverTick = true;//required for spells to // tick! }
Of particular importance is the last line here, PrimaryActorTick.bCanEverTick = true
. If you don't set that, your Spell
objects won't ever have Tick()
called.
Next, we have the SetCaster()
method. This is called so that the person who casts the spell is known to the Spell
object. We can ensure that the caster can't hurt himself with his own spells by using the following code:
void ASpell::SetCaster( AActor *caster ) { Caster = caster; AttachRootComponentTo( caster->GetRootComponent() ); }
Finally, we have the ASpell::Tick()
method, which actually deals damage to all contained actors, as shown in the following code:
void ASpell::Tick( float DeltaSeconds ) { Super::Tick( DeltaSeconds ); // search the proxbox for all actors in the volume. TArray<AActor*> actors; ProxBox->GetOverlappingActors( actors ); // damage each actor the box overlaps for( int c = 0; c < actors.Num(); c++ ) { // don't damage the spell caster if( actors[ c ] != Caster ) { // Only apply the damage if the box is overlapping // the actors ROOT component. // This way damage doesn't get applied for simply // overlapping the SightSphere of a monster AMonster *monster = Cast<AMonster>( actors[c] ); if( monster && ProxBox->IsOverlappingComponent( monster- >CapsuleComponent ) ) { monster->TakeDamage( DamagePerSecond*DeltaSeconds, FDamageEvent(), 0, this ); } // to damage other class types, try a checked cast // here.. } } TimeAlive += DeltaSeconds; if( TimeAlive > Duration ) { Destroy(); } }
The ASpell::Tick()
function does a number of things, as follows:
ProxBox
. Any actor that is not the caster gets damaged if the component overlapped is the root component of that object. The reason we have to check for overlapping with the root component is because if we don't, the spell might overlap the monster's SightSphere
, which means we will get hits from very far away, which we don't want.CapsuleComponent
(they might have ProxBox
or ProxSphere
).Now, let's focus on how the player can acquire spells, by creating an individual PickupItem
for each spell object that the player can pick up.
Compile and run your C++ project with the Spell
class that we just added. We need to create blueprints for each of the spells we want to be able to cast. In the Class Viewer tab, start to type Spell
, and you should see your Spell
class appear. Right-click on Spell, and create a blueprint called BP_Spell_Blizzard, and then double-click to open it, as shown in the following screenshot:
Inside the spell's properties, choose the P_Blizzard spell for the particle emitter, as shown in the following screenshot:
Scroll down until you reach the Spell category, and update the Damage Per Second and Duration parameters to values you like. Here, the blizzard spell will last 3.0 seconds, and do 16.0 damage total per second. After three seconds, the blizzard will disappear.
After you have configured the Default properties, switch over to the Components tab to make some further modifications. Click on and change the shape of ProxBox
so that its shape makes sense. The box should wrap the most intense part of the particle system, but don't get carried away in expanding its size. The ProxBox
object shouldn't be too big, because then your blizzard spell would affect things that aren't even being touched by the blizzard. As shown in the following screenshot, a couple of outliers are ok.
Your blizzard spell is now blueprinted and ready to be used by the player.
Recall that we previously programmed our inventory to display the number of pickup items the player has when the user presses I. We want to do more than that, however.
To allow the player to pick up spells, we'll modify the PickupItem
class to include a slot for a blueprint of the spell the player casts by using the following code:
// inside class APickupItem: // If this item casts a spell when used, set it here UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Item) UClass* Spell;
Once you've added the UClass* Spell
property to the APickupItem
class, recompile and rerun your C++ project. Now, you can proceed to make blueprints of PickupItem
instances for your Spell
objects.
Create a PickupItem blueprint called BP_Pickup_Spell_Blizzard. Double-click on it to edit its properties, as shown in the following screenshot:
I set the blizzard item's pickup properties as follows:
The name of the item is Blizzard Spell, and five are in each package. I took a screenshot of the blizzard particle system and imported it to the project, so the Icon is selected as that image. Under spell, I selected BP_Spell_Blizzard as the name of the spell to be cast (not BP_Pickup_Spell_Blizzard), as shown in the following screenshot:
I selected a blue sphere for the Mesh
class of the PickupItem
class. For Icon, I took a screenshot of the blizzard spell in the particle viewer preview, saved it to disk, and imported that image to the project (see the images folder in the Content Browser tab of the sample project).
Place a few of these PickupItem
in your level. If we pick them up, we will have some blizzard spells in our inventory.
Now we need to activate the blizzard. Since we already attached the left mouse click in Chapter 10, Inventory System and Pickup Items to dragging the icons around, let's attach the right mouse click to casting the spell.
3.15.237.123