Hi, I have several characters, each with an actor component representing an inventory. How can I send data (in this case an array of instanced structs) from any of these actor components to a widget (to display the said actor’s inventory basically), WITHOUT any hard references? I don’t want the widget to be created by the characters themselves or any of their actor components.
Thanks.
Hey @AllGamesSuck!
It’s going to be pretty difficult to not have a hard reference there- because it’s objects. What you COULD do is keep it as a soft reference until you need it, then convert to a hard reference when it’s needed?
That being said, as long as those actors exist, their inventories are loaded whether the widget is asking for them or not- it’s not any slower to ask for that info specifically, it’s pretty safe.
Hope that helps!
Edit: I said it’s pretty safe, and it is- but you can also use “IsValid?” to get further granularity. If it returns false it won’t trigger a game crash. If it Is Valid then it will work as intended.
Thanks, it’s not performance I’m worried about, I just wanted to keep everything as decoupled as possible.
Once you create that widget, it’s going to need a hard pointer to that actor component to pull data from initially. You don’t have to store the pointer though, if you set up the widget to pull data from the actor component by delegates (event dispatcher in blueprint).
Widgets work well like that, using the observer pattern to automatically respond in changes to data (to reflect them on the UI), without having to constantly check for changes or store pointers separately.
soft / hard pointers etc don’t decouple. Soft is used to optimize what loads into memory and when. Hard (UObject*, TObjectPTR) loads into memory immediately. Both still need to reference a class in your case because the widget needs to find that struct property on your custom actor component. So that doesn’t decouple.
Which of the big classes would you use as the observer? Or would you create a new one?
You can use the Edit I made earlier and use IsValid? checks to make sure any couplings aren’t necessary, OR do what @Roy_Wierer.Seda145 suggests. It’s not my style personally, but it’s more scalable and a good thing to know how to do. I just always forget because IsValid checks usually suit my needs and I’m going too fast to do it the better (read: more stable and performative) way.
The widget is the observer. The actor component is the broadcasting object.
Right, sorry I was thinking about mediator pattern.
But here’s the thing: with the observer pattern, using event dispatchers, I wouldn’t know which actor component to subscribe to. Like I said, I have multiple.
I thought about using interface, but I’m not sure how to implement that.
added little snippet from one of my products how to do it in c++ as well.
// Setup
void UDigitalClockWidget::NativeOnInitialized() {
Super::NativeOnInitialized();
// .... snippet of relevant code
// TimeSubsystem here would be your actor component to sync to instead.
// The function you bind to here would need the same signature (function parameters) as what the actor component broadcasts with.
TimeSubsystem->OnSecondChanged.AddDynamic(this, &UDigitalClockWidget::ActOnSecondChanged);
// Synchronize immediately.
ActOnSecondChanged();
}
You could say it makes most sense to have an actor component open up the widget, because if that actor component is not present, that widget should not be created in the first place (no data available for it). There doesn’t seem to be a point in decoupling that.
Otherwise you need some kind of identifier on the actor component.
Each player gets their own HUD and widgets, each player has their own playercontroller controlling a pawn. So you could have a widget scan (or store) relevant pawns on a level, check if their playercontroller matches the controller of the widget, then scan its components for a certain ID (component of class, or component with tag etc.) and move from there. Seems like the hard way to me.
I’d do something like ( pseudocode snippet ) :
UMyActorComponent::DoStuff() {
MyWidget = WidgetSystem::CreateWidget(MyWidgetClass);
MyWidget->InitializeForComponent(HealthActorComponent);
}
Then MyWidget continues to proceed setting up delegate bindings like the C++ snippet I posted earlier. Optimally you also create your widgets from one place so they remain managable. Like from a HUD or UMG based HUD.
Use BP Interface and macros to call for specific data.
Reference to the character would a simple bare bones Actor Obj Reference. So there’s no major overhead… Just whatever an empty Actor would have.
Generally UI/HUD is created and managed by the controller. So you can easily set a base reference to the character via Get Controlled Pawn → pass the return to an Actor Obj Ref variable.