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):

build profiles

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:

editor

# 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:

multiscript

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.

project export

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:

asset preview

Sound sources shows the waveform, it is not very informative as prefabs preview, but still can give some info about nature of the source:

sound source preview

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.

batching

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:

keyboard navigation

Trees can now be traversed using arrow keys as well:

tree keyboard navigation

# Animations in UI

animation

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:

absm

State machines allows you to mix multiple animations into one.

# Grid

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:

colored curves

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:

ui prefab

Each button here is a prefab (shown in purple-ish color in the world viewer) and the prefab itself looks like this:

button prefab

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

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:

reorder

# 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

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

rendering statistics

The editor now can show rendering statistics in a separate dockable panel, this information could be useful for optimization needs.

# Scene Preview

wireframe

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:

mesh control panel

# 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 for MultiBorrowContext.
  • NodeTrait::on_unlink method.
  • Implemented ComponentProvider trait for Node.
  • 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 handles Tab/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 of Option 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 need Control::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 of update.
  • Refactored rectpacker to use plain Vec instead of Pool.
  • 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 to fyrox-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 to NavigationLayer widget instance.
  • Prevented TextBox and NumericUpDown 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 for Plugin 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 for Pool.
  • 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)

Fyrox Engine 2019 - 2024