It’s about time to put another Duality development writeup out there, and with this one I want to try a slightly different form. Getting away from the giant forum thread, we’re back to the blog.
With the recently started Duality v3.0 dev branch and continuous updates to the stable v2.x versions, there’s a lot of progress both visible and invisible to people following the binary release chain. The purpose of this split in two different versions is to reconcile the two conflicting goals of backwards compatibility and forward progress: Updating the Duality version behind a game or plugin project shouldn’t break it, so while fixing and adding features is fine, removing or changing them is not. However, as the project evolves, the requirement to maintain the same facade and feature set adds up to a constant maintenance cost that makes some things harder and pushes others beyond the threshold of viability. Old code just needs to be cleaned up once in a while, polished and streamlined – and that is exactly what v3.0 is for.
Don’t expect tons of big new features, but an improved API and the groundwork for future improvements on a more fundamental scale. That said, I’m (on a nerdy programmer-think basis) pretty excited about the things to come. You can find the full list of v3.0 issues here, and an overview on what has already been done below.
Among the bigger of the already implemented ones, there is a Font resource overhaul that allows for much more efficient serialization and arbitrary bitmap fonts with imported glyph boundaries as well as explicit kerning pairs. With the new data structures, it is not only possible to provide more versatile custom Font importers, but also more convenient to do so.
All logging related classes have been cleaned up, refactored and partially re-implemented to be more efficient, more consistent, more open to custom additions and fully thread-safe. Custom log channels can be defined in user code, are checked at compile time and displayed in the editor just like the three default channels. Similarly, VisualLog API has been updated to mirror Log changes.
The global Time class has been adjusted to introduce the (seconds) DeltaTime property as a first-class alternative to the (abstract) TimeMult property for achieving framerate independent simulation. It may not be a big change, but it does encourage developers to use real-time “X per second” unit representations for movement and change, rather than much less intuitive “X per frame” units. Thanks to @mfep for this contribution.
Sprite animation is now distinct from sprite rendering, allowing users to drop in a custom renderer while still using the default animator, and vice versa. The new class layout also means that the previously specialized renderers and animators for retro-RPG-like character controls can now be used interchangeably with their regular counterparts. As a side effect, it is no longer necessary to use any animation components for indexing single frames inside a sprite sheet.
GameObject and Scene queries have been overhauled to improve performance on both time and memory scales. At the same time their API has been adjusted to allow more efficient usage and reveal each operations impact instead of hiding implementation hints behind an opaque signature.
There is a new RenderSetup resource that replaces and extends the previous customizable rendering steps of the Camera component. While the base class acts as a shared setup for the former, it can also be extended with custom implementations to perform complex rendering setups in code, or to take full control over how a given scene, viewport or pass is rendered.
As a side effect of the cleanup, it is now easily possible to do fixed-viewport or fixed-resolution rendering while Duality handles the details of matching the desired resolution to the player’s screen or resizeable window. The same feature is also used to perform visual picking operations in the editor at a lower resolution for improved performance on complex scenes.
In addition to all of the above, there have been lots of tweaks, fixes and small additions to the ongoing v2.x branch, which you will get in every new Duality install and via package manager updates for existing projects.
By far the biggest recent change is a deterministic component execution order which will make sure that initialization, shutdown, updates and queries to every component follow a type-based schedule that can be adjusted using attributes. This feature addresses situation where code execution of one component type is required to be specifically before or after another component type, which up until now could neither be stated as a requirement nor guaranteed across all edge cases.
When not specified explicitly, an implicit execution order constraint is generated from RequiredComponent attributes, assuming that “A requires B” also means “A needs to execute after B”. As an override to this behavior, every component can now specify any number of ExecutionOrder attributes, each of them placing it before or after a certain component type. It is also possible to use abstract base classes (such as Renderer) or interfaces (such as ICmpSpriteRenderer) as anchors, so more general constraints are possible as well. Cycles in the constraint graph that can be built this way are detected and logged as a warning, so problems arising from erroneous attributes can be fixed early in the process.