Last semester, I teamed up with some other students to create Scumbag Galaxy. My role in the project was comprised almost entirely with my custom event-based user interface API. I had to build the system from the ground up using all original code. The engine we used (MonoGame) was a frequent tool for developing Xbox 360 games. As such, in-engine mouse support was near non-existant. So I created an efficient engine to run mouse events. On the front end, the engine makes use of layered rectangle objects, each with their own mouse events. Boxes on ‘top’ capture events and display above ‘lower’ objects. All of this is handled through the parent UIBox class. All UI elements are derived from it, allowing the engine backend to work seamlessly no matter what type of window is needed during the design process.
The backend of the engine is where things get more complex. I used a custom version of quad tree architecture to achieve resource-efficient mouse collision detection. Native MonoGame only provides the mouse location for the current frame. The GameBoard class manages nearly everything. On creation, it divides up the screen space using a quickly-searchable tree structure. The smallest section of screen that fits into this tree holds an array of references to UI Boxes that overlap with it. In the UIBox constructor, the object places itself into the GameBoard tree (ordered by layer and visibility). This system is highly efficient to determine with which objects the mouse collides. Instead of checking thousands of UI objects every frame, the tree structure reduces that number to less than 10.
Mouse events are checked for during every frame by comparing the previous mouse location with the current one (but it is much more complicated than that). Layer-based mouse events have innumerable edge cases. If the mouse stays on a lower object for both steps, but moves onto a higher layered object (one that overlaps the lower object) in those two steps, what events trigger? Comments in the code on GitHub go through each of these ideas and how they were fixed, so I won’t cover them here. When a mouse event is detected for a UIBox, it calls that box’s corresponding mouse event function.
View and Zoom Support
I implemented a fully scale-able viewport system. The player can zoom in on text and units or pan around the board, all without breaking the underlying mouse event features.
Window Drag and Layering
The API can (and is) used to create dynamic window objects with proper layering. Drag and drop functionality, mouse-enter and mouse-leave, and clicks all support the layering functionality.
Efficient and Elegant Upscaling
Due to the efficiency of my custom tree data structure, stress tests proved that the engine could support the full-force complexity of tactical RPG games filled with tens of thousands of UI objects.
Quickly and dynamically switch between entirely different screen layouts. This functionality was used for our game's title screen, but could easily be used for entirely new UI windows such as maps or inventory screens.
Copyright © 2016 Daniel Timko