I am trying to implement a flexible, designer friendly UI for a UE4 game.
The UI is made of many widgets and a HUD class which acts as the mediator between the rest of the game and the UI itself. Communication between game, HUD, and widgets is implemented through events.
I am having problems identifying the correct pattern and / or implementation for handling events broadcast by UI buttons.
In the beginning I had buttons with very specific commands like "pause the game". I wrote a class to represent a generic command, and then I made a subclass for each desired behaviour. I then used the Visitor pattern to handle the event without knowing what it is specifically.
A pseudo code example of the setup would be:
button.onClicked(...)
{
...
commandObject.broadcast();
...
}
commandObject.broadcast()
{
...
eventInstance.broadcast(this); // hud listens for this call
...
}
hudInstance.visit(Command * command)
{
...
command.acceptVisitor(this);
...
}
eventInstance.acceptVisitor(HUD * hud)
{
...
hud.doSomething();
...
}
Then I had to add buttons with more dynamic commands, like "open the context menu for target object". So I had to go back and implement an interface IHasUITarget, with get/set methods for the UI Target. So now I store a pointer to the target (which I take from a IHasUITarget object such as a widget) in a TargetedCommand object (which is a child of Command)I then pass the target when accepting the HUD visitor.
button.onClicked(...)
{
...
commandObject.broadcast();
...
}
commandObject.broadcast()
{
...
eventInstance.broadcast(this); // hud listens for this call
...
}
hudInstance.visit(Command * command)
{
...
command.acceptVisitor(this);
...
}
eventInstance.acceptVisitor(HUD * hud)
{
...
hud.doSomethingWithTarget(target); // got it using getUITarget() when initializing the command properties
...
}
Now I have to add support for commands like "assign task x to target object". Which means adding another parameter, with an interface etc.
I have problems with this mostly because I feel like I might paint myself into a corner down the road, and also because I'd like to enforce the correct Command choice when the designer adds a button to a widget and picks the Command associated with it: the designer interface used to pick the desired Command filters by class, and the class is defined inside the header of the object which contains it (in this case, a button). If I want to restrict acceptable commands, I'd have to make different types of buttons: one accepting Command objects, one accepting CommandChildA, another accepting CommandChildB, etc. On the other hand, if I don't restrict commands I'd have to check that the Command object to which the designer assigned the context menu event actually implements IHasUITarget, before calling getUITarget on it. Similarly, if I wanted to initialize the Command properties I'd have to check if Command implements the appropriate interface, before making a call such as commandObject.setSomeProperty(Something * s). So either I won't be able to code to interfaces, or I will have to add a whole set of class variables which might or might not be used.
Maybe I am trying to use the wrong pattern. What would you recommend to solve this problem?
Thank you!
Aucun commentaire:
Enregistrer un commentaire