What is the best way to handle the same UMG button click event with different actions in different conditions?

Hi,
I made a goods dealing system in my game, and I use an user widget class to represent the goods in the shop, which has a button for player to click on. When the goods widget is clicked, I want to respond to it with different actions, for example, to buy or to sell the goods, according to whether this widget is in the market or in the inventory panel.
As in the picture below, when the goods widget is in the market panel at the left side, when it is clicked, that means to buy it, I would use the “handleMarketPrebuy” function to respond. If it is the goods widget in the inventory panel at the right side clicked, I would use the “HandlePrebuyCancal” function to respond to it.
Now I am using event dispatcher on the widget and binding these handle functions to the event dispatcher when creating the widget, it can work. But I don’t know if it is the proper way to do it like so, is there better way to implement it?
Is blueprint interface an option? Any example? Thanks.


image

Consider splitting code that handles logic of buying from widgets. I mean widgets should just display stuff and (as you did report) events.

You need to make decision where to place that buy/sell logic. Maybe player controller or player hud actors. Something that is local to player. Or for game with many levels, some actor that survives level travel, so it will remember stock left and what player bought already etc.

Now for widgets, yes dispatchers are way to go (so you do not have chain of referecnes down to parent and up to widgets). Make function library (because those are global), let it provide reference to that actor that keeps all logic for selling/buying. If you communicate trough blueprint interfaces it will be independent of class that holds store.

And then dispatcher that tells all widgets “hey store state changed please update”. DO not worry if they all update, it is quite light operation on just few widgets.

Oh and for store items use map to hold item type and quantity. Use json to load item data from file. There is some tut on YT that shows exactly this.

ps.
You bind event to dispatcher only once at begin play (or in case of widgets construct/pre construct). If something does not work and it should, make print to log when your widgets and actors in level are created (begin play and event construct for widgets), sometimes that stuff is loaded in weird order. So if your widget is created before actor that holds sell/buy logic, widget will not be able to assign to dispatcher etc. Also this is reason to not place dispatchers in widgets, you never know when widget is created.

how to use maps (variables):

CVS (not JSAON) importing data to unreal:

Thank you very much for providing such a detailed answer. I have the following questions:

This is what I want to learn the most, how to decide where to put the data variables and functions correctly? If I put too many variables and functions in the player controller, will it cause some other problems? Can I put them in the Gamemode or GameState (PlayerState)? Which one is the best choice and what is the reason? Could you help to share some tutorials or examples in this area?

I am not sure if my understanding of your words is correct.
Do you mean that blueprint interface is also a solution in my case?
If I use interface, I don’t need to store the reference to the buy/sell logic in the function library, but I need to store the reference to the actors that implement the interface in the widget, right?
So both event dispatcher and interface are ok in my case?

I think your meaning is not to place the buy/sell logic in the widgets, right?

First question:

According to Epic, put this in GameState.

Then make blueprint Function Library and add “global” functions like this:

Using blueprint interfaces is pointless when you always call same function in object that always is same class and always loaded. So pure cast there is enough.

It also answers your second question: if you always keep that data in same class/actor, interfaces are pointless.

Second question:
I would use dispatchers to tell all interested actors that something changed. Ie. from widgets hook to that dispatcher, and just tell them “hey store state changed!”. Then let them read from Game state what is changed. Ie. each item in shop should know its own merchandise and just read about this one.

Last question:
Yes split buy sell logic from widgets, I generally use widgets only to display stuff, and read state of some variables.

About placing dispatchers. It is tip of iceberg. Unreal is object oriented (DUH), it also loads different actors/widgets in different order. Which means if something is working now, for eg. shop widgets are loaded after data in GameState, and they can get their corresponding items, it does not mean that at some point they will be loaded before GameState data, and as result get null pointers.

ps.
I am creating some game that instead of apples in shop has planets, that are loaded from CSV vile, so they are spawned some time after all is loaded. I assume this also will be case for your shop.

So problem is How widget knows pointer to planet if planet is not yet spawned. Yes chicken and egg all over place. All I am sure from widget perspective, is that GameMode (or gameState) are already there. Easiest solution is to make dispatcher in GameMode that tells everybody “we have real game ready state!”, and another “planets state changed, update self!”.

and last problem: using integer as index to some array that identifies planet, solved problems with accessed none at beginning. But code is much more messy and potentially buggy. So use pointers (references) or map (variable type) for shop items and do error handling when reference is null, or use integers and make -1 error value etc.

pps.
If you ever consider loading inventory for shop from CVS, Json etc. do it as soon as possible, those bugs from loading order will surface. Better fix them at beginning.

Thanks a lot, your information is very helpful :+1:

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.