Project OverviewLanguages: C++
Development Time: 12 Weeks
|
Demonstration Video |
Development and Strategy
Event System
The very base of this project revolves around the existence of an underlying user interface architecture. As such, an event system is needed in order to bind functionality to widgets. This system needed to be separate from the UI system so it can be utilized by other parts of the engine in later projects.
The event system contains a map that uses string event names as the key to a struct containing the event itself and its event listeners. Each event contains a class called Properties, which is another map containing AbstractProperty pointers with the property name as the key. AbstractProperty is simply an empty class, that a templated class called Property can derive from. Property contains data of type T, since it is a template. Since we are storing AbstractProperty pointers, we can store the templated Property class as an AsbtractProperty, allowing us to have a map consisting of whatever data we want.
The event listeners are stored in a similar fashion. We create an AsbtractEventListener class that contains a function pointer used for global and static functions. The EventListener template contains two additional pointers, a context pointer and a member function pointer, which allows us to register functions that are members of a certain context.
The event listeners are stored in a similar fashion. We create an AsbtractEventListener class that contains a function pointer used for global and static functions. The EventListener template contains two additional pointers, a context pointer and a member function pointer, which allows us to register functions that are members of a certain context.
Hierarchical UI System
The event system allows us to bind events to widgets for the functionality portion. The widget system itself is hierarchical, so it can keep track of what widget is currently "focused". The focused widget is typically whatever the mouse cursor is hovering over, and gets first priority over handling input events. The UI system itself contains a single root container widget. Each widget registered to the system is a child of the root window. Every widget has a vector of children, which allows for widgets that consist of other widgets, or container widgets that simply hold other widgets within its viewport. Every widget also contains its own transformation matrices. When a new widget is added as a child, it needs to know the transform of its parent, so its position within the parent is relative to where the parent is located.
Custom Widgets
The application itself consists of project-specific widgets to facilitate its functionality. One widget splits up loaded sprite sheets based on a metadata file that contains the sheet dimensions. For now, the editor assumes the sprite sheets are uniform and grid-based. Another widget holds the actual map field where the user does the editing. Both of these widgets are containers that hold a bunch of ActivatableWidget pointers, which are buttons without text labels. The events bound to each of these are defined and bound within the editor cpp file.
XML Map Format
The formatting for exported maps is meant to be easily parsed and edited for use in other game projects. The sprite sheets are defined in a sheet legend at the top, which contains the sheet ID as well as the image file name and dimensions. That is followed by a tile legend, which contains a tile ID, what sheet ID that tile belongs to, and that tile's coordinates on the sheet. The map data itself is separated into rows. Each row has the tile IDs and the count how many of each to place within that row.
Postmortem
What Went Well
- The creation of the event system provided a valuable piece to my ever-growing custom engine, and can be used in future projects.
- The UI system's architecture allows for easy growth and creation of custom widgets on a per project basis, in addition to standardized widgets contained in the engine.
Challenges
- Coming up with solutions on how to store any number of different data types in a single place was difficult.
- The amount of dependencies on the project required a lot of time dedicated to creating a robust event and UI system for everything to be build on top of, which gave less time for creating the actual editor functionality.
- Multiple iterations on the UI system were required. For example, I didn't realize the need for transformation matrices inside the widgets until much later in development.
What Was Learned
- I gained valuable insight on how UI and event systems are made, and the kinds of considerations that need to be taken into account when implementing them.
Future Plans
I plan to continue development on this project, and continue to iterate on the systems that were designed.
- Drop down menus
- Support for game specific tile properties (wall, solid, etc.)
- Support for selecting sprite sheets to load/multiple sheets loaded at one time
- Draggable widgets
- Zooming in/out
- Being able to customize the UI layout (docked tabs, floating windows, etc.)
Code Samples
-
EventSystem.hpp
-
EventSystem.cpp
-
Widget.hpp
-
Widget.cpp
-
UISystem.hpp
-
UISystem.cpp
<
>