Hello, beginner Unreal c++ user here. Brief description of project:
I have a TargetSpawner class that spawns Targets of another class called SphereTarget. The targets are only on the screen for 2 seconds before they are destroyed and a new one is spawned.
PlayerHUD is a class of type UUserWidget, which displays information on screen about how many targets were hit, accuracy, etc.
DefaultPlayer class is the player controller which can shoot the targets. It creates an instance of PlayerHUD which is added to the player screen.
Since an instance of PlayerHUD is created in the DefaultPlayer class, I understand how to retrieve information about target hits/misses since this class includes the functions responsible for firing projectiles, but I also want the PlayerHUD to display the total number of targets spawned. How could I enable my TargetSpawner class to let PlayerHUD know how many targets have spawned? I was hoping there would be some delegate type function available like:
I seem to encounter this question of communicating between classes/objects a lot, and am wondering if I’m designing my class hierarchy incorrectly or if I’m missing something.
Hey Mark, this can indeed be done with delegates, but it’s important to sketch how the classes are going to communicate first. To avoid having to “search” the level it is common to have some kind of manager class which holds relevant references. I like to use GameInstanceSubsystems as manager classes in many situations.
You could make a TargetSubsystem of type GameInstanceSubsystem and access it like this:
UGameInstance* GI = UGameplayStatics::GetGameInstance(this);
UTargetSubsystem* TargetSubsystem = GI ? GI->GetSubsystem<UTargetSubsystem>() : nullptr;
The TargetSubsystem itself holds references to the TargetSpawner instance(s) and is used to broadcast events related to its TargetSpawner(s) such as the total of targets spawned and the creation / destruction of a target.
When the TargetSpawner spawns, it registers itself to the subsystem through a method on the subsystem like TargetSubsystem->RegisterSpawner(this).
The TargetSubsystem then listens to delegates on the registered TargetSpawner and can broadcast them further through its own delegates.
When a HudWidget is created, it binds itself to the relevant delegate on the TargetSubsystem so that it receives updates (total spawn count, target info etc.).
The best part of connecting this through a subsystem is that you can get a subsystem reference from anywhere and that you can avoid having classes pointing at eachother in circles.
Do I put this in both the TargetSpawner and the PlayerHUD?
How would I go about registering a spawned target to the TargetSubsystem?
So this piece of code goes inside HudWidget/PlayerHUD, correct? And then I need to make a function in TargetSubsystem called OnTargetCountChanges that does what exactly? I’m guessing it listens to TargetSpawner and does something when the a new target is spawned.
I’m sorry for so many questions, but I’m having a hard time finding anything useful using only c++ on the internet.
I found a simple solution for my particular problem that I thought I should share if anyone has a similar problem, but I’m still interested in what Roy responds with.
In BeginPlay() for TargetSpawner I created a reference to the Player (DefaultCharacter) by using GameplayStatics to get the current Player pawn.
Then I put the following code block right after the spawning of the targets to update the Targets Spawned count.
TargetsSpawned++;
if (DefaultCharacter->PlayerHUD)
{
DefaultCharacter->PlayerHUD->SetTargetsSpawned(TargetsSpawned);
}
However, it only works after the first target is destroyed for some reason. I think this might be because I needed to include:
(DefaultCharacter->PlayerHUD)
And so maybe the target is spawned before the PlayerHUD is applied to the viewport of the player.
All of my other code like TargetsHit, ShotsFired, and Accuracy are updated in the DefaultCharacter class, so its a little weird that this is a part of the TargetSpawner class.
That piece of code you can write anywhere to get a pointer to the TargetSubsystem.
TargetSubsystem can hold an array like
UPROPERTY() TArray TargetSpawners;
and a method like
RegisterTargetSpawner(UTargetSpawner* InTargetSpawner) {
TargetSpawners.Add(InTargetSpawner)
// Then bind the TargetSubsystem to the spawner delegates
// InTargetSpawner->OnTargetDestroyed.BindDynamic.......
}
Correct. OnTargetCountChanges will be a delegate which can be listened to and which can be broadcasted. Any other class like the HUD or the player can listen to that delegate.
When a TargetSpawner broadcasts an event that the TargetSubsystem listens to, the TargetSubsystem broadcasts that event further so that the HUD and the player are reached.
You can not assume that one object is spawned before the other, that order is not reliable. Using the delegate example I gave you can bind to the delegate to listen for a change in the targetspawner and immediately afterwards request the current status, Example:
Do you know of an example file or project that uses Subsystems in order to access actor references? I want to eventually implement this but I could really use example code to fully grasp my head around it.
Would creating a game instance with a simple TArray, and storing actors in the TArray be a solution as well? For example, for every actor I wish to have a reference of, I could just use
I don’t remember any of the marketplace, but you can treat a GameInstanceSubsystem just like the GameInstance. It’s meant to be an extension you can “plug in” without making the original GameInstance dirty. Engine source code uses it for the ReplaySubsystem which I just heard of: