Fyrox Game Engine 0.34
I'm happy to announce that Fyrox 0.34 has been released! Fyrox is a modern game engine written in Rust, it helps you to create 2D and 3D games with low effort using native editor; it is like Unity, but in Rust. This release includes code hot reloading, project exporter, assets preview generation, UI prefabs, GLTF support, static and dynamic batching, keyboard navigation, animation support for UI, editor style and usability improvements and many more.
# How to Upgrade
At first, install the latest fyrox-template
by executing the following command: cargo install fyrox-template --force
.
Then execute this: fyrox-template upgrade --version=latest
. The amount of breaking changes in the code is quite low and
you can use this commit (opens new window)
as a guide to fix an existing project.
# Code Hot Reloading
Code hot reloading is a new feature of the engine, that allows you to recompile the game while it is running and the new version is then automatically loaded in the running game. This feature is super useful for rapid prototyping, because it turns Rust in some sort of a "scripting" language with close-to-zero iterations overhead. Since only the game code is reloaded, all heavy assets remains loaded in the memory and there's no need do the typical chain of actions: "close game -> write code -> compile -> run -> initialize -> load assets -> setup required conditions -> test", all you need to do is to "write code -> compile -> test". In action it looks like this:
This feature makes game development in Rust much more fun, because you can focus on the actual game development and not repeat all these redundant actions like a robot.
Huge respect to @zakarumych (opens new window), who did awesome research job and also implemented hot reloading in his game engine (opens new window) prototype (?).
# How To Use
At first, install the latest fyrox-template
by executing the following command: cargo install fyrox-template --force
.
Generate a project as usual (opens new window) and do the following instructions.
⚠️ If you have an existing project from one of the previous versions of the engine, the best way to add support for CHR is to re-generate the entire project and copy all the assets and game code in the new project. CHR requires very specific project structure and a small mistake in it could lead to incorrect behavior.
CHR is quite simple to use - a project generated by fyrox-template
already has all that is needed for hot reloading.
Yet, it requires some bootstrapping to start using it. At first, you need to compile your game plugin using the following
command:
RUSTFLAGS="-C prefer-dynamic=yes" cargo build --package game_dylib --no-default-features --features="dylib-engine" --profile dev-hot-reload
This command will compile the engine DLL (fyrox_dylib.dll/so
) and the plugin DLL (game_dylib.dll/so
). Please note the
mandatory environment variable RUSTFLAGS="-C prefer-dynamic=yes"
. It forces the compiler to link standard library
dynamically. It is very important, because if not set, the standard library will be duplicated in game plugin and engine,
which will lead to subtle bugs.
⚠️ Environment variables can be set in a different ways, depending on your OS. On Linux it simply prepends the actual command, on Windows it requires a separate command (opens new window). Other OSes can have their own ways of setting environment variables.
The next step is to compile the editor in CHR mode. To do that, run the following command:
RUSTFLAGS="-C prefer-dynamic=yes" cargo run --package editor --no-default-features --features="dylib" --profile dev-hot-reload
This command will compile the editor in CHR mode and run it. After this, all you need to do is to select build profile
in the editor to be Debug (HR)
:
Once that's done you can run your game by clicking on the green Play
button. You can switch between CHR and normal mode
(static linking) at any time. Keep in mind, that if you run the editor in CHR mode, it will also reload all changed plugins.
# Build Profiles
CHR uses separate build profiles: dev-hot-reload
(no optimizations) and release-hot-reload
(with optimizations).
Separate build profiles allows you to quickly switch between statically linked plugins and code hot reloading. This could
be useful if you're experiencing some issues with hot reloading (see next section for more info).
# Stability
CHR is very new and experimental feature of the engine, it is based on wildly unsafe functionality which could result in memory corruption, subtle bugs, etc. If you experience weird behaviour of your game after hot reloading, run the game in normal (static linking) mode instead. Please report any bugs in the issue tracker (opens new window) of the engine. CHR was tested on two relatively large games - Fish Folly (opens new window) and Station Iapetus (opens new window). You can download these projects and try CHR yourself.
# Technical Details and Limitations
CHR is using standard operating system (OS) mechanism of shared libraries (DLL for short). Pretty much any OS can load native code into a running process dynamically from a DLL. Any dynamically loaded library can then be unloaded from the process memory. This gives a perfect opportunity to reload game code in runtime. It may sound quite easy, but on practice there are a lot of issues.
# Plugin Entities and Reloading
Plugins can supply the engine with a predefined set of entities (such as scripts, etc.). These entities are serialized into
a memory blob before the plugin itself is unloaded. When all plugins are reloaded, this memory blob is used to restore
the state of plugin entities. That being said, pretty much all plugin entities must be serializable (implement Visit
trait).
# Trait Objects
Trait object are very problematic with hot reloading, because internally trait objects contains vtable with function pointers. These pointers can be easily invalidated if the plugin is unloaded. This applies even to engine trait objects, if they're created directly from the plugin side. The only way to bypass this issue is to use special methods from the engine to create its trait objects. It is possible to add a lint to clippy to check for such cases (see the respective issue (opens new window)).
# Dangling Objects
Current plugin system tries its best to remove all plugin's entities from the engine internals before reloading plugins. However, some objects could be overlooked by this system, which could result in crash or memory corruption. Current approach of preventing to having dangling objects is based on built-in reflection system - the plugin system iterates across all fields of every object and checks its assembly name. If the assembly name match the plugin's assembly name, then this object must be deleted before the plugin is unloaded.
# Non-serializable Entities
Not every object can be serialized, and in this case the current plugin system calls a special method to restore such non-serializable entities after hot reloading. Such entities could include server connections, job queues, etc.
# Editor Style and Usability Improvements
The editor has got a new look, that makes it more attracting and pleasant to work with:
# Multi-Script Support
Scene nodes can now have more than one script assigned. This greatly increases flexibility of scripting by allowing you to mix multiple scripts on the same node. Scenes made with previous "one node-one script" approach can still be loaded without any issues (backward compatibility is preserved).
You can assign scripts from both the editor and code. In the editor is multiple scripts looks like this:
To add a new script, click on the +
button and the select the script type from the dropdown list. Execution order of
the script methods is the same as their location in the list.
# Project Exporter
The editor now offers a new tool, that builds your project for various platforms (PC, WebAssembly, Android) and creates packages that are ready for deployment. This automated build system even allows you to build, upload and run your game directly on your smartphone in a few clicks.
At first, this tool installs required build tools, then it scans for used assets in the specified folders and clones them in the output folder. Then it compiles your game in release mode and clones executable file into the output folder. It can also run the final build if needed. The latter option is very useful for Android builds and basically allows you to test your games on a smartphone rapidly.
Project exporter is still a new, highly experimental tool, yet it could save heaps of time by removing a lot of manual work. For now it may lack some targets for specific environments, but thankfully it could be fixed very easily.
# Assets Preview Generation
The editor now generates previews for assets, which helps finding desired one quickly. Previously you'd need to click on each asset to see its preview in the asset previewer. Asset preview works with pretty much any asset that has sensible graphical representation. This is how it looks for prefabs:
Sound sources shows the waveform, it is not very informative as prefabs preview, but still can give some info about nature of the source:
The same applies for fonts, user interfaces, etc. Asset previews could take some time render and on some machines it could go out of control by taking too much time for rendering. If this will happen, you can always disable this feature in the editor settings.
# GLTF Support
GLTF support was one of the requested features and it is finally implemented (kudos to b-guild (opens new window)).
Use gltf
feature to enable the GLTF loader.
# Static and Dynamic Batching
Static baching is a technique that merges multiple meshes with the same material into a larger one, thus significantly reducing amount of draw calls on GPU, which in its turn increases rendering performance. This happens because each draw call has a small overhead, and when there are thousands of draw calls, it could significantly affect performance.
Dynamic batching is similar to static batching, but it works at each frame, merging specified meshes into a large one. This feature could be useful in limited cases only, since it is effective only at meshes with low polygon count (tens to hundreds of polygons).
To use static/dynamic batching all you need to do is to create a root Mesh scene node and specify its batching node to be Static or Dynamic. Next, you need to attach all the objects to it, which you want to be batched. The engine will do the rest of the work for you.
It will automatically create a batch per each material in all descendants of the root mesh node and merge all the geometry in an appropriate batch.
# Multiple UI Instances
Fyrox now supports multiple user interface instances. It was also possible before, but you'd have to manually create UI instances and manage them appropriately, which is quite bug-prone. However, this is not the main reason why there are multiple UI support. The main reason is code hot reloading, when the engine manages UI itself it can reload only a small portion of UI that comes from a game plugin. In vast majority of cases there's no custom widgets and nothing will be reloaded at all, increasing code hot reloading performance and reducing iteration times.
# Keyboard Navigation
For a long time fyrox-ui
framework didn't support keyboard navigation, which was quite annoying, since keyboard navigation
could save heaps of time when doing repetitive actions (such as filling in a series of input fields). Now fyrox-ui
supports
various keyboard navigation techniques. For example, this is how Tab/Shift+Tab navigation looks in the Inspector:
Trees can now be traversed using arrow keys as well:
# Animations in UI
UI now supports animations, which is useful to be able create interactive user interfaces. UI animations uses the same animation system that game scenes do with little to no differences. It is also possible to create state machines:
State machines allows you to mix multiple animations into one.
# Grid
Game scenes now have a oXZ-oriented grid, each square cell of it has 1m in size. It could be useful for grid snapping and estimating objects sizes by eye. The grid can be turned on/off in the editor settings.
# Animation Editor Improvements
It is now possible to select multiple curves at once in the animation editor, this makes editing much faster since you don't need to switch between curves. You can also select a track and all its curves will be selected at once as well. Curves are also colored now:
Background curve is also a new thing in the animation editor, it shows every other curve in the selected animation. It shown in pale gray color.
# UI Prefabs
UI system now supports prefabs, which allows you to put common UI widgets into a separate UI scene and use it in some other. This mechanism works the same as the one for game scenes. On practice it look like this:
Each button here is a prefab (shown in purple-ish color in the world viewer) and the prefab itself looks like this:
When you change such prefabs, its instances on other scenes will take the changes you've made automatically. This allows you to build complex user interfaces from simple "blocks".
# Adaptive Scroll Bar
Scroll bar's thumb size is now changes its size accordingly to the content size. Previously it had fixed size, which wasn't informative and in some cases it was quite annoying to use in "tight" spaces.
# World Viewer
World Viewer now is able to reorder its items by simple drag'n'drop. This is especially useful in user interfaces, where the order of widgets defines draw order. In action it looks like this:
# Shape Casting
Fyrox 0.34 allows to perform shape casting to check whether an arbitrary shape intersects with physical objects or not. It is similar to ray casting, however it could be used to check if there's an obstacle along a given line segment, which could be useful in AI.
# Grid Snapping Quick Access Panel
Grid snapping exists in the engine for more than 3 years already, but it was hidden in editor settings and overall usability was quite bad, simply because to enable it or change settings you'd need to open editor settings, find grid snapping section, tweak settings, close the editor settings, try the new settings. Rinse and repeat if you need other settings. In the new version all you need to do is to click onto magnet icon on the toolbar, set the required settings and that's pretty much all.
# Rendering Statistics
The editor now can show rendering statistics in a separate dockable panel, this information could be useful for optimization needs.
# Scene Preview
You can switch between shaded and wire frame rendering modes directly from the scene previewer. Wire frame rendering mode could be useful if you want to find something hidden behind walls or large objects.
# Asset Browser Performance
For a long time searching in the asset browser was quite slow, turned out that it was ./target
folder with build artifacts.
Usually, it is located in your project's directory and it could contain tens of gigabytes and tens of thousands files. Asset
browser included this folder in searching and that's why it was horribly slow.
# Mesh Control Panel
Mesh control panel helps you to perform various actions with meshes. For example you can create trimesh static colliders, convex colliders, add rigid bodies, etc. The panel opens automatically when a mesh node is selected and it looks like this:
# Reflection
Reflection system has gotten a small, yet very important feature (mostly for code hot reloading) - an ability to fetch assembly
name of anything that implements Reflect
trait. This feature is used to scan scene content for plugin entities and unload
them.
# Full List of Changes
The list is split into four sections for ease of reading and finding particular information.
# Added
- Code hot reloading for plugins.
- Ability to have multiple scripts on one scene node.
- Static and dynamic batching for meshes.
- Project exporter for automated deployment.
- Configurable build profiles for the editor.
- Ability to have multiple user interface instances.
- GLTF support (available via
gltf
feature). - Keyboard navigation support in the UI.
- Preview generation for assets in the asset browser.
- Grid for the scene preview.
fyrox-template
improvements to generate projects, that supports code hot reloading.AnimationPlayer
+AnimationBlendingStateMachine
widgets.- UI prefabs with ability to instantiate them.
Pool::try_get_component_of_type
+ the same forMultiBorrowContext
.NodeTrait::on_unlink
method.- Implemented
ComponentProvider
trait forNode
. MultiBorrowContext::get/get_mut
methods.- Ability to remove objects from multiborrow context.
newtype_reflect
delegating macro.SceneGraph::change_hierarchy_root
method.- Ability to change UI scene root.
- Property inheritance for UI widgets.
- Ability to instantiate UI prefabs by dropping prefab into world viewer/scene previewer.
- Ability to open scripts from the editor's inspector.
Control::post_draw
method.- Ability to reorder children of a scene node.
SceneGraph::relative_position
+SceneGraphNode::child_position
methods.- Ability to reorder nodes/widgets in the world viewer.
- Added more icons for widgets.
- Added support for UI animations in the animation editor.
- Configurable UI update switches.
- Ability to edit ui absm nodes in the absm editor.
AbsmEventProvider
widget.- Ability to enable msaa when initializing graphics context.
- Ability to change corner radius in
Border
widget. - Ability to draw rectangles with rounded corners in UI drawing context.
- Added layout rounding for
fyrox-ui
which significantly reduced blurring. - Added support for embedded textures in FBX.
Selector
widget.- Added project dir and scenes to open as cli args to editor.
utils::make_cross_primitive
helper method.- Ability to draw wire circle in the UI drawing context.
- Ability to draw WireCircle primitives in VectorImage widget.
- More tests.
- Vertex buffer API improvements.
- Rendering statistics window for the editor.
- Added shape casting in physics.
- Ability to unassign textures in material editor.
- Allow to set negative playback speed for animations in animation editor.
Scene::clone_one_to_one
shortcut for easier scene cloning.fyrox-dylib
crate to be able to link the engine dynamically.- Ability to link the engine dynamically to the editor.
- Added property editor for untyped textures.
- Added
Plugin::on_loaded
method. NetListener::local_address
method.Model::new
method.- Ability to disable space optimization of
InheritableVariable
on serialization. - Added CI for project template for all supported platforms.
- Added diagnostics for degenerated triangles when calculating tangents.
Pool::first_ref/first_mut
methods.- Added release keystore for android project templates.
- Collect rendering statistics on per-scene basis.
transmute_slice
helper function.- Ability to read GPU texture data.
- Experimental histogram-based auto-exposure for HDR (disabled by default).
- Short-path angle interpolation mode for
Curve
-Curve::angle_at
. - Property editor for
RcUiNodeHandle
type. - Adaptive scroll bar thumb.
- Ability to fetch current task pool from resource manager.
- Async icon generation for assets in the asset browser.
- Case-insensitive string comparison helper method
fyrox::core::cmp_strings_case_insensitive
. - Major performance improvement for searching in the asset browser.
- Configurable interpolation mode for animations.
- Ability to close popups using
Esc
key. - Added diagnostics for docking manager layout, that warns if a window has empty name.
- Keyboard navigation for tree widget.
- Ability to close windows by
Esc
key. - Focus opened window automatically.
- Keyboard navigation for
Menu
widget. - Added
ImmutableString
editor. - Docs for inspector module.
- Ability to deactivate menus using
Esc
key. PopupMessage::RelayedMessage
to re-cast messages from a popup to a widget.NavigationLayer
widget that handlesTab
/Shift+Tab
navigation.- Ability to switch check box state using space key.
- Ability to click button widget using
Space
/Enter
keys. accepts_input
for widgets that can be used for keyboard interaction.- Added keyboard navigation for input fields in the inspector.
- Highlight a widget with keyboard focus.
Visitor
docs.- Ability to open/close drop down list using arrow keys.
- Re-cast
Variant
message on enum property editor. - Focus popup content (if any) on opening.
- Keyboard navigation for list view widget.
- Focus window content (if any) on opening.
- Optional ability to bring focused item into view in navigation layer.
- Hotkey to run the game from the editor (default is
F5
). - Ability to increase/decrease
NumericUpDown
widget value by arrow keys. - Configurable command stack max capacity (prevents the command stack to grow uncontrollably, which could eat a lot of memory if the editor is running for a long time).
- Auto-select text on focusing
TextBox
widget. - Ability to render scene manually.
- Ability to set precision for
VecEditor
widget. - Ability to switch between shaded and wireframe mode in the scene preview.
- Multi-curve support for the curve editor widget.
Color::COLORS
array with pre-defined colors.- Ability to set different brushes for every curve in the curve editor.
- Apply different colors to curves in the animation editor.
- Show multiple curves at once when selecting tracks in the animation editor.
- Dropdown menu widget.
- Quick-access menu for grid snapping.
Create Parent
context menu option for scene nodes.- Add background curves concept to the curve editor widget.
- Smart placement for newly created objects.
- Added mesh control panel - allows to create physics entities (colliders, rigid bodies, etc) in a few clicks.
Reflect::assembly_name
to retrieve assembly name of a type.
# Changed
- Major style improvements for the editor UI.
- Migrated to Rapier 0.18.
- Refactored multiborrow context - removed static size constraint and made borrowing tracking dynamic and more efficient.
- Use
Result
instead ofOption
for multiborrowing for better UX. - Added panic on
Ticket::drop
to prevent dangling pool records. - Moved generic graph handling code into
fyrox-graph
crate. - Do not call
Control::update
for every widget:- in the editor on complex scenes it improves average performance by 13-20%.
- you have to set
need_update
flag when building the widget if you needControl::update
to be called.
- Mutable access to UI in
Control::update
. - Refactored
Selection
to use dynamic dispatch. - Refactored the entire editor command system to use dynamic dispatch.
- Split
SceneGraph
trait into object-safe and object-non-safe parts. - Run most of
Engine::pre_update
logic even if there's no graphics context. - Moved color space transformation to vertex shader of particle system to increase performance.
- Recalculate world space bounding box of a mesh on
sync_transform
instead ofupdate
. - Refactored rectpacker to use plain
Vec
instead ofPool
. - Moved rectangle-related code to
rectutils
crate. - Automatically unregister faulty resources if registering ok one.
- Prevent uvgen to modifying the actual surface data.
- Extracted uvgen module to
uvgen
crate. - Use simple vec instead of pool in octree.
- Moved
math
+curve
+octree
mods tofyrox-math
crate. - Moved lightmapper into a
lightmap
crate. - Support for backwards movement (negative speed) for navmesh agent.
- Moved the engine implementation into
fyrox-impl
crate,fyrox
crate now is a proxy to it. - Moved interaction modes panel to the toolbar.
- Made shader methods public to be able to create them manually.
- Show unassigned handles in orange color to attract attention.
- Major refactoring of
TextBox
widget that makes it much more pleasant to work with. - Major usability improvements for
DockingManager
tiles. Window
widget content is now linked toNavigationLayer
widget instance.- Prevented
TextBox
andNumericUpDown
widgets from sending change messages when they have not changed. - Reduced width and precision for worldspace position of current selection.
- Use
ImmutableString
for scene nodes and widgets to reduce memory consumption on duplicated strings. - Do not flush the renderer when changing scenes, to prevent various graphical issues.
- More informative names for curves in the animation editor.
- Change cursor icon when picking/dragging keys in curve editor.
- Major refactoring of coordinate system in the curve editor.
- Keep the animation player selected in the animation editor.
- Changed AABB validity to include zero-size dimensions to allow camera fitting to work with flat objects.
- Prefer prefab roots when selecting nodes in scene.
Reflect
trait bound forPlugin
trait.
# Fixed
- Fixed cascade shadow maps (CSM) rendering.
- Fixed crash when setting particle spawn rate too high.
- Fixed UB when using MultiBorrowContext.
- Fixed visibility of cloned widget.
- Set unique id for widget copies.
- Fixed crash when closing scenes.
- Fixed
Default
impl forPool
. - Fixed rare crash in
TextBox
widget when typing in something - Fixing double pixel loop (it was looping over y twice) in terrain internals.
- Fixed creating a MenuItem in editor.
- Force ui widget to recalculate layout if it was animated
- Registered property editors for all UI properties.
- Fixed incorrect FBX cluster loading (fixes incorrect rendering of FBX models)
- Fixed crash when selection range is incorrect in the
TextBox
widget. - Fixed crash in the animation editor when trying to rebind a track referencing deleted scene node.
- Properly expand tree items when building path in file browser widget.
- Fixed doubling of items under disks in file browser widget.
- Fixed track deletion in the animation editor.
- Fixed file browser behaviour on empty file path
- Select current dir in the asset browser.
- Automatically remove disconnected listeners from the log.
- Fixed support of custom layout panel of
ListView
widget. - Fixed async tasks at WebAssembly target.
- Fixed property inheritance for types with interior mutability.
- Keep selected brush when hovering mouse over a
Decorator
widget. - Fixed
TabControl
widget headers style. - Improved SearchBar widget style.
- Fixed incorrect script task handling (it was passing task result to all scripts, instead the one that launched the task).
- Prevent particle systems from over-spawn particles when spawn rates are high.
- Fixed incorrect vertex buffer data layout.
- Fixed crash if a selected node was deleted during asset hot reloading.
- Prevent moving a folder into its own subfolder in the asset browser.
- Fixed lightmap saving when corresponding lightmap textures were deleted.
- Sort rectangles back-to-front when rendering to prevent blending issues.
- Back-to-front sorting when rendering nodes with transparency.
- Fixed seams on skybox cubemap.
- Hide
should_be_deleted
field. - Do not update scripts on disabled nodes.
- Fixed sound context serialization (this bug caused all sound buses to disappear on load)
- Fixed potential crash in audio bus editor.
- Fixed crash when closing the editor.
- Fixed crash
attempt to subtract with overflow
in particle systems. - Fixed incorrect
Selection::is_empty
implementation. - Fixed canvas background color leaking to the rendered image on WebAssembly.
- Ignore
target
dir when doing search in the asset browser. - Fixed accidental enabling/disabling tracks when expanding them in the animation editor.
- Fixed editor layout saving and loading.
- Prevent
Inspector
properties from disappearing when expander is closed. - Use context menus instead of plain popups in color gradient editor.
- Fixed incorrect extension proposal for in the resource creator.
- Fixed incorrect resource creation in resource creator.
- Fixed sluggish tiles resizing in the docking manager.
- Keep the order of interaction modes the same.
- Fixed bring-into-view for
ScrollPanel
widget - not it does not jump unpredictable. - Do not pass keyboard input to invisible widgets.
- Handle edge cases properly when calculating curve bounds.
- Fixed "zoom to fit" functionality in the curve editor widget.
- Fixed sliding of the view in the curve editor widget on resizing.
- Fixed frustum culling flag usage.
- Fixed inspector syncing/context changing.
- Fixed crash when trying to get selected entity from empty selection.
- Fixed crash when closing scenes using
X
button on the tabs.
# Removed
- Removed
define_command_stack
macro - Removed redundant
old_selection
arg from change selection command
# Support
If you want to support the development of the project, click this link (opens new window). Also, you can help by fixing one of the "good first issues" (opens new window), adding a desired feature to the engine, or making a contribution to the book (opens new window)