What is the best solution for UI handling in projects using SveltoECS? #114
-
What is the best solution for handling UI in projects using SveltoECS right now? I'm struggling with UI handling in my hobby project. I'm curious about your solutions for UI handling. |
Beta Was this translation helpful? Give feedback.
Replies: 3 comments
-
Hello! this is a great question. I had a solution in mind that I have never ended up developing. After we tried to implement an ECS UI framework we decided that was NOT the way to go. GUI is one of the few application that are really OOP/Event based in nature. So for me the best solution is to code with UI with the standard tools/patterns and interface the UI presenter/controller/viewmodel with Svelto engines. I was starting to envision a solution, but unfortunately I don't remember anymore my train of thought. I think the idea was to be able to register "CommandEngines" that could have been retrieved inside presenter/view model and executed as normal commands from the GUI point of view. However for Svelto they would be normally registered engines (so can access the DB etc) Another approach could be to create entities to signal that something happened. So in your gui class you would inject the entity factory and create entities with values passed from the ui. later on engines would process them and copy the values inside other gameplay related svelto entities. The current working solution in the project is to use managed components with with EntityViewComponents. EntityViewComponents are components that can hold managed objects like: public struct DropdownEntityViewComponent : IEntityViewComponent
{
public EGID ID { get; set; }
public IDropdown dropdown;
public IDropdownOptions dropdownOptions;
} when you build an entity with this component, you need to pass the optional array of implementors. among the implementors there must be objects implementing IDropdown and IDropDownOptions (like in this example). IT looks like var initializer = _entityFactory.BuildEntity<CharacterEntityDescriptor>(id, new []{ objectA, objectB }); the trick is then using the ReactiveValue class, like in this example: public interface IDropdown : IImplementor
{
uint startingIndex { set; }
ReactiveValue<uint> index { get; set; }
} then the ReactiveValue is initialised (and can be initialised only) in an engine like class DropdownEngine : IQueryingEntitiesEngine, IReactOnAddAndRemove<DropdownEntityViewComponent>
, IReactOnDispose<DropdownEntityViewComponent>
{
public EntitiesDB entitiesDB { get; set; }
public void Ready()
{
_handleIndexChanged = HandleIndexChanged;
}
public void Add(ref DropdownEntityViewComponent entityView, EGID egid)
{
var dropdown = entityView.dropdown;
var startingIndex = ((IDropdownInternal)entityView.dropdown).startingIndex;
dropdown.index = new ReactiveValue<uint>(egid.ToEntityReference(entitiesDB), _handleIndexChanged
, startingIndex);
var dropDownInternal = dropdown as IDropdownInternal;
OnIndexChanged(egid, startingIndex, dropDownInternal, startingIndex);
}
public void Remove(ref DropdownEntityViewComponent entityView, EGID egid)
{
entityView.dropdown.index.StopNotify();
}
void HandleIndexChanged(EntityReference reference, uint newValue)
{
var egid = reference.ToEGID(entitiesDB);
var dropdown = entitiesDB.QueryEntity<DropdownEntityViewComponent>(egid).dropdown;
var dropDownInternal = dropdown as IDropdownInternal;
OnIndexChanged(egid, newValue, dropDownInternal, dropdown.index.value);
}
void OnIndexChanged(EGID egid, uint newValue, IDropdownInternal dropDownInternal, uint previousValue)
{
// update view
dropDownInternal.TMPIndex = newValue;
// update pure component linked to this widget
ref var dropdownComponent = ref entitiesDB.QueryEntity<DropdownEntityComponent>(egid);
dropdownComponent.previousIndex = previousValue;
dropdownComponent.index = newValue;
}
Action<EntityReference, uint> _handleIndexChanged;
} The trick is to use the same object passed in the implementor array in your GUI Framework. this object implementing IDropdown will be able to set values in the index field. the index field will trigger immediatly an engine callback which has access to the svelto database. |
Beta Was this translation helpful? Give feedback.
-
Hi Sebas, It's a pity that you forgot the best solution to this problem. In the meantime, I will try with the EntityViewComponent approach. |
Beta Was this translation helpful? Give feedback.
-
I had some time to think about the CommandEngine idea. As you may have understood engines are ticked by the user, not svelto. Svelto provides an IStepEngine interface just to give an idea to the final user, but this interface is not really part of the framework. Svelto doesn't really care about when the user decides to update an engine. You can also have of course your own interface. Step(), Run(), Execute() is really something Svelto doesn't care about! This can be done now, the only thing I might have added in the framework would, maybe, have global way to retrieve "CommandEngine" from any point instead of injecting a reference. |
Beta Was this translation helpful? Give feedback.
Hello!
this is a great question. I had a solution in mind that I have never ended up developing. After we tried to implement an ECS UI framework we decided that was NOT the way to go. GUI is one of the few application that are really OOP/Event based in nature.
So for me the best solution is to code with UI with the standard tools/patterns and interface the UI presenter/controller/viewmodel with Svelto engines. I was starting to envision a solution, but unfortunately I don't remember anymore my train of thought. I think the idea was to be able to register "CommandEngines" that could have been retrieved inside presenter/view model and executed as normal commands from the GUI point of view. …