• entries
    9
  • comments
    13
  • views
    26012

Simple stuff: Creating a spawner in UE4 using C++

Sign in to follow this  

8921 views

Hi everyone!

This weeks simple project was to create an effective enemy spawner in UE4 using C++. I already had a simplistic blueprint version of this, but it had some bugs which were causing crashes in the engine and editor. Once blueprint scripts start crashing the editor, it is hard to troubleshoot the actual cause of the crash, so in these instances i always find C++ easier to debug, having the vast wealth of tools available that are visual studio 2013.

The concept of an enemy spawner is simple. When the player character enters a box defined within the level, one or more enemies spawn at a set location which may or may not be within the confines of that box.

I have decided to share the majority of this class with the community, as i found very little complete code on how to do this, and researching the correct syntax for the SpawnActor() method alone took me a good thirty minutes of googling.

To implement the NPC Spawner, i started out by simply deriving a class from ATriggerBox:
UCLASS()class SEVENSPELLS_API ANPCSpawner : public ATriggerBox{ GENERATED_BODY() bool triggered; /* List of character types to spawn at or near the Spawn Location */ UPROPERTY(EditAnywhere, Category = "Spawner") TArray> CharactersToSpawn; /* Spawn location. If not set, no enemies spawn. */ UPROPERTY(EditAnywhere, Category = "Spawner") class ATargetPoint* SpawnLocation; /* Number of copies of each type of character to spawn */ UPROPERTY(EditAnywhere, Category = "Spawner") int32 Count; /* This value indicates the maximum number of times the spawner will trigger enemies. If zero, the default of 1 is assumed. */ UPROPERTY(EditAnywhere, Category = "Spawner") int32 MaxTriggers; ANPCSpawner(); UFUNCTION() void OnBeginOverlap(AActor* Other, UPrimitiveComponent* Box, int32 OtherBodyIndex, bool bFromSweep, const FHitResult &hitResult); UFUNCTION() void OnEndOverlap(class AActor * OtherActor, class UPrimitiveComponent* OtherComp, int32 OtherBodyIndex);};

The class attaches to two delegates for OnActorBeginOverlap and OnActorEndOverlap. These notify our class when any other actor walks into the edges of our trigger box, and are initialised when the object is constructed:
ANPCSpawner::ANPCSpawner() : triggered(false){ GetCollisionComponent()->OnComponentBeginOverlap.AddDynamic(this, &ANPCSpawner::OnBeginOverlap); GetCollisionComponent()->OnComponentEndOverlap.AddDynamic(this, &ANPCSpawner::OnEndOverlap); if (!MaxTriggers) MaxTriggers = 1;}

Within these two delegate functions, we use the boolean value 'triggered' to ensure that the event may only occur once (for some reason, UE4 sends the begin overlap notification twice for the player character, followed by the end overlap twice), and that we only trigger the event when the player character walks into the trigger area (otherwise, the NPC which is spawned may also create another NPC within the spawn box, and so on, until the system runs out of RAM and crashes!)

Once we have verified that we are triggering in the right situation, we use the SpawnActor method of the UWorld class to spawn the relevant actor objects:
void ANPCSpawner::OnBeginOverlap(AActor* Other, UPrimitiveComponent* Box, int32 OtherBodyIndex, bool bFromSweep, const FHitResult &hitResult){ APlayerCharacter* pc = Cast(Other); if (pc && !triggered && MaxTriggers > 0) { triggered = true; /* Player overlapped the NPC Spawner, spawn the enemies */ FActorSpawnParameters parameters; parameters.Owner = this; parameters.SpawnCollisionHandlingOverride = ESpawnActorCollisionHandlingMethod::AdjustIfPossibleButAlwaysSpawn; for (int i = 0; i < Count; ++i) { for (TSubclassOf actor : CharactersToSpawn) { logprint(actor->GetClass()->GetName()); if (SpawnLocation) { GetWorld()->SpawnActor(actor, SpawnLocation->GetTransform(), parameters); } } } MaxTriggers--; }}void ANPCSpawner::OnEndOverlap(class AActor * OtherActor, class UPrimitiveComponent* OtherComp, int32 OtherBodyIndex){ APlayerCharacter* pc = Cast(OtherActor); if (pc && triggered) { triggered = false; }}

This serves as a great way to test combat, by putting a known trigger box at some point near the player's start point, walking into it to spawn the enemy we want to test:



As always, comments and suggestions are more than welcome!
Sign in to follow this  


2 Comments


Recommended Comments

 

I already had a simplistic blueprint version of this, but it had some bugs which were causing crashes in the engine and editor

 

That is weird. I have done many blueprints spawners(even randoms to make levels more fun) in UE4 and it never crashed for me. 

 

As a side note I would like to point out that making Spawners in code/script is way easier in Torque 3D compared to work in C++ in UE4 smile.png UE4 Blueprint is also much more speedy to use in UE4 than C++. I guess all the scripting languages from various engines and the UE4 Blueprint has made me a bit lazy... I am perhaps one of those who find Scripting/coding gameplay stuff and events in C++ a weird choice.... C++ should stick to the engine and more speedy solutions such as Blueprint and light scripting languages are more fit for the gameplay and events etc. etc. Less focus on syntax and more on the development of the game.

 

However still an interesting and needy read though.

 

Also which version of the UE4 is used when seeing crashes with Blueprint spawners?

Share this comment


Link to comment

The ue4 version I was using was 4.8.2, it seems that it was crashing on an attempt to instantiate an object who's construction script referenced uninitialised component objects. 

Rather than this being captured in the blueprint interpreter the null pointer or possibly corrupt pointer found its way all the way to native code causing a total crash.

 

I managed to identify this by debugging the engine with visual studio.

 

It seems a little more stable in 4.9, but if I have to resort to visual studio to debug a blueprint bug I find myself much more at home just writing C++ anyway...

 

Blueprint is great for the simple things, triggers and simple conditionals but whenever I've tried to write anything complex in blueprint it quickly becomes an unreadable spaghetti jumble of lines and nodes like some programmer spider web. 

 

It's great for shaders too because it represents data flow perfectly. 

 

Once you try to do complex maths in blueprint, you hit the wall I describe and it's nasty :)

 

Can't knock the engine though, loving it and progressing much faster than trying to do it all from scratch... 

Share this comment


Link to comment

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now