CI type-checks bevy_mod_scripting for aarch64-linux-android and aarch64-apple-ios; nothing is run on a device. Both need the target’s C toolchain (Android NDK, Xcode) as the Lua runtimes are built from source, and Android needs a bevy activity feature in your app (e.g. bevy/android-native-activity).
Language
Android
iOS
Lua51, Lua52, Lua53
yes
no: the Lua sources call system, which the iOS SDK marks unavailable (fixed upstream in Lua 5.4)
Lua54
yes
yes
Luajit, Luajit52
yes
no: luajit-src can’t build for iOS, which also forbids JIT
Luau
yes
yes
Rune
yes
yes
unsafe_lua_modules loads native libraries at runtime, which iOS restricts.
In order to fit as many use cases as possible, BMS allows you to disable a lot of its functionality.
By default all of the useful features are enabled, but you may disable them if you wish if you are only needing BMS for script lifecycle management, and want to populate the bindings yourself.
Feature
Description
core_functions
If enabled, will enable all core functions, i.e. bevy integrations which let you interact with Bevy via reflection
bevy_<bevy_crate>_bindings
enables bindings for the specified bevy crate, e.g. bevy_ecs, bevy_transform, bevy_pbr etc. for a full list of available modules check out The Cargo manifest
A script does not participate in any callbacks until it is evaluated, to evaluate a script you must first attach it to an entity, or to a static script entry.
To evaluate a script, add it to a ScriptComponent manually attach it.
#![allow(unused)]
fn main() {
extern crate bevy;
extern crate bevy_mod_scripting;
use bevy::prelude::*;
use bevy_mod_scripting::prelude::*;
fn load_script(asset_server: Res<AssetServer>, mut commands: Commands) {
let handle = asset_server.load::<ScriptAsset>("my_script.lua");
commands.spawn(ScriptComponent(vec![handle]));
}
}
Prefer using strong asset handles, internal references will only persist weak versions of the handle, leaving you in control of the asset handle via the container component.
#![allow(unused)]
fn main() {
extern crate bevy;
extern crate bevy_mod_scripting;
use bevy::{asset::Assets, prelude::*};
use bevy_mod_scripting::prelude::*;
fn add_script(mut script_assets: ResMut<Assets<ScriptAsset>>, mut commands: Commands) {
let content: String = "x = 0".into();
let mut script = ScriptAsset::from(content);
script.language = Language::Lua;
let handle = script_assets.add(script);
commands.spawn(ScriptComponent(vec![handle]));
}
}
Scripts added directly through assets, or via asset server without an asset path, will not contain path information, and logs will be slightly less useful.
You can use attach and detach commands, which will run the script pipeline, repeatedly until the requested script attachments are processed. If you don’t want to incur big frametime slowdowns, you can instead send ScriptAttachedMessage and ScriptDetachedMessage manually, and let the pipeline pick these up as normal.
When you no longer need a script asset you can freely unload it as soon as it has been loaded, but the script attachment will persist.
In order to trigger the on_script_unloaded etc. callbacks, you need to remove the script from the ScriptComponent or detach it manually via DetachScript if it’s static.
Once the last script in a context is removed, the context itself will also be removed.
In the script, import_namespace("bms").current_entity() returns the entity the script is attached to.
Be wary of path separators, by default script ID's are derived from asset paths, which are platform dependent. Make sure to use `std::path::PathBuf` if you are targetting multiple platforms.
Some scripts do not require attaching to an entity. You can run these scripts by loading them first as you would with any other script, then either adding them by issuing a AttachScript command like so:
Scripts can run logic either when loaded or when triggered by an event. For example the script:
print("hello from load time")
function on_event(arg1)
print("hello from event time")
print(arg1)
end
Will print “hello from load time” when the script is loaded, and “hello from event time” when the script receives an event targeting the on_event callback with a receiver list including this script or entity.
In order to trigger on_event you need to first define a label, then send an event containing the label:
#[derive(Reflect)]
pub struct MyReflectType;
// define the label, you can define as many as you like here
callback_labels!(OnEvent => "on_event");
// trigger the event
fn send_event(mut writer: EventWriter<ScriptCallbackMessage>) {
// allocated values live as long as references to them
let my_reflect_payload = ReflectReference::new_allocated(MyReflectType);
writer.send(ScriptCallbackMessage::new_for_all_scripts(
OnEvent,
vec![my_reflect_payload.into()],
));
}
Note the second argument is the payload we are sending with the event, in this case we are sending an arbitrary reflect type MyReflectType. This can be any type you like, as long as it implements Reflect.
Other variants of the ScriptValue enum are available for sending different types of data, such as ScriptValue::Integer for primtive, types.
In order for the events you send to actually be picked up, you need to inject special systems into your application. These systems will listen for the events and trigger the appropriate callbacks on the scripts:
Note the system is parameterized by the label we defined earlier, and the scripting plugin we are using. You can add as many of these systems as you like.
The event handler will catch all events with the label OnEvent and trigger the on_event callback on all targeted scripts which have that callback defined.
In order to handle events in the same frame and not accidentally have events “spill over” into the next frame, you should make sure to order any systems which produce these events before the event handler systems.
You can also use manually issued RunScriptCallback commands to trigger script callbacks as well. These must be run from a exclusive system, or via a any other system but with limited access to the world (See the WithWorldGuard system param, which will allow you to create a WorldGuard and use it to run the commands)
In this book we refer to anything accessible by a script, which allows it to communicate with your Rust code a binding (which in previous versions was more generically referred to as a script API).
The “binding” here being used as in: binding script code to rust code.
Namespaces are a way to group functions together, and are used to prevent naming conflicts. You can have multiple namespaces, and each namespace can have multiple functions.
Language implementations will also look for specific functions registered on your type first before looking at the generic ReflectReference namespace.
Everything callable by scripts must first be registered in the dynamic function registry. Notably we do not make use of the normal bevy function registry to improve performance and usability. This means you cannot call just any function.
In order for a function to be callable by a script it must adhere to a few requirements:
Each argument must implement FromScript.
Each return type must implement IntoScript.
Each argument must also implement GetTypeDependencies
Each return type must also implement GetTypeDependencies
The into/from requirements allow us to convert these types to ScriptValue’s, and each supported scripting language can then marshall these into the script.
Note these types are implemented for primitives, but if you want to interact with one of your Reflect implementing types, you will need to use one of R<T>, M<T> or V<T> wrappers in place of &T, &mut T and T respectively.
These wrappers enable us to safely interact with bevy, and claim any necessary mutex’es on Resources, Components or Allocations.
The GetTypeDependencies, trait is simply a local trait alias for GetTypeRegistration with less strict type requirements. It allows us to register all the types necessary for the function calls, so that you don’t have to register anything manually. If your type implements GetTypeRegistration you should not face any issues on this front.
This will allow you to call this function within lua like so:
some_type:hello_world("hi from method!");
hello_world2("hi from global!");
Note the new_unregistered call instead of new, this is because GlobalNamespace is not a Reflect type, and the new call also automatically registers the type in the reflection registry.
The above is a bit tedious, so instead you can use the script_bindings macro, which applies to impl blocks like so:
#[script_bindings(name = "test_fn")]
impl TestStruct {
/// My docs !!
///
/// Arguments:
/// * `_self` - the first argument
/// * `arg1` - the second argument
/// Returns:
/// * `return` - nothing
fn test_fn(_self: R<TestStruct>, mut arg1: usize) {}
}
pub fn main() {
let mut app = App::new();
register_test_fn(app.world_mut())
}
Note the documentation will automatically be picked up and stored for the purposes of reflection and documentation generation, including argument/return type specific docs.
Generally all reflect types are wrapped in: R<T>V<T> or M<T> when writing script bindings. However some types may opt in to implement FromScript or IntoScript directly.
Doing this requires implementing a set of other traits which tell the runtime how to manage your type in conversion and function dispatch.
The most trivial way to allow your type to be used directly in bindings is to apply derive macros like so:
#![allow(unused)]
fn main() {
#[derive(Reflect, ScriptType)]
pub struct MyThing(usize);
#[script_bindings(remote)]
impl MyThing {
// Can now be used directly without V<> wrapper, acts exactly like V<MyThing>
pub fn test(thing: MyThing) -> MyThing {
thing
}
}
}
ScriptType implements FromScript, IntoScript, ArgMeta, GetTypeDependencies and TypedThrough at once, converting through V<Self>.
If you wish to customize one of the conversions, write that impl by hand and use #[script_type(from = custom)] or #[script_type(into = custom)]. This skips the generated impl and documents the type as opaque in generated docs. See the custom conversions example for more information.
Each script function call always receives an additional context argument: FunctionCallContext.
You can opt-in to receive this argument in your own function definitions by adding it as the first argument.
The context contains requests from the caller to your function, such as “I am calling you from a 1-indexed array system, please convert the index first”, This argument is only relevant if you’re targeting multiple languages.
It also allows you to retrieve the world via FunctionCallContext::world().
You can use this as follows:
NamespaceBuilder::<ReflectReference>::new(&mut world)
.register(
"hello_world",
|ctx: FunctionCallContext, s: String| {
let world = ctx.world()?;
let should_use_0_indexing = ctx.convert_to_0_indexed;
println!(should_use_0_indexing);
println!(s)
Ok(())
},
);
There are a few reserved functions that you can override by registering them on a specific type:
Function Name
Description
Overridable?
Has Default Implementation?
get
a getter function, used for indexing into a type
❌
✅
set
a setter function, used for setting a value on a type
❌
✅
sub
a subtraction function, used for subtracting two values
✅
❌
add
an addition function, used for adding two values
✅
❌
mul
a multiplication function, used for multiplying two values
✅
❌
div
a division function, used for dividing two values
✅
❌
rem
a remainder function, used for getting the remainder of two values
✅
❌
neg
a negation function, used for negating a value
✅
❌
pow
a power function, used for raising a value to a power
✅
❌
rsub, rdiv, rrem, rpow
the reflected sub, div, rem and pow, called on the right operand when the left one is not a value, i.e. 1 - v calls rsub(v, 1). add and mul are called with their operands swapped instead
✅
❌
eq
an equality function, used for checking if two values are equal
✅
❌
lt
a less than function, used for checking if a value is less than another
✅
❌
iter
an iterator function, used for iterating over a value
❌
✅
display
a display function, used for pretty printing values
❌*
✅
debug
a display function, used for displaying the internals of a value
❌*
✅
* - You can register an instance of ReflectDisplayWithTypeInfo type data on Reflect implementing types to override their printing behavior
In this context overridable indicates whether language implementations will look for a specific function on your type before looking at the generic ReflectReference namespace. You can still remove the existing registration for these functions on the ReflectReference namespace if you want to replace them with your own implementation.
Note the ReflectReference namespace is special, in that functions defined on it, act like a fallback and hence apply to ALL references.
The type in the impl block you apply script_bindings to (or the one passed to NamespaceBuilder) decides where the functions are available:
impl target
Available on
Receives
MyType
MyType values and the MyType type, as imported from its namespace
whatever you declare
ReflectReference
every value, after MyType’s own functions
the value
StaticTypeRef
every type, after MyType’s own functions
the type, filled in automatically
NamespaceMembers
the namespace given by namespace = "...", i.e. import_namespace("my_game")
whatever you declare
GlobalNamespace
the builtin globals, i.e. no prefix
whatever you declare
When the same name exists in more than one place, the most specific one wins.
For ReflectReference and StaticTypeRef in a script_bindings block outside of the crate defining them, use the remote attribute.
The StaticTypeRef namespace is the equivalent for types, i.e. Vec3 in Vec3.new(..).
Functions registered on it are available on every type, and receive the type as their first argument, which is filled in automatically:
local Vec3 = import_namespace("math").Vec3
print(Vec3.describe()) -- "glam::Vec3"
A function registered on the type itself always takes priority over one on StaticTypeRef.
The new constructor is implemented this way, see Constructing Arbitrary Types.
A shared function’s signature can refer to the type it is bound to, using the placeholder types BoundValue (a value of the type), BoundFields (a table of the type’s fields) and BoundHandle (a handle to an asset of the type).
Wherever the function is bound to a type, these are replaced with the type, so the documentation and the Lua language server files show, for example, MyStruct.new(payload: MyStruct.Fields): MyStruct rather than a generic table and reference, or Image.load(path: string): Handle<Image>.
Functions using BoundHandle are only documented on asset types, i.e. types registered with register_asset_reflect.
The same placeholders work for functions on EnumVariantRef, which are bound to each variant of each enum when the LAD file is built (as functions in the Variant namespace). Placeholders left unbound are documented as what they are at runtime, a ReflectReference and a table.
The enum variants of a type are also available on the type, i.e. MyEnum.Variant, after the functions.
local my_game = import_namespace("my_game")
my_game.spawn_enemy(5.0)
Values are registered per namespace with AppScriptNamespaceMembers::register_value, and are made each time the namespace is imported.
The only globals are the builtins: import_namespace, export_namespace and register_callback. world is in the bms namespace: import_namespace("bms").world.
Types are not globals, which keeps types with the same name from shadowing each other. Use the filter of CoreScriptGlobalsPlugin to choose which types scripts can look up by name.
You should be able to achieve what you need by registering script functions in most cases. However sometimes you might want to override the way contexts are loaded, or how the runtime is initialized.
This is possible using Context Initializers and Context Pre Handling Initializers as well as Runtime Initializers.
It is however always reccomened to use the dynamic script function registry whenever possible, as it is more flexible and easier to use. It also allows you to introspect available functions easier.
For example, let’s say you want to set a dynamic amount of globals in your script, depending on some setting in your app.
You could do this by customizing the scripting plugin:
let plugin = LuaScriptingPlugin::default().add_context_initializer(|_context_key: &ContextKey, context: &mut Lua| {
let globals = context.globals();
for i in 0..10 {
globals.set(i, i);
}
Ok(())
});
app.add_plugins(plugin)
The above will run every time the script is loaded or re-loaded and before it handles any callbacks.
If you want to customize your context before every time it’s about to handle events (and when it’s loaded + reloaded), you can use Context Pre Handling Initializers:
#![allow(unused)]
fn main() {
use bevy::prelude::*;
use bevy_mod_scripting::prelude::*;
fn scripting_plugin(app: &mut App) {
app.add_plugins(LuaScriptingPlugin::default()
.add_context_pre_handling_initializer(|context_key: &ContextKey, context: &mut Lua| {
let globals = context.globals();
if let Some(script_id) = context_key.script_id.as_ref() {
globals.set("script_name", script_id.to_owned());
}
Ok(())
}));
}
}
Some scripting languages, have the concept of a runtime. This is a global object which is shared between all contexts. You can customize this object using Runtime Initializers:
Each script runs in a context. By default BMS will create an individual context, or sandbox, for each script-entity pair that is run. This means that each script-entity pair will have its own set of global variables and functions that are isolated from other scripts. However, sometimes this might not be desirable. If you are not worried about scripts interfering with each other, or if you want to easily share data between scripts, you can consider using a different context policy.
A shared context means that all scripts run in the same context; there is in fact only one context to run in. If the scripts interact heavily with each other, this may be what you want.
To enable a shared context, set the corresponding context policy on the scripting plugin:
Shared contexts require your scripts to be more careful about how they register callbacks due to the nature of top level functions belonging to the “currently loaded” global script. See the callbacks section
A per script context provides each script with their own context. However, scripts may be attached to multiple entities, in which case a single script context is shared by multiple entities.
To enable per script contexts, insert the ContextPolicy::per_script() resource.
we read policy_b like this: if ContextKey has a script, return a ContextKey with only a script. Failing that ContextRule::Shared always returns an empty ContextKey.
One may also provide an entirely custom rule by implementing the ContextKeySelector trait.
function on_script_loaded()
print("doing things")
end
this callback is refered to by “name”, and on the rust side can be invoked either via RunScriptCallback command, or by setting up an event handler system which passes on ScriptCallbackMessage’s to scripts.
The key thing to note about this type of callback, is that if the script is ever reloaded, and the contents of this callback change, the logic inside it will also be hot-reloaded.
register_callback("on_script_loaded", my_registered_callback)
function my_registered_callback()
print("doing things")
end
Registered callbacks, take priority over freestanding ones, and contrary to freestanding callbacks, they are “frozen”. I.e. once a callback is registereed in this manner,
hot reloads won’t affect the logic inside them.
This works well when using shared contexts, where scripts will overwrite top level functions when being loaded. You can use the on_script_loaded callback to register all your scripts callbacks while they are loaded as top level functions, and when future loads happen, every callback will be issued correctly.
This functionality is implemented at script plugin level, so some languages might not support this. All core languages do however.
It’s possible within BMS to inject new systems from within scripts themselves.
Systems introduced by scripts can run in parallel to other systems, and can be freely inserted between any other system, including other script systems.
BMS also provides utilities for visualising schedules using dot graphs, allowing low-effort modding frameworks for game authors.
Bevy doesn’t support reflecting schedules, so BMS rolls it’s own schedule registry resource: AppScheduleRegistry, which can be used to add any custom schedules you want to interact with. The default Bevy schedules will be pre-populated for you.
Once you’ve registered your schedule you will be able to interact with it in scripts like below:
local world = import_namespace("bms").world
local update_schedule = world.get_schedule_by_name("Update")
local systems = update:systems()
local system_with_name = update:get_system_by_name("my_system")
To insert a system you will need to use bms.system_builder like below:
local bms = import_namespace("bms")
local attachment = bms.ScriptAttachment.new_entity_script(bms.current_entity(), bms.current_script())
local system = bms.system_builder("my_system", attachment)
:exclusive()
:after(some_other_system)
:before(another_system)
This will let you call world.add_system like so:
local world = import_namespace("bms").world
world.add_system(update_schedule,system)
Besides systems (after/before), script systems can be ordered against:
other script systems, by callback label and script, which must have been added before: :after_script_system("on_physics", attachment)
sets: :after_set(set), :before_set(set), and put in sets: :in_set(set)
Scripts register their own sets into a script.* namespace, i.e. to group and order the systems of a mod. A set is removed from its namespace when its script unloads, registering it again gives the same set:
local ai = world.register_new_system_set("script.my_mod", "ai")
-- other scripts: import_namespace("script.my_mod").ai
Rust sets the game registers are members of their namespace, the BMS bindings register the main Bevy sets (i.e. transform.TransformSystems.Propagate, time.TimeSystems):
:run_if only runs the system if the condition holds, conditions are evaluated in Rust without calling the script. Every condition passed to run_if must hold:
local c = bms.conditions
builder:run_if(c.any_with_component(Enemy))
:run_if(c.not_(c.resource_exists(Paused)))
:run_if(c.on_timer(0.5))
Conditions: resource_exists, resource_changed, resource_added, any_with_component, on_timer(seconds), run_once and the combinators not_, and_, or_ (named so as not, and, or are Lua keywords).
A script system is identified by its attachment and callback label: adding it again replaces the old one (with a warning). Script systems are removed when their script unloads or reloads, a reloading script adds them again in on_script_loaded.
Bevy cannot change a schedule while it runs, i.e. PreUpdate where scripts are loaded. Systems added to (or removed from) a running schedule are added (or removed) once it finishes, removed systems stop running right away. world.add_system then returns a system which can be used for ordering, but has no node in the schedule yet.
Errors in script systems are sent as ScriptErrorMessages, like those of other callbacks.
The system builder allows script authors to parameterise systems, using resource and query functions.
The order in which those functions are called, will define the order in which arguments will be provided to the specified script callback.
For example:
local bms = import_namespace("bms")
local world = bms.world
bms.system_builder("my_system")
:query(
world.query()
:component(ComponentA)
:component(ComponentB)
:with(ComponentC)
:without(ComponentD)
)
:resource(ResourceA)
will create a system which calls the specified callback my_system with 2 arguments:
The results of the query, each a list of the entity followed by the components in the order they were added: {entity, a, b}
The ReflectReference to ResourceA
function my_system(results, resource_a)
for _, result in ipairs(results) do
local entity, a, b = result[1], result[2], result[3]
end
end
resource and component give read and write access. Use resource_read and component_read for read only access, systems reading the same resources and components can run in parallel, while systems writing them cannot:
Queries can also retrieve components entities don’t need to have (optional, optional_read, nil for entities without them), and only match entities whose components were added or changed since the system last ran (added, changed, filters like with). Outside of systems, added and changed are relative to the last app update:
Queries of a system may access the same components if they can’t match the same entities, i.e. component(A):with(B) and component(A):without(B), otherwise the system reports an error and doesn’t run.
An exclusive system can be created using the exclusive function call on the system builder.
This allows the system to access everything as in a normal event handler.
Non-exclusive systems, will only be able to access the set of components and resources as parameterized when building the system. This is why we can run the system in parallel to other non-overlapping systems.
Components are only accessible on the entities matched by the queries declaring them, i.e. with world.query():component(A):with(B) the system can’t access A on entities without B, since Bevy runs it in parallel with systems accessing A on those. Components only used as filters (with, without) can’t be accessed.
Exclusive systems on the other hand, cannot run in parallel.
The system injected will be similar to an event handler, however it will only trigger the specified script, and without any entity, in the first example you’d see the following lua callback:
function my_system()
print("I am a dynamic system")
end
In the future we hope to embedd live WASM code examples into the documentation, for now the best source of example scripts will be our regression test suite available in all supported languages in our github repository.
BMS provides a custom Language Agnostic Declaration format (LAD), which can be used to integrate with other tooling.
If you use #[script_bindings] macros, these files will magically contain your code comments, bindings as well as everything else required to drive other powerful integrations.
The declaration file is generated from the type registry and serves as a light abstraction layer simplifying the traversal of all
available types, namespaces and functions.
You can customize how / where and if these files are stored using the main BMS plugin group:
app.add_plugins(BMSPlugin.set::<ScriptingFilesGenerationPlugin>(
ScriptingFilesGenerationPlugin::new(
true, // enabled, you can use a compilation feature to disable this here
PathBuf::from("assets").join("definitions"),
Some(PathBuf::from("bindings.lad.json")), // do also save the ladfile itself
"Core BMS framework bindings",
true,
true,
),
));
This plugin is only available when one of the sub features (like lua_language_server_files) mentioned in this chapter is enabled.
You might not want to run this pipeline in your final binary, but rather bundle some of the generated files into some sort of development pack for modding. You can use compiler flags like #[cfg(not(debug_assertions))] to disable ladfile generation at runtime, or simply disable the lower level features within BMS to avoid compiling related dependencies too.
This feature is in early stages, the definitions provide 90% of the way there, but there are rough edges that will need to be worked out
Luals or LLS, is an open source language server which integrates with many IDE’s.
It is powered by lua specific annotation or definition files which BMS can generate directly from its own LADfiles.
To enable this simply enable the lua_language_server_files feature, and a bindings.d.lua definition file, plus a bindings.<namespace>.d.lua file per namespace, will be generated in the LADfile output directory in the Startup schedule.
Script writers can then use this generated file by pointing their .luarc.json file to these definitions:
The generated definitions file is large (over a megabyte with the default bindings), and the language server skips Lua files bigger than workspace.preloadFileSize (in KB, 500 by default).
If completions for BMS types are missing, i.e. Vec3. suggests nothing, raise this limit above the size of the generated file and restart the language server.
Processing script atachments and detachments is all done by the script pipeline.
In versions prior to 16.0 the pipeline was represented by load unload commands, which atomically processed scripts. Since then the pipeline has been maassively overhauled to allow:
Customizing callbacks during the process
Budgeting the work done by the pipeline each frame
Allowing async steps in the process
The new pipeline works like this:
A set of systems in PreUpdate “filter” incoming ScriptLifecycleMessage’s (attachments and detachments, processed in the order they were written) as well as ScriptAssetModifiedMessage’s (some generated from asset events, others from component hooks). The filtered events are wrapped in ForPlugin<P>(inner) events based on the language of the underlying event’s.
A set of other systems then converts those into active ScriptMachine’s
Then inside the ScriptProcessingSchedule<P>, which is only run in PreUpdate if there are active machines, we tick each machine until it either errors or reaches its final state.
Script loading is handled by the following state machines:
graph TD;
A[Loading Initialized]
B[Reloading Initialized
Enter: OnReloaded callback]
A --[ new context ]--> C
B --[ existing context]--> C
C[Context Assigned
Enter: OnReloaded + OnLoaded callbacks]
C --> D
D[Loading Completed]
E[Unloading Initialized
Enter: OnUnloaded callback]
F[Context Removed]
G[Resident Removed]
E --[Last resident]--> F
E --[More residents]--> G
H[Unlading Completed]
F --> H
G --> H
Hooks, such as on_script_loaded are implemented using bevy observers. This allows you to hook into various parts of the loading process and add your own callbacks, i.e.:
By default all machines are ticked to completion in the frame they start. Setting ScriptLoadingPipeline::tick_budget limits the amount of machine ticks (steps in the state machine above) per frame, spreading the loading of many scripts over multiple frames. The budget counts steps, not time, so a step running a slow callback can still take up the frame.
You can override this and many other settings (including disabling the core callbacks) by changing the ScriptLoadingPipeline<P> settings via your scripting plugin’s set_pipeline_settings.
The script loading/unloading order will look as follows:
the order in which components are attached/detached, will determing what order scripts will be processed
an attachment whose script asset is still loading holds back the attachments and detachments after it, until it finishes loading
scripts are processed one-by-one, i.e. each machine is ticked to completion before the next one is started
meaning for example if two scripts are loaded, their on_script_loaded hooks will not run at the same “lockstep”.
loading/unloading might happen over multiple frames, depending on the pipeline’s settings.
Given the same inputs (the same order of attachments, detachments and app updates), scripts load in the same order and callbacks reach scripts in the same order, on every run and machine: script contexts are kept in insertion ordered maps, and the pipeline budget counts steps rather than time.
What BMS cannot control:
which frame a script asset finishes loading in, as asset IO is asynchronous. Wait for scripts to load (see below) before relying on frame timing.
language internals, for example Lua iterates tables with string keys (pairs) in a different order on each run. BMS seeds Lua’s math.random with 0 when creating a context, call math.randomseed to change it.
In order to check on the pipeline and figure out when everything is ready, you can use the ScriptPipelineState<P> system parameter in a system as shown in the script_loadingexample.
The use of handles for scripts is perhaps the biggest user-facing change. It makes BMS asset usage follow Bevy’s conventions. It requires less configuration. It is more idiomatic.
Because ScriptComponent accepts handles, it is no longer necessary to store the handles somewhere to prevent the script from unloading. Nor is it necessary to configure an asset path to script id mapper.
It is still beneficial to retain script assets in memory, for certain features to work. For example,
script_asset will not be able to retrieve the asset path of the script if it’s not retained.
In prior versions, BMS would immediately evaluate a ScriptAsset when it was loaded, and if multiple script assets were loaded, they would be evaluated in non-deterministic order. (See issue #426.) Script assets no longer evaluate immediately. Script assets are only evaluated when their handles are added to a ScriptComponent or they are added to StaticScripts.
In addition when a ScriptComponent loads its scripts, it loads them sequentially.
These were present to associate the previous ScriptId with the asset ID and language.
That is no longer necessary as the ScriptAsset knows its own language.
pub struct ScriptAsset {
/// The body of the script
pub content: Box<[u8]>,
/// The language of the script
pub language: Language,
}
Choosing how scripts run is a big policy decision. Previously BMS had two options:
Each script ran in its own context.
All scripts ran in one context.
This was controlled by the enable_context_sharing() method on
ConfigureScriptPlugin. That function is now deprecated. Instead use the set_context_policy method:
Previously BMS used a ScriptId and sometimes an Entity to refer to a
context. If there was no entity then Entity::from_raw(0) was used. Instead BMS
now uses this ContextKey to look up contexts.
pub struct ContextKey {
/// Entity if there is one.
pub entity: Option<Entity>,
/// Script ID if there is one.
pub script_id: Option<Handle<ScriptAsset>>
}
This change affects the parameters for the context_pre_handling_initializers
#[derive(Clone, Debug)]
pub enum Recipients {
/// The event needs to be handled by all scripts, if multiple scripts share a context, the event will be sent once per script in the context.
AllScripts,
/// The event is to be handled by all unique contexts, i.e. if two scripts share the same context, the event will be sent only once per the context.
AllContexts,
/// The event is to be handled by a specific script-entity pair
ScriptEntity(ScriptId, Entity),
/// the event is to be handled by a specific static script
StaticScript(ScriptId),
}
This aligns with how scripts are associated with contexts better.
The language for recipients is now controlled through the language field of the ScriptCallbackEvent. This allows for more flexibility in how callbacks are handled, especially when multiple languages are involved.
If no language is specified, the callback will apply to all languages.
#[derive(Clone, Event, Debug)]
#[non_exhaustive]
pub struct ScriptCallbackEvent {
/// The label of the callback
pub label: CallbackLabel,
/// The recipients of the callback
pub recipients: Recipients,
/// The language of the callback, if unspecified will apply to all languages
+ pub language: Option<Language>
/// The arguments to the callback
pub args: Vec<ScriptValue>,
/// Whether the callback should emit a response event
pub trigger_response: bool,
}
script_id is replaced with script_asset which now has a type of Handle<ScriptAsset>. This means that you can no longer use a string to refer to a script, but rather you must use a handle.
Scripts can still access the asset path of the script using:
The concept of script attachments has been introduced to describe the idea of a script instance. Previously this was muddied due to the fact script ID’s were the primary way to refer to scripts. Now the instance of a script and its mapping to a context is clearer.
This is reflected in a brand new set of bindings:
-- will create backing ScriptAttachment instances, that can be used in other bindings.
local entity_script = ScriptAttachment.new_entity_script(entity, script_asset)
local static_script = ScriptAttachment.new_static_script(script_asset)
This release focuses on aligning bevy_mod_scripting with modern Bevy practices, most notably by switching to Handle<ScriptAsset> for script management. This change simplifies the API, removes boilerplate, and makes script handling more idiomatic.
Scripts are now treated as first-class Bevy assets. The old ScriptId (which was a string) has been replaced by AssetId<ScriptAsset>, and you’ll primarily interact with scripts via Handle<ScriptAsset>.
// New way
let handle: Handle<ScriptAsset> = asset_server.load("my_script.lua");
commands.spawn(ScriptComponent(vec![handle]));
Scripts are now only evaluated when they are attached to a ScriptComponent or added to StaticScripts, which means you have more control over when and how scripts are executed.
You now have much finer control over how script contexts (i.e., the environment a script runs in) are created. The old enable_context_sharing() has been replaced with set_context_policy() which accepts a ContextPolicy:
ContextPolicy::shared(): All scripts run in one global context
ContextPolicy::per_script(): Each script asset gets its own context (the old default.)
ContextPolicy::per_entity(): Each entity with scripts gets its own context.
ContextPolicy::per_entity_and_script(): A unique context for every script on every entity (the new default).
This means that each script is maximally isolated by default, but you can still opt for shared contexts if needed.
Recipients Enum: The Recipients enum for events has been redesigned to align with the new context policies, offering AllScripts and AllContexts variants, and removing some variants which don’t fit the new model. If you need the old behaviour, you can simply query the ECS first before sending events.
API Cleanup: Several types and traits were removed or simplified, including ScriptAssetSettings, AssetPathToScriptIdMapper, and ScriptMetadataStore, as they are no longer needed with the new asset-based approach.
Replace all usages of CallbackBuilder<P>.load and CallbackBuilder<P>.reload with P::load(..) and P::reload(..) or P::context_loader()(..) and P::context_reloader()(..)
If you were relying on BMS enabling some features in Bevy crates, your builds might be pulling in less features than you expect. Ensure you explicitly select all features you need from bevy. BMS will now only pull in strictly necessary features and crates from bevy
RuntimeSettings and RuntimeContainer are removed, replace usages with P::readonly_settings(world.id()).runtime) access to initializers is no longer possible outside the plugin setup phase
if you implemented a scripting plugin you will need to correct your function signatures for the above aliases, and fetch the config yourself to access callbacks via Plugin::readonly_configuration(world_id) using the world id you will now receive.
References previously resident in bevy_mod_scripting_core::asset/bevy_mod_scripting::core::asset will now need to come from bevy_mod_scripting_asset or bevy_mod_scripting::asset
Adding supported extensions will now need to be done via scripting_plugin.add_supported_extension("extension") rather than via the app directly
Plugin implementors will need to add the new crate as a dependency and should not directly rely on the top workspace crate
Update imports to new crates (bevy_mod_scripting_bindings, bevy_mod_scripting_display, bevy_mod_scripting_asset) where appropriate, if you rely on the workspace these will be re-exported under bindings, display and asset respectively
Replace DisplayWithWorld usage with DisplayWithTypeInfo / DebugWithTypeInfo and use WithTypeInfo::new(value) in format strings. Ensure that the WorldGuard is in scope when printing using these utilities, or you might see raw type id’s
Printing has been extracted into its own bevy_mod_scripting_display crate, and brand new traits including the reflectable DisplayWithTypeInfo were written.
There are now Debug and Display equivalent traits which receive type information from semi-static thread local objects in order to print things like TypeId’s with full information.
You can now also register ReflectDisplayWithTypeInfo against your types in the reflect registry, to customize printing.
Bindings have been split out into individual crates, for example bevy_reflect has a corresponding bevy_reflect_bms_bindings crate which contain’s its bindings. This is possible thanks to a brand new tool which allows us to calculate crate dependencies across a workspace during codegen (see bevy_mod_scripting_codegen)
At the same time more bindings are now being generated after fixing some bugs in type discovery.
Other refactorings include bevy_mod_scripting_bindings, bevy_mod_scripting_display and bevy_mod_scripting_asset being pulled out into their own crates.
This has significantly improved compile times allowing us to pack more bindings!
HandlerCtxt has been removed and all of its containing resources have been put in P::readonly_configuration, which is a semi-static, WorldId parameterised static.
At the same time ScriptContext<P> has been wrapped in an Arc allowing us to run callbacks within callbacks, as long as they don’t modify the same contexts!
BMS has historically pointed at the top level bevy crate, this was handy but fairly clunky as it brought way more dependencies than necessary.
From now on all BMS crates point at bevy subcrates directly, with default features disabled, this means we pull in the minimum amount of dependencies possible (in theory, as long as we didn’t miss a feature)
A new crate bevy_mod_scripting_script was created, to decouple some concerns within the workspace. References will now need to use the bevy_mod_scripting::script namespace or a direct crate reference.
On top of the ability to plug into mdbook docs, you can now generate Lua declaration files for use with the lua language server.
You can do this by simply adding the generation plugin to your app:
#![allow(unused)]
fn main() {
// this example is used to drive the generated docs on the official BMS book
app.add_plugins(BMSPlugin.set::<ScriptingFilesGenerationPlugin>(
ScriptingFilesGenerationPlugin::new(
true, // enabled, you can use a compilation feature to disable this here
PathBuf::from("assets").join("definitions"),
Some(PathBuf::from("bindings.lad.json")), // do also save the ladfile itself
"Core BMS framework bindings",
true,
true,
),
));
}
The definitions will be generated on startup!
While the generated files are not currently perfect, and not everything is yet supported, they should already make development much smoother with basic type hints and inline docs.
We have decided to drop using the native Bevy reflection paths, and switched them out with custom ReferencePath and ReferencePart structures.
This works around previous limitations of not being able to reflect and modify into dictionaries as well as reference into (but not modify sets, this one is due to limitations in the native rust type).
This should also give us some minor performance benefits.
You can now register callbacks dynamically from your scripts like so:
function on_script_loaded()
register_callback("on_test", dynamic_on_test)
end
function dynamic_on_test()
register_callback("on_test_last", dynamic_on_test_last)
return "on test: I am dynamically registered from a normal callback!"
end
This has many benefits, including allowing registering different callbacks from withing a shared global script context (where different functions are in scope depending on the last loaded script, but are predictable during loads), finally making those fully usable!
Many functions previously living directly in WorldAccessGuard impls, now need to be accessed via the WorldExtensions trait.
Functions previously known as with_exclusive_access etc, have been split into: with_world and with_world_mut, allowing read access to the world too in some circumstances.
ReflectAccessId was removed in favour of WorldAccessRange which can be created via Into from ComponentId’s.
The script loading pipeline became more idiomatic with the use of observers.
What you would do previously with TransitionListener implementations you can now do with observers registered against each machine state, for example, to react to fresh scripts being loaded you can do the following:
Callback commands now accept a post_callback function pointer which can be set to execute some logic right after the callback runs, to make working with observers and callbacks easier in general.
Contexts are now stored in a Context enum which also contains the “state” of the context. Contexts which are being processed by the pipeline will be marked as loading/reloading or unloading, allowing us to have more information when trying to run callbakcs against these.
Callbacks which are loading or unloading for example, will not have callbacks issued to them generally.
The ArgMeta trait now includes: variadic() -> bool which if true, allows an argument in a binding to capture all remaining arguments from the call (effectively only usable as the last argument).
This is implemented by the brand new VariadicTuple type, which is an untyped wrapper for a variable amount of non-homogenous values.
New variants in the TypedThrough and LAD file format have ben added to represent variadics, and lua definition files generated from these will support them.
Two bindings have been added: pack_args and unpack_args to allow script code to directly create/unpack tuple variants if necessary. However for languages like Lua, supporting multiple return values, ScriptValue::Tuple variants (which is what we produce to represent variadic returns), are converted to many return values naturally.
register_new_component takes a script.* namespace first:
- local Enemy = world.register_new_component("Enemy")
+ local Enemy = world.register_new_component("script.my_game", "Enemy")
Registering a name twice is still an error. If you worked around that for scripts which run more than once, you can now pass allow_existing as the third argument to get the existing registration:
local Enemy = world.register_new_component("script.my_game", "Enemy", true)
In Bevy’s component registry, script components are now named by their full path, i.e. script.my_game.Enemy.
Running a callback in a script context which is already running a callback (e.g. from an observer or a command flushed during the callback) now returns an error instead of deadlocking. Use queue_script_callback to run it once the context is free.
WorldAccessGuard::setup_cache_raw takes two new arguments, the AppScriptCallbackRunners and the AppScriptTypeCatalogue. Prefer setup_cache(world, attachment), which reads them from the world. Use CurrentScriptAttachment::empty() for guards not tied to a running script.
ScriptingSystemSet has a new variant, RunQueuedCallbacks.
The script function world.trigger_event (and WorldExtensions::trigger_event) applies all pending world commands before it returns, not only those of its observers.
Recipients moved to bevy_mod_scripting_script, and is re-exported from core::event. Resolving recipients is the GetRecipients trait.
AnyTypeRegistration has a fourth case, bundles (ScriptBundleRegistration).
ScriptComponentRegistration has a new public field, script_namespace.
LAD files have new fields: namespace on types, Fields payloads, variant function namespaces, and default metadata. Custom LAD backends may need to handle them.
Scripts can run each other’s callbacks, in any language:
run_script_callback runs a callback right away and returns its result.
queue_script_callback runs it as soon as the current callback finishes, or in Last when called from a non-exclusive script system (ScriptingSystemSet::RunQueuedCallbacks).
Nested callbacks are now safe: re-entering a context that is already running (a script calling itself, scripts sharing a context, or a loop A → B → A) returns an error instead of freezing the app, and nesting is capped at 64. This is built on AppScriptCallbackRunners, which lets language-agnostic code run callbacks in any scripting language.
Every reflected type gets a new constructor, and enum variants are values:
local v = Vec3.new{ x = 1, y = 2 } -- z is filled in from the default
local all = Recipients.AllScripts:new{} -- construct a variant
if event.recipients:variant() == Recipients.AllScripts then ... end
Missing fields are filled in from defaults, and unknown fields are an error. The global construct follows the same rules. The generated LAD files and Lua language server definitions describe constructor payloads and variants, so editors complete and check them. In Rhai, call Type.new_.call(...), since new is reserved.
world.trigger_event(type, value) and world.write_message(type, value) send reflected Bevy events and messages. trigger_event applies all pending commands, including those its observers queued, before it returns.
world.add_observer(event, callback, components?, entities?) runs a script callback when an event is triggered. That includes the component lifecycle events (AddEvent, InsertEvent, RemoveEvent, …), for native and script components alike. Observer callbacks never re-enter a busy script: they wait until it is free. Their errors are reported like those of other callbacks.
world.register_new_event(namespace, name) lets scripts define their own events, triggered with the same world.trigger_event(event, payload).
Types now live in namespaces: the crate that defines them, or bms for BMS’ own types. Override this with ScriptNamespace type data or #[script_bindings(namespace = "...")].
import_namespace("bevy_transform") returns the types in a namespace. It replaces the types global, which is now deprecated.
Scripts register components and events into script.* namespaces, and other scripts import them from there.
export_namespace(namespace, name, callback) shares a script function with other scripts, in any language.
A script’s exports and observers are removed when it unloads or reloads.
LAD files record each type’s namespace, and the Lua language server types import_namespace per namespace.
Types are indexed once in a shared, lazily rebuilt type catalogue, instead of once per script context. See Namespaces.
Inserting a script component no longer leaks memory.
register_new_component and register_new_event take an allow_existing argument, which returns the existing registration instead of failing when the name is taken (useful for scripts which run again).
world.insert_bundle and world.spawn_bundle insert bundles registered with #[reflect(Bundle)], all at once.
The only globals left are import_namespace, export_namespace and register_callback. Everything else, world included, is imported from a namespace, see Namespaces:
+ local world = import_namespace("bms").world
world.spawn()
Types (Vec3, Transform, and the deprecated types table) are imported from their namespace. Bevy types are grouped by category (math, ecs, transform, ui, …), BMS’ types live in bms:
- local t = Transform.from_translation(Vec3.new(1, 2, 3))
+ local Vec3 = import_namespace("math").Vec3
+ local Transform = import_namespace("transform").Transform
+ local t = Transform.from_translation(Vec3.new(1, 2, 3))
BMS’ functions moved to bms: construct, system_builder, run_script_callback, queue_script_callback, pack_args, unpack_args and log_*. entity and script_asset are now functions:
Query results are plain lists of the entity followed by the components, result:entity() and result:components() are removed:
for _, result in pairs(world.query():component(Transform):build()) do
- local entity, transform = result:entity(), result:components()[1]
+ local entity, transform = result[1], result[2]
end
Other changes:
world.get_type_by_name only takes full paths, i.e. "bevy_transform::components::transform::Transform" or "script.my_game.Enemy".
Numbers work on the left of operators: 2 * v, 1 - v (dispatched to mul, rsub, …), in Rhai too.
Script globals are removed (AppScriptGlobalsRegistry, #[script_globals], register_static*, GlobalNamespace, FunctionScope::Global, DummyScriptFunctionRegistry, use_dummy_registry, …). Put functions in a namespace instead:
Values go in AppScriptNamespaceMembers::register_value, made each time the namespace is imported.
Types with bindings from the bevy crates are in category namespaces. Move a type with app.override_script_namespace.
Registering a function name twice replaces it, there are no overloads (overwrite*, iter_overloads, remove_all_overloads are removed). Take a Union or ScriptValue for arguments of different types.
#[derive(ScriptType)] implements everything a type needs to be used directly in function signatures (FromScript, IntoScript, ArgMeta, GetTypeDependencies, TypedThrough). Use #[script_type(from = custom)] / (into = custom) for a hand written conversion.
Functions on StaticTypeRef take StaticTypeRef instead of V<StaticTypeRef>.
ScriptFunctionRegistry is keyed by TypeId instead of FunctionScope: register(type_id, ..), get_function(type_id, name), iter_namespace → iter_type.
Values allocated for scripts are owned by the references to them and freed with the last one, the allocator is removed (ReflectAllocator, AppReflectAllocator, WorldExtensions::allocator, AllocatorDiagnosticPlugin, ScriptingSystemSet::GarbageCollection, InteropError::GarbageCollectedAllocation). Drop the allocator argument:
// before
let reference = ReflectReference::new_allocated(value, &mut world.allocator().write());
// after
let reference = ReflectReference::new_allocated(value);
new_allocated_boxed and new_allocated_boxed_parial_reflect change the same way.
ScriptLoadingPipeline::time_budget: Option<Duration> is replaced by tick_budget: Option<usize>, the maximum amount of pipeline steps per frame, unlimited by default. ActiveMachines::budget and RunProcessingPipelineOnce::new take the same step count.
ScriptAttachedMessage(a) / ScriptDetachedMessage(a) are one message, ScriptLifecycleMessage::Attached(a) / ::Detached(a), so attachments and detachments are processed in the order they were written. GetScriptHandle::get_script_handle returns an Option (None: the event doesn’t wait for its script to load) and ScriptLifecycleQueue::get_ready, formerly known as LoadedWithHandles yields (event, Option<(StrongScriptHandle, Language)>).
An attachment whose script is still loading holds back the attachments and detachments after it, and callbacks reach scripts in a deterministic order (attachment order, until scripts are removed), see Determinism. Lua’s math.random is seeded with 0 in new contexts.
Script systems are identified by attachment and callback label (adding one again replaces it) and are removed with their script. ScriptSystemSet holds a recycled slot instead of the callback label, ScriptSystemBuilder no longer implements IntoSystem (use ScriptSystemBuilder::build), and their errors are sent as ScriptErrorMessages instead of being logged. WorldAccessGuard::new_non_exclusive takes an Option<Arc<ArchetypeFilter>>, restricting component accesses to the given archetypes (None for no restriction).
Script system parameters are declared to Bevy as they’re accessed: resource and query components as writes (they were declared as reads, or as accessing nothing for resources, while scripts could write them), resource_read / component_read as reads. Script systems writing the same resources or components no longer run in parallel. ScriptQueryBuilder::as_query_state takes a QueryAccess: Declared declares the written components as written (for queries reported to the scheduler), MatchOnly declares all components as read (i.e. for EntityRef queries only finding entities).
Script systems are put in and ordered against system set values (ReflectSystemSet) instead of names: scripts register sets with world.register_new_system_set(namespace, name), Rust sets are registered into a namespace with app.register_script_system_set(namespace, name, set) (ScriptSystemSetAppExt, now in bevy_mod_scripting_bindings), both are found with world.get_system_set_by_name. ScriptSystemBuilder::{in_set, before_set, after_set} take a SystemSet. ReflectSystemSet is opaque, and only its set nodes of a schedule graph have a node id.
ScriptQueryResult converts to a list instead of a reference, its script functions are removed.
ScriptQueryResult::components is a Vec<Option<ReflectReference>>, None for absent optional components. ArchetypeFilter::allow / is_allowed and WorldAccessGuard::check_archetype_access take whether the component is written.
AccessByteSet::from_allowed_lists(read_write, read_only) creates subsets with read only accesses. At most 63 accesses to the same value can be claimed at once, claims beyond fail (the count used to overflow).
WorldAccessRange::External holds the value’s owner (AccessOwner) instead of an id, and the access map keeps it alive until the claim is released. Build it from the value ((&allocation_id).into()); WorldAccessRange is no longer Copy.
Dispatch every script operation through bevy_mod_scripting_bindings::dispatch (index, set_index, static_index, binary, unary, len, iter, display, debug), see Necessary Features. WorldExtensions::lookup_function, lookup_instance_function, lookup_type_function and lookup_reserved_function are private.
Convert the new ScriptValue::Type(TypeId), a type itself (what world and StaticTypeRef are).
Set bindings::builtins::builtins() (import_namespace, export_namespace) and your register_callback as globals, nothing else. Don’t set world, entity or script_asset anymore.
LuaStaticReflectReference / RhaiStaticReflectReference are LuaTypeRef / RhaiTypeRef.
The Lua language server files are split per namespace (bindings.d.lua, bindings.<namespace>.d.lua, point workspace.library at the directory), classes are named after their namespace (math.Vec3), and generic types are declared once (Handle<Image>, needs LuaLS 3.17+).
cargo xtask docgen is replaced by cargo xtask docs (all binding features, --test-bindings for the script test functions). cargo xtask bless-lad updates the LAD test fixtures.
ThroughTypeInfo is flat (ThroughTypeInfo::Vec(..), ThroughTypeInfo::Ref(type_info)), ThroughTypeInfo::TypeInfo is Opaque. #[derive(TypedThrough)] documents a type as opaque, #[typed_through(value)] as a value.
LAD: LadFieldOrVariableKind → LadTypeKind; globals is removed, builtin functions are LadFunctionNamespace::Global (the mdbook “Globals” and “Functions” pages are one “Builtins” page), namespaces have values (add_instance* → add_namespace_value_of / add_namespace_instance); new namespaces, generic_types, LadTypeDefinition::generic_type, LadFunction::receiver, LadFunctionMetadata::operator (replaces is_operator); arguments are always named and never include the call context; polymorphizied_types is removed. ArgumentVisitor has an Output type and visit_* methods.
This part of the book covers the user-facing API of the scripting languages supported by BMS. This will be where you will want to forward your script users to get started with scripting in BMS.
If you are a modder, welcome! 👋, apologies for the rust-centricity of this guide, we are working on it!
register_callback(label, function): registers a function as a callback handler
Everything else is imported. world is a static reference to the world, with all sorts of functions available: import_namespace("bms").world. BMS’ own functions are in bms, i.e. bms.current_entity() (the entity the script is attached to, or nil) and bms.current_script() (the script’s handle):
local bms = import_namespace("bms")
print(bms.current_entity())
When interfacing with bevy, we do this via reflection.
While the bevy bindings do not cover constructors for every single type that bevy or other libraries provide, reflection allows us to construct some (not all types implement FromReflect) types from dynamic structs.
BMS exposes this ability to all script writers via the new function available on every type, as well as the more verbose bms.construct.
Every type has a new function, taking a table of the fields to set. Types are reached through their namespace, i.e. import_namespace("my_game").MyStruct:
local MyStruct = import_namespace("my_game").MyStruct
local my_struct = MyStruct.new({
my_field = "hello",
other_field = 5,
})
pub struct MyTupleStruct(pub String);
Tuple structs and tuples are constructed using the index of the field as the key, the keys follow the indexing convention of the language, i.e. in Lua the first field is "1":
local my_tuple_struct = MyTupleStruct.new({
["1"] = "hello",
})
A list is taken to be the fields in order, which is usually the more natural way to write tuple structs:
local my_tuple_struct = MyTupleStruct.new({ "hello" })
Types which are not structs, tuple structs, tuples or enums, can be constructed only if they implement Default, using Type.new({}).
Fields which are not provided are filled in, in the following order of priority:
the value from the type’s Default implementation, if it has one. For enums, only if the default is the variant being constructed
the Default of the field’s type, if it has one
If neither is available the construction fails, and the error names the missing field.
Entries which are not fields of the type, i.e. a misspelled field name, are an error too, rather than being ignored in favour of a default.
The generated Lua language server files mark exactly these fields as optional, see Editor support.
new is a single function shared by all types, and is made available on every type.
A function with the same name registered on the type itself always takes priority, i.e. Vec3.new(x, y, z) is the Vec3 constructor from the math bindings, not the shared one.
For such types use bms.construct, which works for every type.
The name of the function is configurable via the ConstructorsPlugin:
The variant a value is in can be obtained with :variant(), and compared with the variants of the enum:
if my_enum:variant() == MyEnum.VariantA then
print(my_enum.field)
end
This is also useful with enums that carry no data, such as keys:
local KeyCode = import_namespace("input").KeyCode
if event.key_code:variant() == KeyCode.KeyA then
print("A was pressed")
end
:variant() returns nil if the value is not an enum. If you need the name of the variant, :variant_name() is also available on both values and variants.
Functions on the type take priority over variants, so a variant named like a function cannot be accessed with MyEnum.Name.
Use world.get_enum_variant to get at it in that case:
local world = import_namespace("bms").world
local variant = world.get_enum_variant(import_namespace("my_game").MyEnum, "Name")
The bms.construct function constructs types from a registration.
It works for every type, including those whose own new function shadows the shared one, and is useful if you only have the type as a value:
local bms = import_namespace("bms")
local my_game = import_namespace("my_game") -- the crate defining the types
local concrete_my_struct = bms.construct(my_game.MyStruct, {
my_field = "hello"
})
local variantA = bms.construct(my_game.MyEnum, {
variant = "VariantA",
field = "hello"
})
local vec = bms.construct(import_namespace("math").Vec3, { x = 1, y = 2, z = 3 })
Missing fields are filled in from defaults in the same way as for new.
The Lua language server files describe the table each constructor takes, so editors can complete and check the fields.
A field is optional exactly when a default can fill it in, as described in Defaults:
---@class (exact) MyStruct.Fields
---@field my_field? string
---@field handle MyHandle
---@field [any] 'never'
---@param payload MyStruct.Fields
---@return MyStruct
function MyStruct.new(payload) end
The language server then warns about missing required fields, fields of the wrong type, and unknown fields, i.e. a misspelled my_feild.
The last line rejects unknown fields: until the language server checks exact classes against tables passed to functions (LuaLS/lua-language-server#1990), any undeclared field has to be of a type no value has, so the warning reads “Cannot assign string to 'never'”.
Tuple fields are declared under their integer keys, i.e. [1], which a list such as { "hello" } fills in, so prefer lists for tuple structs.
Types and variants without fields all take NoFields, i.e. {}.
Each enum variant has a class of its own, i.e. MyEnum.VariantA, so MyEnum.VariantA:new{ completes the fields of VariantA and returns a MyEnum.
The table of MyEnum.new{ variant = "VariantA", ... } is not described, as the language server cannot narrow its fields down by the variant given.
Prefer MyEnum.VariantA:new{...}, which is checked.
Since the type of bms.construct’s first argument is only known when the script runs, its table is not described.
Scripts can send Bevy events (observer-style, run immediately) and messages (buffered, read later by MessageReaders) using the reflection data Bevy generates for them.
Function
Rust type data required
Behaviour
world.trigger_event(type, value)
ReflectEvent
Triggers the event. Observers run, and all pending commands are applied, before the call returns. Also triggers script events.
world.write_message(type, value)
ReflectMessage
Writes the message to its Messages<T> buffer, readers see it later in the frame.
local world = import_namespace("bms").world
local my_game = import_namespace("my_game") -- the crate defining the types
world.trigger_event(my_game.MyEvent, my_game.MyEvent.new({ amount = 3 }))
world.write_message(my_game.MyMessage, my_game.MyMessage.new({ amount = 3 }))
The value is copied when sent, so the same value can be sent again, and changing it afterwards does not affect events already sent.
Only types which are registered with the matching reflect attribute can be sent, anything else errors with a missing type data error:
#[derive(Event, Reflect)]
#[reflect(Event)]
struct MyEvent { amount: usize }
#[derive(Message, Reflect)]
#[reflect(Message)]
struct MyMessage { amount: usize }
app.register_type::<MyEvent>()
.register_type::<MyMessage>()
// messages must also be added as usual, otherwise there is nothing to write them to
.add_message::<MyMessage>();
The type must be registered with register_type, otherwise scripts cannot import it.
#[reflect(Event)] is only generated for events using the default (global) trigger. Events with custom triggers, such as entity events, need a hand-written ReflectEvent.
Types which do not implement FromReflect (all fields must be reflectable) cannot be sent.
Bevy’s own events and messages are not usable unless they have the reflect attributes.
trigger_event needs exclusive access to the world, so it fails with an access error if the script is currently holding a borrowed reference elsewhere in the same call (e.g. inside a query iteration or with_* access). Trigger events from regular callbacks.
Observers run before trigger_event returns, and so do the commands they queue (as for other operations such as spawning). A Rust observer or command which runs a callback in the currently executing script context fails with an error. Use bms.run_script_callback or bms.queue_script_callback instead (see Calling Other Scripts).
world.add_observer(event, callback, components?, entities?) runs the calling script’s callback callback (registered with register_callback, or a top level function of that name) with a copy of the event, whenever the event is triggered. It returns the observer entity: despawn it to remove the observer.
local world = import_namespace("bms").world
local ecs = import_namespace("ecs")
local Mana = world.register_new_component("script.my_game", "Mana")
register_callback("on_mana_added", function(event)
print("Mana was added to", event.entity)
end)
local observer = world.add_observer(ecs.AddEvent, "on_mana_added", { Mana })
world.despawn(observer) -- later
The lifecycle events AddEvent, InsertEvent, DiscardEvent, RemoveEvent and DespawnEvent (in ecs) work for any native or script component. Other events need #[reflect(Event)].
As in Bevy, components is an OR filter, and entities restricts the observer to those entities.
Callbacks run right after the triggering operation, in unspecified order. If the script’s context is busy (e.g. it triggered the event), the callback runs as soon as the busy callback finishes, so observers never re-enter a running script.
The world is already past the change: removed component data can’t be read in RemoveEvent callbacks.
Errors are reported like those of other callbacks (ScriptErrorMessage), and don’t fail the triggering operation.
The following BMS messages are reflected and can be written from scripts:
ScriptCallbackMessage: runs a callback on other scripts, processed later in the frame by the handler systems set up for its callback label. Labels without a handler system are never processed, prefer bms.queue_script_callback to run callbacks from scripts.
ScriptLifecycleMessage (Attached / Detached), ScriptAssetModifiedMessage: drive the script loading pipeline
The internal loading pipeline state events (LoadingCompleted, ContextAssigned, …) are intentionally not reflected.
local world = import_namespace("bms").world
local EnemyBundle = import_namespace("my_game").EnemyBundle
world.spawn_bundle(EnemyBundle, EnemyBundle.new({ health = Health.new({ hp = 10 }) }))
Every type known to scripts lives in a namespace, and scripts reach types through their namespace: there are no globals named after types.
Types
Namespace
bevy types covered by the bevy bindings, grouped by category
math, ecs, time, transform, color, input, window, asset, camera, render, sprite, text, ui, gizmos, animation, audio, state
BMS’ own types (ScriptAttachment, ReflectReference, …)
bms
any other type
the name of the crate defining it, i.e. my_game
primitives like i32
the root namespace, ""
A category namespace holds every type of the bevy crates it covers, i.e. math holds the types of glam and bevy_math, render those of bevy_render, bevy_pbr, bevy_light, bevy_mesh and bevy_image.
Type:namespace() returns a type’s namespace. Script registered components and events live in script.* namespaces, see below.
import_namespace(namespace) returns a table of the namespace’s types (and, for script namespaces, events and exported functions) by short name. Each type can be used to call its static functions, to access its enum variants, and wherever a type is expected:
local bms = import_namespace("bms")
local world = bms.world
local Vec3 = import_namespace("math").Vec3
local transform = import_namespace("transform")
local t = transform.Transform.from_translation(Vec3.new(0, 1, 0))
world.add_default_component(bms.current_entity(), transform.Transform)
Import once at the top of a script, and keep the types you use in locals. In Lua, don’t name the table of the math namespace math: that would hide Lua’s own math library.
If two types in a namespace share a short name, the first one registered wins.
Each call reads the shared type catalogue, so the result is always up to date. A table imported earlier does not pick up later registrations: import again.
A namespace which does not exist is an error, with suggestions: namespace "transfrom" does not exist. ... Did you mean: transform?
To also expose every type as a global named after it (i.e. Vec3), as before 0.24, set CoreScriptGlobalsPlugin::register_static_references. Types sharing a name then shadow each other.
// on types with script bindings: keeps a namespace set before (with a warning)
#[script_bindings(namespace = "my_namespace")]
impl MyType { /* ... */ }
// on any type: always wins, before or after `script_bindings`
app.override_script_namespace::<MyType>("my_namespace"); // bevy_mod_scripting::bindings::ScriptNamespaceAppExt
// or directly
registry.register_type_data_with::<MyType, ScriptNamespace, _>("my_namespace");
Scripts register components and events into namespaces they pass explicitly. These start with script., followed by dot-separated segments of letters, digits and underscores:
local bms = import_namespace("bms")
local world = bms.world
local Enemy = world.register_new_component("script.game.ai", "Enemy")
local Hit = world.register_new_event("script.game.ai", "Hit")
-- anywhere, including other scripts
local ai = import_namespace("script.game.ai")
world.add_default_component(bms.current_entity(), ai.Enemy)
world.trigger_event(ai.Hit, { damage = 3 })
Names are unique within a namespace, across components, events and exports: script.game.ai.Enemy and script.game.ui.Enemy are different components.
Registering a taken name is an error. allow_existing returns the existing registration instead, which is useful for scripts which run again.
A script namespace exists once something is registered or exported into it.
Components and events stay registered when their script unloads, as Bevy cannot unregister components.
Values of script components are DynamicComponents, holding any value in data. Tables read from data are copies: assign a new table to change them.
local bms = import_namespace("bms")
local world = bms.world
local DynamicComponent = bms.DynamicComponent
world.insert_component(bms.current_entity(), Enemy, DynamicComponent.new({ data = { hp = 5 } }))
local enemy = world.get_component(bms.current_entity(), Enemy)
enemy.data = { hp = enemy.data.hp - 1 } -- `enemy.data.hp = 4` would not write through
export_namespace(namespace, name, callback) makes a callback of the calling script callable by other scripts, in any language:
-- script A
local world = import_namespace("bms").world
register_callback("spawn_enemy_impl", function(health)
return world.spawn()
end)
export_namespace("script.game", "spawn_enemy", "spawn_enemy_impl")
-- script B, once script A has run
local enemy = import_namespace("script.game").spawn_enemy(5)
callback is a function registered with register_callback, or a top level function of that name. Only functions can be exported.
Calls run in the exporting script’s context, like bms.run_script_callback (see Calling Other Scripts), and return the callback’s result.
A script cannot call its own exports, or those of scripts sharing its context, from one of its callbacks: its context is busy. Call the function directly instead.
Exports are looked up on every call, so replaced exports are picked up and removed ones fail with an error.
A path can only be exported by one script. Exporting it again from the same script replaces it. Exports can’t share a path with a component.
export_namespace only works inside a script callback.
When a script unloads or is about to reload, its exports and observers are removed right after its on_script_unloaded callback. Export from a callback which runs on load (e.g. on_script_loaded), so a reloaded script exports again.
LAD files record each type’s namespace. The Lua language server definitions declare a namespace.<name> class per native namespace, and import_namespace("transform") returns the matching class, so completion works on imported tables. Script namespaces only exist at runtime and are untyped.
Scripts can run callbacks of other scripts, in any scripting language, using two functions:
Function
When the callback runs
Returns
bms.run_script_callback(label, target, ...args)
Immediately, before the call returns
What the callback returned
bms.queue_script_callback(label, target, ...args)
As soon as the current callback finishes, in the same frame
Nothing
local bms = import_namespace("bms")
local ScriptAttachment, Recipients = bms.ScriptAttachment, bms.Recipients
local enemy_script = ScriptAttachment.new_entity_script(enemy, bms.current_script())
-- runs `take_damage(10, "fire")` in the enemy's script right away
local remaining_health = bms.run_script_callback("take_damage", enemy_script, 10, "fire")
-- runs `on_hit()` in every script once this callback is done
bms.queue_script_callback("on_hit", Recipients.all_scripts())
let bms = import_namespace("bms");
let ScriptAttachment = bms.ScriptAttachment;
let Recipients = bms.Recipients;
let enemy_script = ScriptAttachment.new_entity_script(enemy, bms.current_script());
// runs `take_damage(10, "fire")` in the enemy's script right away
let remaining_health = bms.run_script_callback("take_damage", enemy_script, 10, "fire");
// runs `on_hit()` in every script once this callback is done
bms.queue_script_callback("on_hit", Recipients.all_scripts());
Recipients.all_scripts(): every loaded script, once per script even if scripts share a context
Recipients.all_contexts(): every loaded context once
Recipients.static_script(script)
Recipients.entity_script(entity, script)
For a single script, bms.run_script_callback returns the callback’s value. For recipients it returns a list with the value of each script which ran the callback.
Scripts which are still loading or unloading are skipped when targeting many scripts, while targeting a specific script which is not loaded is an error.
bms.run_script_callback raises the callback’s error in the calling script, so it can be handled like any other error. For recipients, the first error is raised after all scripts ran the callback.
Errors of queued callbacks cannot reach the script which queued them, they are reported like other script errors (logged by default).
While a callback runs, its script context is busy, so bms.run_script_callback cannot run a callback in a busy context. This includes:
the calling script itself (call the function directly instead),
scripts sharing a context with the calling script,
scripts further up the current chain of callbacks, e.g. A calls B, which calls A again.
These calls return an error instead. bms.queue_script_callback works for all of them, since queued callbacks only run once the current callback has finished.
Whether scripts share a context depends on the context policy of the scripting plugin:
Queued callbacks run in the order they were queued, and can queue further callbacks, which run in the same go.
They never run in the middle of the callback which queued them, even if it keeps using the world afterwards.
To stop callbacks from queueing each other endlessly, at most 1024 queued callbacks run in one go. Any beyond that run later, and an error is reported.
Script systems running in parallel with other systems (i.e. not marked exclusive) cannot use bms.run_script_callback, as another system might be running the target script at the same time.
Callbacks they queue run at the end of the frame instead, in the ScriptingSystemSet::RunQueuedCallbacks system set in the Last schedule.
Exports a callback of the calling script as a function in a script namespace, so that any script (in any language) can call it.
The callback is either a function registered with register_callback, or a top level function with that name.
Calls run in the exporting script and return what the callback returns.
register_callback("spawn_enemy_impl", function(health) ... end)
export_namespace("script.game", "spawn_enemy", "spawn_enemy_impl")
-- in another script, once the first one has run
import_namespace("script.game").spawn_enemy(5)
Imports a namespace, returning a table of the types, functions, values, events, system sets and exports in it by name.
Bevy types with bindings live in a namespace per category (math, ecs, transform, ui, …), BMS’ own types and functions in bms,
any other type in the namespace named after the crate defining it. Namespaces defined by scripts start with script..
local bms = import_namespace("bms")
local Vec3 = import_namespace("math").Vec3
bms.world.spawn()
Fails with a list of similar namespaces if the namespace does not exist.
Constructs a new instance of the type from a table of its fields.
Missing fields are filled in from the type’s default, then from the defaults of the fields’ types.
For enums use the variant key, or construct from a variant directly.
An animation that an [`AnimationPlayer`] is currently either playing or was playing, but is presently paused. A stopped animation is considered no longer active.
Constructs a new instance of the type from a table of its fields.
Missing fields are filled in from the type’s default, then from the defaults of the fields’ types.
For enums use the variant key, or construct from a variant directly.
A graph structure that describes how animation clips are to be blended
together.
Applications frequently want to be able to play multiple animations at once
and to fine-tune the influence that animations have on a skinned mesh. Bevy
uses an animation graph to store this information. Animation graphs are a
directed acyclic graph (DAG) that describes how animations are to be
weighted and combined together. Every frame, Bevy evaluates the graph from
the root and blends the animations together in a bottom-up fashion to
produce the final pose.
There are three types of nodes: blend nodes, add nodes, and clip
nodes, all of which can have an associated weight. Blend nodes and add
nodes have no associated animation clip and combine the animations of their
children according to those children’s weights. Clip nodes specify an
animation clip to play. When a graph is created, it starts with only a
single blend node, the root node.
In this case, assuming that Idle, Run, and Walk are all playing with weight
1.0, the Run and Walk animations will be equally blended together, then
their weights will be halved and finally blended with the Idle animation.
Thus the weight of Run and Walk are effectively half of the weight of Idle.
Nodes can optionally have a mask, a bitfield that restricts the set of
animation targets that the node and its descendants affect. Each bit in the
mask corresponds to a mask group, which is a set of animation targets
(bones). An animation target can belong to any number of mask groups within
the context of an animation graph.
When the appropriate bit is set in a node’s mask, neither the node nor its
descendants will animate any animation targets belonging to that mask group.
That is, setting a mask bit to 1 disables the animation targets in that
group. If an animation target belongs to multiple mask groups, masking any
one of the mask groups that it belongs to will mask that animation target.
(Thus an animation target will only be animated if all of its mask groups
are unmasked.)
A common use of masks is to allow characters to hold objects. For this, the
typical workflow is to assign each character’s hand to a mask group. Then,
when the character picks up an object, the application masks out the hand
that the object is held in for the character’s animation set, then positions
the hand’s digits as necessary to grasp the object. The character’s
animations will continue to play but will not affect the hand, which will
continue to be depicted as holding the object.
Animation graphs are assets and can be serialized to and loaded from RON
files. Canonically, such files have an .animgraph.ron extension.
The animation graph implements RFC 51. See that document for more
information.
Constructs a new instance of the type from a table of its fields.
Missing fields are filled in from the type’s default, then from the defaults of the fields’ types.
For enums use the variant key, or construct from a variant directly.
A node index from its number. Scripts see this type as `NodeIndex`. ```multi-lang-example,lua local NodeIndex = import_namespace(“animation”).NodeIndex local node = NodeIndex.new(3) assert(node:index() == 3) ``` ```multi-lang-example,rune let NodeIndex = import_...
Constructs a new instance of the type from a table of its fields.
Missing fields are filled in from the type’s default, then from the defaults of the fields’ types.
For enums use the variant key, or construct from a variant directly.
A component that identifies which parts of an [AnimationClip] asset can
be applied to an entity. Typically used alongside the
[AnimatedBy] component.
AnimationTargetId is implemented as a UUID. When importing an armature
or an animation clip, asset loaders typically use the full path name from
the armature to the bone to generate these UUIDs. The ID is unique to the
full path name and based only on the names. So, for example, any imported
armature with a bone at the root named Hips will assign the same
[AnimationTargetId] to its root bone. Likewise, any imported animation
clip that animates a root bone named Hips will reference the same
[AnimationTargetId]. Any animation is playable on any armature as long as
the bone names match, which allows for easy animation retargeting.
Note that asset loaders generally use the full path name to generate the
[AnimationTargetId]. Thus a bone named Chest directly connected to a
bone named Hips will have a different ID from a bone named Chest that’s
connected to a bone named Stomach.
Constructs a new instance of the type from a table of its fields.
Missing fields are filled in from the type’s default, then from the defaults of the fields’ types.
For enums use the variant key, or construct from a variant directly.
Constructs a new instance of the type from a table of its fields.
Missing fields are filled in from the type’s default, then from the defaults of the fields’ types.
For enums use the variant key, or construct from a variant directly.
A unique runtime-only identifier for an [`Asset`]. This is cheap to [`Copy`]/[`Clone`] and is not directly tied to the lifetime of the Asset. This means it _can_ point to an [`Asset`]...
A generational runtime-only identifier for a specific [Asset] stored in [Assets]. This is optimized for efficient runtime
usage and is not suitable for identifying assets across app runs.
Constructs a new instance of the type from a table of its fields.
Missing fields are filled in from the type’s default, then from the defaults of the fields’ types.
For enums use the variant key, or construct from a variant directly.
Represents a path to an asset in a “virtual filesystem”.
Asset paths consist of three main parts:
[AssetPath::source]: The name of the AssetSource to load the asset from.
This is optional. If one is not set the default source will be used (which is the assets folder by default).
[AssetPath::path]: The “virtual filesystem path” pointing to an asset source file.
[AssetPath::label]: An optional “named sub asset”. When assets are loaded, they are
allowed to load “sub assets” of any type, which are identified by a named “label”.
Asset paths are generally constructed (and visualized) as strings:
# use bevy_asset::{Asset, AssetServer, Handle};
# use bevy_reflect::TypePath;
#
# #[derive(Asset, TypePath, Default)]
# struct Mesh;
#
# #[derive(Asset, TypePath, Default)]
# struct Scene;
#
# let asset_server: AssetServer = panic!();
// This loads the `my_scene.scn` base asset from the default asset source.
let scene: Handle<Scene> = asset_server.load("my_scene.scn");
// This loads the `PlayerMesh` labeled asset from the `my_scene.scn` base asset in the default asset source.
let mesh: Handle<Mesh> = asset_server.load("my_scene.scn#PlayerMesh");
// This loads the `my_scene.scn` base asset from a custom 'remote' asset source.
let scene: Handle<Scene> = asset_server.load("remote://my_scene.scn");
[AssetPath] implements [From] for &'static str, &'static Path, and &'a String,
which allows us to optimize the static cases.
This means that the common case of asset_server.load("my_scene.scn") when it creates and
clones internal owned AssetPaths.
This also means that you should use [AssetPath::parse] in cases where &str is the explicit type.
Constructs a new instance of the type from a table of its fields.
Missing fields are filled in from the type’s default, then from the defaults of the fields’ types.
For enums use the variant key, or construct from a variant directly.
If an asset is set to the RENDER_WORLD but not the MAIN_WORLD, the asset data (pixel data,
mesh vertex data, etc) will be removed from the cpu-side asset once it’s been extracted and prepared
in the render world. The asset will remain in the assets collection, but with only metadata.
Unloading the asset data saves on memory, as for most cases it is no longer necessary to keep
it in RAM once it’s been uploaded to the GPU’s VRAM. However, this means you cannot access the
asset data from the CPU (via the Assets<T> resource) once unloaded (without re-loading it).
If you never need access to the asset from the CPU past the first frame it’s loaded on,
or only need very infrequent access, then set this to RENDER_WORLD. Otherwise, set this to
RENDER_WORLD | MAIN_WORLD.
If you have an asset that doesn’t actually need to end up in the render world, like an Image
that will be decoded into another Image asset, use MAIN_WORLD only.
On Wasm, it is not possible for now to free reserved memory. To control memory usage, load assets
in sequence and unload one before loading the next. See this
discussion about memory management for more
details.
Constructs a new instance of the type from a table of its fields.
Missing fields are filled in from the type’s default, then from the defaults of the fields’ types.
For enums use the variant key, or construct from a variant directly.
Constructs a new instance of the type from a table of its fields.
Missing fields are filled in from the type’s default, then from the defaults of the fields’ types.
For enums use the variant key, or construct from a variant directly.
Constructs a new instance of the type from a table of its fields.
Missing fields are filled in from the type’s default, then from the defaults of the fields’ types.
For enums use the variant key, or construct from a variant directly.
Constructs a new instance of the type from a table of its fields.
Missing fields are filled in from the type’s default, then from the defaults of the fields’ types.
For enums use the variant key, or construct from a variant directly.
Constructs a new instance of the type from a table of its fields.
Missing fields are filled in from the type’s default, then from the defaults of the fields’ types.
For enums use the variant key, or construct from a variant directly.
Constructs a new instance of the type from a table of its fields.
Missing fields are filled in from the type’s default, then from the defaults of the fields’ types.
For enums use the variant key, or construct from a variant directly.
Constructs a new instance of the type from a table of its fields.
Missing fields are filled in from the type’s default, then from the defaults of the fields’ types.
For enums use the variant key, or construct from a variant directly.
Constructs a new instance of the type from a table of its fields.
Missing fields are filled in from the type’s default, then from the defaults of the fields’ types.
For enums use the variant key, or construct from a variant directly.
When a type with a [Handle<T>] field derives [FromTemplate], that field is replaced by its
template type, [HandleTemplate<T>], when created via BSN.
We can see that [HandleTemplate<T>] has the following trait impl block:
[AssetPath<'static>] implements [From<&'static str>].
Because of that, assigning a string literal to a Handle<T> field automatically converts it into
[HandleTemplate<T>::Path] with that asset path when used in the bsn! macro.
Calls to bsn! automatically insert .into() conversions, and due to Rust’s blanket impl that turns [From] trait impls into their [Into]
equivalents, the conversion from &'static str to AssetPath<'static> is handled automatically.
Finally, the [HandleTemplate<T>::Path] generated gets converted to a [Handle<T>] during scene initialization,
as the asset is loaded from the given path, and the resulting handle is assigned to the field,
pointing to the asset that was found at the file path in our original string.
Constructs a new instance of the type from a table of its fields.
Missing fields are filled in from the type’s default, then from the defaults of the fields’ types.
For enums use the variant key, or construct from a variant directly.
When a type with a [Handle<T>] field derives [FromTemplate], that field is replaced by its
template type, [HandleTemplate<T>], when created via BSN.
We can see that [HandleTemplate<T>] has the following trait impl block:
[AssetPath<'static>] implements [From<&'static str>].
Because of that, assigning a string literal to a Handle<T> field automatically converts it into
[HandleTemplate<T>::Path] with that asset path when used in the bsn! macro.
Calls to bsn! automatically insert .into() conversions, and due to Rust’s blanket impl that turns [From] trait impls into their [Into]
equivalents, the conversion from &'static str to AssetPath<'static> is handled automatically.
Finally, the [HandleTemplate<T>::Path] generated gets converted to a [Handle<T>] during scene initialization,
as the asset is loaded from the given path, and the resulting handle is assigned to the field,
pointing to the asset that was found at the file path in our original string.
Constructs a new instance of the type from a table of its fields.
Missing fields are filled in from the type’s default, then from the defaults of the fields’ types.
For enums use the variant key, or construct from a variant directly.
When a type with a [Handle<T>] field derives [FromTemplate], that field is replaced by its
template type, [HandleTemplate<T>], when created via BSN.
We can see that [HandleTemplate<T>] has the following trait impl block:
[AssetPath<'static>] implements [From<&'static str>].
Because of that, assigning a string literal to a Handle<T> field automatically converts it into
[HandleTemplate<T>::Path] with that asset path when used in the bsn! macro.
Calls to bsn! automatically insert .into() conversions, and due to Rust’s blanket impl that turns [From] trait impls into their [Into]
equivalents, the conversion from &'static str to AssetPath<'static> is handled automatically.
Finally, the [HandleTemplate<T>::Path] generated gets converted to a [Handle<T>] during scene initialization,
as the asset is loaded from the given path, and the resulting handle is assigned to the field,
pointing to the asset that was found at the file path in our original string.
Constructs a new instance of the type from a table of its fields.
Missing fields are filled in from the type’s default, then from the defaults of the fields’ types.
For enums use the variant key, or construct from a variant directly.
When a type with a [Handle<T>] field derives [FromTemplate], that field is replaced by its
template type, [HandleTemplate<T>], when created via BSN.
We can see that [HandleTemplate<T>] has the following trait impl block:
[AssetPath<'static>] implements [From<&'static str>].
Because of that, assigning a string literal to a Handle<T> field automatically converts it into
[HandleTemplate<T>::Path] with that asset path when used in the bsn! macro.
Calls to bsn! automatically insert .into() conversions, and due to Rust’s blanket impl that turns [From] trait impls into their [Into]
equivalents, the conversion from &'static str to AssetPath<'static> is handled automatically.
Finally, the [HandleTemplate<T>::Path] generated gets converted to a [Handle<T>] during scene initialization,
as the asset is loaded from the given path, and the resulting handle is assigned to the field,
pointing to the asset that was found at the file path in our original string.
Constructs a new instance of the type from a table of its fields.
Missing fields are filled in from the type’s default, then from the defaults of the fields’ types.
For enums use the variant key, or construct from a variant directly.
When a type with a [Handle<T>] field derives [FromTemplate], that field is replaced by its
template type, [HandleTemplate<T>], when created via BSN.
We can see that [HandleTemplate<T>] has the following trait impl block:
[AssetPath<'static>] implements [From<&'static str>].
Because of that, assigning a string literal to a Handle<T> field automatically converts it into
[HandleTemplate<T>::Path] with that asset path when used in the bsn! macro.
Calls to bsn! automatically insert .into() conversions, and due to Rust’s blanket impl that turns [From] trait impls into their [Into]
equivalents, the conversion from &'static str to AssetPath<'static> is handled automatically.
Finally, the [HandleTemplate<T>::Path] generated gets converted to a [Handle<T>] during scene initialization,
as the asset is loaded from the given path, and the resulting handle is assigned to the field,
pointing to the asset that was found at the file path in our original string.
Constructs a new instance of the type from a table of its fields.
Missing fields are filled in from the type’s default, then from the defaults of the fields’ types.
For enums use the variant key, or construct from a variant directly.
When a type with a [Handle<T>] field derives [FromTemplate], that field is replaced by its
template type, [HandleTemplate<T>], when created via BSN.
We can see that [HandleTemplate<T>] has the following trait impl block:
[AssetPath<'static>] implements [From<&'static str>].
Because of that, assigning a string literal to a Handle<T> field automatically converts it into
[HandleTemplate<T>::Path] with that asset path when used in the bsn! macro.
Calls to bsn! automatically insert .into() conversions, and due to Rust’s blanket impl that turns [From] trait impls into their [Into]
equivalents, the conversion from &'static str to AssetPath<'static> is handled automatically.
Finally, the [HandleTemplate<T>::Path] generated gets converted to a [Handle<T>] during scene initialization,
as the asset is loaded from the given path, and the resulting handle is assigned to the field,
pointing to the asset that was found at the file path in our original string.
Constructs a new instance of the type from a table of its fields.
Missing fields are filled in from the type’s default, then from the defaults of the fields’ types.
For enums use the variant key, or construct from a variant directly.
When a type with a [Handle<T>] field derives [FromTemplate], that field is replaced by its
template type, [HandleTemplate<T>], when created via BSN.
We can see that [HandleTemplate<T>] has the following trait impl block:
[AssetPath<'static>] implements [From<&'static str>].
Because of that, assigning a string literal to a Handle<T> field automatically converts it into
[HandleTemplate<T>::Path] with that asset path when used in the bsn! macro.
Calls to bsn! automatically insert .into() conversions, and due to Rust’s blanket impl that turns [From] trait impls into their [Into]
equivalents, the conversion from &'static str to AssetPath<'static> is handled automatically.
Finally, the [HandleTemplate<T>::Path] generated gets converted to a [Handle<T>] during scene initialization,
as the asset is loaded from the given path, and the resulting handle is assigned to the field,
pointing to the asset that was found at the file path in our original string.
Constructs a new instance of the type from a table of its fields.
Missing fields are filled in from the type’s default, then from the defaults of the fields’ types.
For enums use the variant key, or construct from a variant directly.
A unique runtime-only identifier for an [Asset]. This is cheap to [Copy]/[Clone] and is not directly tied to the
lifetime of the Asset. This means it can point to an [Asset] that no longer exists.
For an identifier tied to the lifetime of an asset, see Handle.
For an “untyped” / “generic-less” id, see [UntypedAssetId].
Constructs a new instance of the type from a table of its fields.
Missing fields are filled in from the type’s default, then from the defaults of the fields’ types.
For enums use the variant key, or construct from a variant directly.
Initial settings to be used when audio starts playing.
If you would like to control the audio while it is playing, query for the
AudioSink or SpatialAudioSink
components. Changes to this component will not be applied to already-playing audio.
Constructs a new instance of the type from a table of its fields.
Missing fields are filled in from the type’s default, then from the defaults of the fields’ types.
For enums use the variant key, or construct from a variant directly.
Constructs a new instance of the type from a table of its fields.
Missing fields are filled in from the type’s default, then from the defaults of the fields’ types.
For enums use the variant key, or construct from a variant directly.
Constructs a new instance of the type from a table of its fields.
Missing fields are filled in from the type’s default, then from the defaults of the fields’ types.
For enums use the variant key, or construct from a variant directly.
Constructs a new instance of the type from a table of its fields.
Missing fields are filled in from the type’s default, then from the defaults of the fields’ types.
For enums use the variant key, or construct from a variant directly.
Constructs a new instance of the type from a table of its fields.
Missing fields are filled in from the type’s default, then from the defaults of the fields’ types.
For enums use the variant key, or construct from a variant directly.
Constructs a new instance of the type from a table of its fields.
Missing fields are filled in from the type’s default, then from the defaults of the fields’ types.
For enums use the variant key, or construct from a variant directly.
Specifies a unique attachment of a script. These attachments are mapped to [`bevy_mod_scripting_core::ContextKey`]’s depending on the context policy used.
Runs a callback on other scripts immediately, and returns what it returned. For example `run_script_callback(“take_damage”, ScriptAttachment.new_entity_script(enemy, script), 10)` calls `take_...
A reference to a variant of an enum, independent of any value of that enum.
This is what scripts see when accessing MyEnum.Variant, it can be compared to
the result of value:variant() and used to construct values of the variant.
Constructs a value of this enum variant from a table of its fields.
Missing fields are filled in from the enum’s default if it is this variant, then from the defaults of the fields’ types.
Retrieves the script namespace of the type, by default the name of the crate defining it.
Types in the root namespace have the empty string as namespace.
Constructs a new instance of the type from a table of its fields.
Missing fields are filled in from the type’s default, then from the defaults of the fields’ types.
For enums use the variant key, or construct from a variant directly.
Retrieves the script namespace of the type, by default the name of the crate defining it.
Types in the root namespace have the empty string as namespace.
Constructs a new instance of the type from a table of its fields.
Missing fields are filled in from the type’s default, then from the defaults of the fields’ types.
For enums use the variant key, or construct from a variant directly.
Builds the query and retrieves the entities and component references.
Each result is a list of the entity followed by the components in the order they were added to the query,
i.e. local entity, a, b = result[1], result[2], result[3] in Lua, absent optional components are nil.
Only matches entities whose component was changed since the system last ran (outside of systems: since the last update).
This component will NOT be retrieved.
Only matches entities whose component was added since the system last ran (outside of systems: since the last update).
This component will NOT be retrieved.
Constructs a new instance of the type from a table of its fields.
Missing fields are filled in from the type’s default, then from the defaults of the fields’ types.
For enums use the variant key, or construct from a variant directly.
Retrieves the script namespace of the type, by default the name of the crate defining it.
Types in the root namespace have the empty string as namespace.
Constructs a new instance of the type from a table of its fields.
Missing fields are filled in from the type’s default, then from the defaults of the fields’ types.
For enums use the variant key, or construct from a variant directly.
Retrieves the script namespace of the type, by default the name of the crate defining it.
Types in the root namespace have the empty string as namespace.
Constructs a new instance of the type from a table of its fields.
Missing fields are filled in from the type’s default, then from the defaults of the fields’ types.
For enums use the variant key, or construct from a variant directly.
Constructs a new instance of the type from a table of its fields.
Missing fields are filled in from the type’s default, then from the defaults of the fields’ types.
For enums use the variant key, or construct from a variant directly.
Constructs a new instance of the type from a table of its fields.
Missing fields are filled in from the type’s default, then from the defaults of the fields’ types.
For enums use the variant key, or construct from a variant directly.
Specifies the system is to run *before* the given set. Sets are registered by scripts with `register_new_system_set`, game sets are found with `get_...
Specifies the system is to run before the script system with the given callback label of the given script.
That system must have been added before this one.
Specifies the system is to run after the given set.
Sets are registered by scripts with register_new_system_set, game sets are found with get_system_set_by_name.
Specifies the system is to run before the given set.
Sets are registered by scripts with register_new_system_set, game sets are found with get_system_set_by_name.
Puts the system in the given set, ordering the set orders the system.
Sets are registered by scripts with register_new_system_set, game sets are found with get_system_set_by_name.
Requests the system have read only access to the given resource. The resource will be added to the
list of arguments of the callback in the order they’re provided.
Script systems reading a resource can run in parallel with each other, unlike ones writing it.
Specifies the system is to run after the script system with the given callback label of the given script.
That system must have been added before this one.
Requests the system have access to the given resource. The resource will be added to the
list of arguments of the callback in the order they’re provided.
Constructs a new instance of the type from a table of its fields.
Missing fields are filled in from the type’s default, then from the defaults of the fields’ types.
For enums use the variant key, or construct from a variant directly.
Constructs a new instance of the type from a table of its fields.
Missing fields are filled in from the type’s default, then from the defaults of the fields’ types.
For enums use the variant key, or construct from a variant directly.
Specifies a unique attachment of a script. These attachments are mapped to [bevy_mod_scripting_core::ContextKey]’s depending on the context policy used.
Constructs a new instance of the type from a table of its fields.
Missing fields are filled in from the type’s default, then from the defaults of the fields’ types.
For enums use the variant key, or construct from a variant directly.
Constructs a new instance of the type from a table of its fields.
Missing fields are filled in from the type’s default, then from the defaults of the fields’ types.
For enums use the variant key, or construct from a variant directly.
Constructs a new instance of the type from a table of its fields.
Missing fields are filled in from the type’s default, then from the defaults of the fields’ types.
For enums use the variant key, or construct from a variant directly.
A reflectable system set: a set node of a schedule graph, or a set scripts put and order their systems in.
Bevy does not expose the interned sets of a schedule graph, so set nodes of a graph only have a node id,
while sets made from an interned set (i.e. the ones scripts use) only have the set.
Constructs a new instance of the type from a table of its fields.
Missing fields are filled in from the type’s default, then from the defaults of the fields’ types.
For enums use the variant key, or construct from a variant directly.
Constructs a new instance of the type from a table of its fields.
Missing fields are filled in from the type’s default, then from the defaults of the fields’ types.
For enums use the variant key, or construct from a variant directly.
Constructs a new instance of the type from a table of its fields.
Missing fields are filled in from the type’s default, then from the defaults of the fields’ types.
For enums use the variant key, or construct from a variant directly.
Constructs a new instance of the type from a table of its fields.
Missing fields are filled in from the type’s default, then from the defaults of the fields’ types.
For enums use the variant key, or construct from a variant directly.
Constructs a new instance of the type from a table of its fields.
Missing fields are filled in from the type’s default, then from the defaults of the fields’ types.
For enums use the variant key, or construct from a variant directly.
Constructs a new instance of the type from a table of its fields.
Missing fields are filled in from the type’s default, then from the defaults of the fields’ types.
For enums use the variant key, or construct from a variant directly.
Constructs a new instance of the type from a table of its fields.
Missing fields are filled in from the type’s default, then from the defaults of the fields’ types.
For enums use the variant key, or construct from a variant directly.
Constructs a new instance of the type from a table of its fields.
Missing fields are filled in from the type’s default, then from the defaults of the fields’ types.
For enums use the variant key, or construct from a variant directly.
Scripts receive it as a plain list, the entity followed by the components in the order they were added to the query,
i.e. {entity, a, b} in Lua, with nil for absent optional components. Nothing is allocated for the result itself.
Constructs a new instance of the type from a table of its fields.
Missing fields are filled in from the type’s default, then from the defaults of the fields’ types.
For enums use the variant key, or construct from a variant directly.
Constructs a new instance of the type from a table of its fields.
Missing fields are filled in from the type’s default, then from the defaults of the fields’ types.
For enums use the variant key, or construct from a variant directly.
Constructs a new instance of the type from a table of its fields.
Missing fields are filled in from the type’s default, then from the defaults of the fields’ types.
For enums use the variant key, or construct from a variant directly.
Constructs a new instance of the type from a table of its fields.
Missing fields are filled in from the type’s default, then from the defaults of the fields’ types.
For enums use the variant key, or construct from a variant directly.
Constructs a new instance of the type from a table of its fields.
Missing fields are filled in from the type’s default, then from the defaults of the fields’ types.
For enums use the variant key, or construct from a variant directly.
Constructs a new instance of the type from a table of its fields.
Missing fields are filled in from the type’s default, then from the defaults of the fields’ types.
For enums use the variant key, or construct from a variant directly.
Constructs a new instance of the type from a table of its fields.
Missing fields are filled in from the type’s default, then from the defaults of the fields’ types.
For enums use the variant key, or construct from a variant directly.
Constructs a new instance of the type from a table of its fields.
Missing fields are filled in from the type’s default, then from the defaults of the fields’ types.
For enums use the variant key, or construct from a variant directly.
Constructs a new instance of the type from a table of its fields.
Missing fields are filled in from the type’s default, then from the defaults of the fields’ types.
For enums use the variant key, or construct from a variant directly.
Attempts to construct the given type, given an arbitrary map of values.
Unlike Type.new, this works for every type, including ones with a new function of their own.
Missing fields are filled in from the type’s default, then from the defaults of the fields’ types, as for Type.new.
Queues a callback to run on other scripts as soon as the current callback finishes, in the same frame.
Unlike run_script_callback, this works for any script, including the calling script itself and scripts sharing its context.
Queued callbacks run in the order they were queued, and their errors are reported as script errors instead of being raised here.
Runs a callback on other scripts immediately, and returns what it returned.
For example run_script_callback("take_damage", ScriptAttachment.new_entity_script(enemy, script), 10)
calls take_damage(10) in that script before this function returns.
The target is either a single script attachment, returning that callback’s value,
or [Recipients], returning a list with the value of each script which ran the callback.
Errors of the callbacks are raised in the calling script, for recipients only after all of them ran.
Callbacks cannot run immediately in a script whose context is busy, i.e. the calling script itself,
scripts sharing its context (depending on the context policy), or scripts further up the current chain of callbacks.
Use queue_script_callback for those. It is also unavailable in script systems running in parallel with other systems.
Project a 3D space onto a 2D surface using parallel lines, i.e., unlike [`PerspectiveProjection`], the size of objects remains the same regardless of their distance to the camera. The volume contained in the projection is called the *view frustum*. Since the viewport is rectangular and projection lines are parallel, the view frustum takes the shape of a cuboid. Note that the scale of the projection and the apparent size of objects are inversely proportional. As the size of the projection increases, the size of objects decreases. # Examples Configure the orthographic projection to one world unit per 100 window pixels: ``` # use bevy_camera::{OrthographicProjection, Projection, ScalingMode}; let projection = Projection::Orthographic(OrthographicProjection { scaling_mode: ScalingMode::WindowSize, scale: 0.01, ..OrthographicProjection::default_2d() }); ```
Settings to define a camera sub view. When [`Camera::sub_camera_view`] is `Some`, only the sub-section of the image defined by `size` and `offset` (relative to the `full_size` of the whole image) is projected to the cameras viewport. Take the example of the following multi-monitor setup: ```css ┌───┬───┐ │ A │ B │ ├───┼───┤ │ C │ D │ └───┴───┘ ``` If each monitor is 1920x1080, the whole image will have a resolution of 3840x2160. For each monitor we can use a single camera with a viewport of the same size as the monitor it corresponds to. To ensure that the image is cohesive, we can use a different sub view on each camera: - Camera A: `full_size` = 3840x2160, `size` = 1920x1080, `offset` = 0,0 - Camera B: `full_size` = 3840x2160, `size` = 1920x1080, `offset` = 1920,0 - Camera C: `full_size` = 3840x2160, `size` = 1920x1080, `offset` = 0,1080 - Camera D: `full_size` = 3840x2160, `size` = 1920x1080, `offset` = 1920,1080 However since only the ratio between the values is important, they could all be divided by 120 and still produce the same image. Camera D would for example have the following values: `full_size` = 32x18, `size` = 16x9, `offset` = 16,9
Render viewport configuration for the [`Camera`] component. The viewport defines the area on the render target to which the camera renders its image. You can overlay multiple cameras in a single window using viewports to create effects like split screen, minimaps, and character viewers.
Note that the physical position is in actual screen coordinates and not virtual pixels for window targets. You should use the scaling factor reported by the window, which on some OS’s defaults to a value other than 1. Please see the example code (which assumes a single camera and window) ```no_run # use bevy_camera::{Camera, Projection, Viewport}; # use bevy_transform::prelude::Transform; # use bevy_ecs::prelude::*; # use bevy_math::UVec2; # use bevy_window::Window; # use bevy_utils::default; fn update_viewport( mut camera_query: Query<(&mut Camera, &mut Transform, &mut Projection)>, windows: Query<&Window> ) { let Ok((mut camera, _, _)) = camera_query.single_mut() else { return; }; let window = windows.single().expect(“Window not found”); let scale = window.resolution.scale_factor(); camera.viewport = Some(Viewport { physical_position: (UVec2::new(10, 10).as_vec2() * scale).as_uvec2(), physical_size: (UVec2::new(100, 100).as_vec2() * scale).as_uvec2(), ..default() }); } ```
For a camera, specifies the color used to clear the viewport [before rendering](crate::camera::Camera::clear_color) or when [writing to the final render target texture](crate::camera::Camera::output_mode)...
The defining [Component] for camera entities,
storing information about how and what to render through this camera.
The [Camera] component is added to an entity to define the properties of the viewpoint from
which rendering occurs. It defines the position of the view to render, the projection method
to transform the 3D objects into a 2D image, as well as the render target into which that image
is produced.
Note that a [Camera] needs a CameraRenderGraph to render anything.
This is typically provided by adding a Camera2d or Camera3d component,
but custom render graphs can also be defined. Inserting a [Camera] with no render
graph will emit an error at runtime.
Constructs a new instance of the type from a table of its fields.
Missing fields are filled in from the type’s default, then from the defaults of the fields’ types.
For enums use the variant key, or construct from a variant directly.
Project a 3D space onto a 2D surface using parallel lines, i.e., unlike [PerspectiveProjection],
the size of objects remains the same regardless of their distance to the camera.
The volume contained in the projection is called the view frustum. Since the viewport is rectangular
and projection lines are parallel, the view frustum takes the shape of a cuboid.
Note that the scale of the projection and the apparent size of objects are inversely proportional.
As the size of the projection increases, the size of objects decreases.
Constructs a new instance of the type from a table of its fields.
Missing fields are filled in from the type’s default, then from the defaults of the fields’ types.
For enums use the variant key, or construct from a variant directly.
Whether or not an entity is visible in the hierarchy.
This is a computed component that users should not change manually.
To set the visibility of an entity, use [Visibility] instead.
For more information, see module level documentation.
This property is updated in VisibilityPropagate in the [PostUpdate] schedule.
Until then, it is not up to date with [Visibility] if it was changed
in the same frame.
If this is false, then [ViewVisibility] should also be false.
Constructs a new instance of the type from a table of its fields.
Missing fields are filled in from the type’s default, then from the defaults of the fields’ types.
For enums use the variant key, or construct from a variant directly.
Constructs a new instance of the type from a table of its fields.
Missing fields are filled in from the type’s default, then from the defaults of the fields’ types.
For enums use the variant key, or construct from a variant directly.
Constructs a new instance of the type from a table of its fields.
Missing fields are filled in from the type’s default, then from the defaults of the fields’ types.
For enums use the variant key, or construct from a variant directly.
Constructs a new instance of the type from a table of its fields.
Missing fields are filled in from the type’s default, then from the defaults of the fields’ types.
For enums use the variant key, or construct from a variant directly.
Constructs a new instance of the type from a table of its fields.
Missing fields are filled in from the type’s default, then from the defaults of the fields’ types.
For enums use the variant key, or construct from a variant directly.
Constructs a new instance of the type from a table of its fields.
Missing fields are filled in from the type’s default, then from the defaults of the fields’ types.
For enums use the variant key, or construct from a variant directly.
Constructs a new instance of the type from a table of its fields.
Missing fields are filled in from the type’s default, then from the defaults of the fields’ types.
For enums use the variant key, or construct from a variant directly.
Constructs a new instance of the type from a table of its fields.
Missing fields are filled in from the type’s default, then from the defaults of the fields’ types.
For enums use the variant key, or construct from a variant directly.
Constructs a new instance of the type from a table of its fields.
Missing fields are filled in from the type’s default, then from the defaults of the fields’ types.
For enums use the variant key, or construct from a variant directly.
Constructs a new instance of the type from a table of its fields.
Missing fields are filled in from the type’s default, then from the defaults of the fields’ types.
For enums use the variant key, or construct from a variant directly.
When [Camera::sub_camera_view] is Some, only the sub-section of the
image defined by size and offset (relative to the full_size of the
whole image) is projected to the cameras viewport.
Take the example of the following multi-monitor setup:
┌───┬───┐
│ A │ B │
├───┼───┤
│ C │ D │
└───┴───┘
If each monitor is 1920x1080, the whole image will have a resolution of
3840x2160. For each monitor we can use a single camera with a viewport of
the same size as the monitor it corresponds to. To ensure that the image is
cohesive, we can use a different sub view on each camera:
However since only the ratio between the values is important, they could all
be divided by 120 and still produce the same image. Camera D would for
example have the following values:
full_size = 32x18, size = 16x9, offset = 16,9
Constructs a new instance of the type from a table of its fields.
Missing fields are filled in from the type’s default, then from the defaults of the fields’ types.
For enums use the variant key, or construct from a variant directly.
Render viewport configuration for the [Camera] component.
The viewport defines the area on the render target to which the camera renders its image.
You can overlay multiple cameras in a single window using viewports to create effects like
split screen, minimaps, and character viewers.
Note that the physical position is in actual screen coordinates and not virtual pixels for window targets. You should use the scaling factor reported by the window, which on some OS’s defaults to a value other than 1.
Please see the example code (which assumes a single camera and window)
# use bevy_camera::{Camera, Projection, Viewport};
# use bevy_transform::prelude::Transform;
# use bevy_ecs::prelude::*;
# use bevy_math::UVec2;
# use bevy_window::Window;
# use bevy_utils::default;
fn update_viewport(
mut camera_query: Query<(&mut Camera, &mut Transform, &mut Projection)>,
windows: Query<&Window>
) {
let Ok((mut camera, _, _)) = camera_query.single_mut() else { return; };
let window = windows.single().expect("Window not found");
let scale = window.resolution.scale_factor();
camera.viewport = Some(Viewport {
physical_position: (UVec2::new(10, 10).as_vec2() * scale).as_uvec2(),
physical_size: (UVec2::new(100, 100).as_vec2() * scale).as_uvec2(),
..default()
});
}
Constructs a new instance of the type from a table of its fields.
Missing fields are filled in from the type’s default, then from the defaults of the fields’ types.
For enums use the variant key, or construct from a variant directly.
Constructs a new instance of the type from a table of its fields.
Missing fields are filled in from the type’s default, then from the defaults of the fields’ types.
For enums use the variant key, or construct from a variant directly.
MSAA writeback copies the previous camera’s output into the MSAA sampled texture before
rendering, allowing multiple cameras to layer their results when MSAA is enabled.
Constructs a new instance of the type from a table of its fields.
Missing fields are filled in from the type’s default, then from the defaults of the fields’ types.
For enums use the variant key, or construct from a variant directly.
The effect of these scaling modes are combined with the [OrthographicProjection::scale] property.
For example, if the scaling mode is ScalingMode::Fixed { width: 100.0, height: 300 } and the scale is 2.0,
the projection will be 200 world units wide and 600 world units tall.
Constructs a new instance of the type from a table of its fields.
Missing fields are filled in from the type’s default, then from the defaults of the fields’ types.
For enums use the variant key, or construct from a variant directly.
[CIE 1931](https://en.wikipedia.org/wiki/CIE_1931_color_space) color space, also known as XYZ, with an alpha channel. The SDR channel bounds below are the D65 white point, `(0.9505, 1.0, 1.0889)`.
[Color] supports all the standard color operations, such as mixing,
luminance and hue adjustment,
and diffing. These operations delegate to the concrete color space contained
by [Color], but will convert to Oklch for operations which aren’t supported in the
current space. After performing the operation, if a conversion was required, the result will be
converted back into the original color space.
#![allow(unused)]
fn main() {
use bevy_color::{Hue, Color};
let red_hsv = Color::hsv(0., 1., 1.);
let red_srgb = Color::srgb(1., 0., 0.);
// HSV has a definition of hue, so it will be returned.
red_hsv.hue();
// SRGB doesn't have a native definition for hue.
// Converts to Oklch and returns that result.
red_srgb.hue();
}
Oklch has been chosen as the intermediary space in cases where conversion is required
due to its perceptual uniformity and broad support for Bevy’s color operations.
To avoid the cost of repeated conversion, and ensure consistent results where that is desired,
first convert this [Color] into your desired color space.
A color from red, green and blue in the sRGB space, each from `0` to `1`, fully opaque. ```multi-lang-example,lua local Color = import_namespace(“color”).Color local red = Color.srgb(1.0, 0.0, 0.0) assert(red:to_srgba().red == 1.0) `...
Constructs a new instance of the type from a table of its fields.
Missing fields are filled in from the type’s default, then from the defaults of the fields’ types.
For enums use the variant key, or construct from a variant directly.
A color from linear red, green, blue and alpha, each from `0` to `1`. ```multi-lang-example,lua local LinearRgba = import_namespace(“color”).LinearRgba local c = LinearRgba.new(1.0, 0.0, 0.0, 1.0) assert(c.red == 1.0) ``` ```multi-lang-example,rune let LinearRgba = import_...
A color from red, green, blue and alpha, each from `0` to `1`. ```multi-lang-example,lua local Srgba = import_namespace(“color”).Srgba local c = Srgba.new(1.0, 0.0, 0.0, 0.5) assert(c.alpha == 0.5) ``` ```multi-lang-example,rune let Srgba = import_...
Okhsl is defined relative to the sRGB (Rec. 709) gamut. Converting a wide-gamut or
HDR color clamps the lightness to 1.0 but can push saturation outside [0.0, 1.0].
Constructs a new instance of the type from a table of its fields.
Missing fields are filled in from the type’s default, then from the defaults of the fields’ types.
For enums use the variant key, or construct from a variant directly.
Constructs a new instance of the type from a table of its fields.
Missing fields are filled in from the type’s default, then from the defaults of the fields’ types.
For enums use the variant key, or construct from a variant directly.
Constructs a new instance of the type from a table of its fields.
Missing fields are filled in from the type’s default, then from the defaults of the fields’ types.
For enums use the variant key, or construct from a variant directly.
Constructs a new instance of the type from a table of its fields.
Missing fields are filled in from the type’s default, then from the defaults of the fields’ types.
For enums use the variant key, or construct from a variant directly.
Constructs a new instance of the type from a table of its fields.
Missing fields are filled in from the type’s default, then from the defaults of the fields’ types.
For enums use the variant key, or construct from a variant directly.
Constructs a new instance of the type from a table of its fields.
Missing fields are filled in from the type’s default, then from the defaults of the fields’ types.
For enums use the variant key, or construct from a variant directly.
Constructs a new instance of the type from a table of its fields.
Missing fields are filled in from the type’s default, then from the defaults of the fields’ types.
For enums use the variant key, or construct from a variant directly.
Constructs a new instance of the type from a table of its fields.
Missing fields are filled in from the type’s default, then from the defaults of the fields’ types.
For enums use the variant key, or construct from a variant directly.
Unique identifier for an entity in a [`World`]. Note that this is just an id, not the entity itself. Further, the entity this id refers to may no longer exist in the [`World`]...
Stores the parent entity of this child entity with this component. This is a [`Relationship`] component, and creates the canonical “parent / child” hierarchy. This is the “source of truth” component, and it pairs with the [`Children`]...
[`EntityEvent`] emitted for each component on an entity when it is despawned. See [`ComponentHooks::on_despawn`](`crate::lifecycle::ComponentHooks::on_...
Unique identifier for an entity in a World.
Note that this is just an id, not the entity itself.
Further, the entity this id refers to may no longer exist in the World.
For more information about entities, their ids, and how to use them, see the module docs.
Once an entity is despawned, it ceases to exist.
However, its [Entity] id is still present, and may still be contained in some data.
This becomes problematic because it is possible for a later entity to be spawned at the exact same id!
If this happens, which is rare but very possible, it will be logged.
Aliasing can happen without warning.
Holding onto a [Entity] id corresponding to an entity well after that entity was despawned can cause un-intuitive behavior for both ordering, and comparing in general.
To prevent these bugs, it is generally best practice to stop holding an [Entity] or [EntityGeneration] value as soon as you know it has been despawned.
If you must do otherwise, do not assume the [Entity] id corresponds to the same entity it originally did.
See [EntityGeneration]’s docs for more information about aliasing and why it occurs.
For all intents and purposes, Entity should be treated as an opaque identifier. The internal bit
representation is liable to change from release to release as are the behaviors or performance
characteristics of any of its trait implementations (i.e. Ord, Hash, etc.). This means that changes in
Entity’s representation, though made readable through various functions on the type, are not considered
breaking changes under SemVer.
In particular, directly serializing with Serialize and Deserialize make zero guarantee of long
term wire format compatibility. Changes in behavior will cause serialized Entity values persisted
to long term storage (i.e. disk, databases, etc.) will fail to deserialize upon being updated.
This data type is returned by iterating a Query that has Entity as part of its query fetch type parameter (learn more).
It can also be obtained by calling EntityCommands::id or EntityWorldMut::id.
The entity as a single number, which can be turned back into the entity with [`from_bits`](Self::from_bits). Only meaningful while the app is running, don’t save it.
Constructs a new instance of the type from a table of its fields.
Missing fields are filled in from the type’s default, then from the defaults of the fields’ types.
For enums use the variant key, or construct from a variant directly.
Stores the parent entity of this child entity with this component.
This is a Relationship component, and creates the canonical
“parent / child” hierarchy. This is the “source of truth” component, and it pairs with
the [Children] RelationshipTarget.
This relationship should be used for things like:
Organizing entities in a scene
Propagating configuration or data inherited from a parent, such as “visibility” or “world-space global transforms”.
Ensuring a hierarchy is despawned when an entity is despawned.
[ChildOf] contains a single “target” [Entity]. When [ChildOf] is inserted on a “source” entity,
the “target” entity will automatically (and immediately, via a component hook) have a [Children]
component inserted, and the “source” entity will be added to that [Children] instance.
If the [ChildOf] component is replaced with a different “target” entity, the old target’s [Children]
will be automatically (and immediately, via a component hook) be updated to reflect that change.
Likewise, when the [ChildOf] component is removed, the “source” entity will be removed from the old
target’s [Children]. If this results in [Children] being empty, [Children] will be automatically removed.
When a parent is despawned, all children (and their descendants) will also be despawned.
You can create parent-child relationships in a variety of ways. The most direct way is to insert a [ChildOf] component:
# use bevy_ecs::prelude::*;
# let mut world = World::new();
let root = world.spawn_empty().id();
let child1 = world.spawn(ChildOf(root)).id();
let child2 = world.spawn(ChildOf(root)).id();
let grandchild = world.spawn(ChildOf(child1)).id();
assert_eq!(&**world.entity(root).get::<Children>().unwrap(), &[child1, child2]);
assert_eq!(&**world.entity(child1).get::<Children>().unwrap(), &[grandchild]);
world.entity_mut(child2).remove::<ChildOf>();
assert_eq!(&**world.entity(root).get::<Children>().unwrap(), &[child1]);
world.entity_mut(root).despawn();
assert!(world.get_entity(root).is_err());
assert!(world.get_entity(child1).is_err());
assert!(world.get_entity(grandchild).is_err());
However if you are spawning many children, you might want to use the [EntityWorldMut::with_children] helper instead:
# use bevy_ecs::prelude::*;
# let mut world = World::new();
let mut child1 = None;
let mut child2 = None;
let mut grandchild = None;
let root = world.spawn_empty().with_children(|p| {
child1 = Some(p.spawn_empty().with_children(|p| {
grandchild = Some(p.spawn_empty().id());
}).id());
child2 = Some(p.spawn_empty().id());
}).id();
assert_eq!(&**world.entity(root).get::<Children>().unwrap(), &[child1.unwrap(), child2.unwrap()]);
assert_eq!(&**world.entity(child1.unwrap()).get::<Children>().unwrap(), &[grandchild.unwrap()]);
Constructs a new instance of the type from a table of its fields.
Missing fields are filled in from the type’s default, then from the defaults of the fields’ types.
For enums use the variant key, or construct from a variant directly.
Component used to identify an entity. Stores a hash for faster comparisons.
The hash is eagerly re-computed upon each update to the name.
[Name] should not be treated as a globally unique identifier for entities,
as multiple entities can have the same name. [Entity] should be
used instead as the default unique identifier.
Creates a new [`Name`](bevy_ecs::name::Name) component, used to identify entities. ```multi-lang-example,lua local Name = import_namespace(“ecs”).Name local name = Name.new(“player”) assert(name:as_str() == “player”) ``` ```multi-lang-example,rune let Name = import_namespace(“ecs”).Name; let name = Name.new(“player”); assert!(name.as_str() == “player”); ```
While the distinction between ComponentId and [TypeId] may seem superficial, breaking them
into two separate but related concepts allows components to exist outside of Rust’s type system.
Each Rust type registered as a Component will have a corresponding ComponentId, but additional
ComponentIds may exist in a World to track components which cannot be
represented as Rust types for scripting or other advanced use-cases.
A ComponentId is tightly coupled to its parent World. Attempting to use a ComponentId from
one World to access the metadata of a Component in a different World is undefined behavior
and must not be attempted.
Given a type T which implements [Component] (including Resource), the ComponentId for T can be retrieved
from a World using World::component_id() or via [Components::component_id()].
Constructs a new instance of the type from a table of its fields.
Missing fields are filled in from the type’s default, then from the defaults of the fields’ types.
For enums use the variant key, or construct from a variant directly.
Trigger emitted when a component is inserted onto an entity that does not already have that
component. Runs before Insert.
See ComponentHooks::on_add for more information.
Constructs a new instance of the type from a table of its fields.
Missing fields are filled in from the type’s default, then from the defaults of the fields’ types.
For enums use the variant key, or construct from a variant directly.
Constructs a new instance of the type from a table of its fields.
Missing fields are filled in from the type’s default, then from the defaults of the fields’ types.
For enums use the variant key, or construct from a variant directly.
Constructs a new instance of the type from a table of its fields.
Missing fields are filled in from the type’s default, then from the defaults of the fields’ types.
For enums use the variant key, or construct from a variant directly.
Trigger emitted when a component is inserted, regardless of whether or not the entity already
had that component. Runs after Add, if it ran.
See ComponentHooks::on_insert for more information.
Constructs a new instance of the type from a table of its fields.
Missing fields are filled in from the type’s default, then from the defaults of the fields’ types.
For enums use the variant key, or construct from a variant directly.
Trigger emitted when a component is removed from an entity, and runs before the component is
removed, so you can still access the component data.
See ComponentHooks::on_remove for more information.
Constructs a new instance of the type from a table of its fields.
Missing fields are filled in from the type’s default, then from the defaults of the fields’ types.
For enums use the variant key, or construct from a variant directly.
Constructs a new instance of the type from a table of its fields.
Missing fields are filled in from the type’s default, then from the defaults of the fields’ types.
For enums use the variant key, or construct from a variant directly.
Immediate mode debug drawing: lines and shapes drawn this frame.
Every function draws straight away, the shapes are shown for the current frame only.
Needs the gizmo plugin, without it the functions fail with an error.
local Color = import_namespace("color").Color
local Gizmos = import_namespace("gizmos").Gizmos
local Vec3 = import_namespace("math").Vec3
Gizmos.line(Vec3.zero(), Vec3.new(1, 0, 0), Color.white())
let Color = import_namespace("color").Color;
let Gizmos = import_namespace("gizmos").Gizmos;
let Vec3 = import_namespace("math").Vec3;
Gizmos.line(Vec3.zero(), Vec3.new(1.0, 0.0, 0.0), Color.white());
Constructs a new instance of the type from a table of its fields.
Missing fields are filled in from the type’s default, then from the defaults of the fields’ types.
For enums use the variant key, or construct from a variant directly.
A button on a mouse device. ## Usage It is used as the generic `T` value of an [`ButtonInput`] to create a `bevy` resource. ## Updating The resource is updated inside of the [`mouse_button_input_system`]...
Represents gamepad input types that are mapped in the range [-1.0, 1.0]. ## Usage This is used to determine which axis has changed its value when receiving a gamepad axis event. It is also used in the [`Gamepad`]...
Represents gamepad input types that are mapped in the range [0.0, 1.0]. ## Usage This is used to determine which button has changed its value when receiving gamepad button events. It is also used in the [`Gamepad`]...
The cause for a [`FocusGained`] Sometimes widgets would like to know how their focus was gained so they can act accordingly. For example, a text input may want to select all text when navigated into, but not when pressed.
Constructs a new instance of the type from a table of its fields.
Missing fields are filled in from the type’s default, then from the defaults of the fields’ types.
For enums use the variant key, or construct from a variant directly.
Constructs a new instance of the type from a table of its fields.
Missing fields are filled in from the type’s default, then from the defaults of the fields’ types.
For enums use the variant key, or construct from a variant directly.
Constructs a new instance of the type from a table of its fields.
Missing fields are filled in from the type’s default, then from the defaults of the fields’ types.
For enums use the variant key, or construct from a variant directly.
Resource representing which entity has input focus, if any. Input events (other than pointer-like inputs) will be
dispatched to the current focus entity, or to the primary window if no entity has focus.
Changing the input focus is as easy as modifying this resource.
To detect when an entity gains or loses focus, listen for the [FocusGained] and [FocusLost] events.
#![allow(unused)]
fn main() {
use bevy_ecs::prelude::*;
use bevy_input_focus::{FocusCause, InputFocus};
fn clear_focus(mut input_focus: ResMut<InputFocus>) {
input_focus.clear();
}
}
With exclusive (or deferred) world access:
#![allow(unused)]
fn main() {
use bevy_ecs::prelude::*;
use bevy_input_focus::{FocusCause, InputFocus};
fn set_focus_from_world(world: &mut World) {
let entity = world.spawn_empty().id();
// Fetch the resource from the world
let mut input_focus = world.resource_mut::<InputFocus>();
// Then mutate it!
input_focus.set(entity, FocusCause::Navigated);
// Or you can just insert a fresh copy of the resource
// which will overwrite the existing one.
world.insert_resource(InputFocus::from_entity(entity));
}
}
Constructs a new instance of the type from a table of its fields.
Missing fields are filled in from the type’s default, then from the defaults of the fields’ types.
For enums use the variant key, or construct from a variant directly.
In case multiple systems are checking for [ButtonInput::just_pressed] or [ButtonInput::just_released]
but only one should react, for example when modifying a
[Resource], you should consider clearing the input state, either by:
Using [ButtonInput::clear_just_pressed] or [ButtonInput::clear_just_released] instead.
Calling [ButtonInput::clear] or [ButtonInput::reset] immediately after the state change.
ButtonInput<KeyCode> is tied to window focus. For example, if the user holds a button
while the window loses focus, [ButtonInput::just_released] will be triggered. Similarly if the window
regains focus, [ButtonInput::just_pressed] will be triggered.
ButtonInput<GamepadButton> is independent of window focus.
When adding this resource for a new input type, you should:
Call the [ButtonInput::press] method for each press event.
Call the [ButtonInput::release] method for each release event.
Call the [ButtonInput::clear] method at each frame start, before processing events.
Note: Calling clear from a ResMut will trigger change detection.
It may be preferable to use DetectChangesMut::bypass_change_detection
to avoid causing the resource to always be marked as changed.
Constructs a new instance of the type from a table of its fields.
Missing fields are filled in from the type’s default, then from the defaults of the fields’ types.
For enums use the variant key, or construct from a variant directly.
In case multiple systems are checking for [ButtonInput::just_pressed] or [ButtonInput::just_released]
but only one should react, for example when modifying a
[Resource], you should consider clearing the input state, either by:
Using [ButtonInput::clear_just_pressed] or [ButtonInput::clear_just_released] instead.
Calling [ButtonInput::clear] or [ButtonInput::reset] immediately after the state change.
ButtonInput<KeyCode> is tied to window focus. For example, if the user holds a button
while the window loses focus, [ButtonInput::just_released] will be triggered. Similarly if the window
regains focus, [ButtonInput::just_pressed] will be triggered.
ButtonInput<GamepadButton> is independent of window focus.
When adding this resource for a new input type, you should:
Call the [ButtonInput::press] method for each press event.
Call the [ButtonInput::release] method for each release event.
Call the [ButtonInput::clear] method at each frame start, before processing events.
Note: Calling clear from a ResMut will trigger change detection.
It may be preferable to use DetectChangesMut::bypass_change_detection
to avoid causing the resource to always be marked as changed.
Constructs a new instance of the type from a table of its fields.
Missing fields are filled in from the type’s default, then from the defaults of the fields’ types.
For enums use the variant key, or construct from a variant directly.
In case multiple systems are checking for [ButtonInput::just_pressed] or [ButtonInput::just_released]
but only one should react, for example when modifying a
[Resource], you should consider clearing the input state, either by:
Using [ButtonInput::clear_just_pressed] or [ButtonInput::clear_just_released] instead.
Calling [ButtonInput::clear] or [ButtonInput::reset] immediately after the state change.
ButtonInput<KeyCode> is tied to window focus. For example, if the user holds a button
while the window loses focus, [ButtonInput::just_released] will be triggered. Similarly if the window
regains focus, [ButtonInput::just_pressed] will be triggered.
ButtonInput<GamepadButton> is independent of window focus.
When adding this resource for a new input type, you should:
Call the [ButtonInput::press] method for each press event.
Call the [ButtonInput::release] method for each release event.
Call the [ButtonInput::clear] method at each frame start, before processing events.
Note: Calling clear from a ResMut will trigger change detection.
It may be preferable to use DetectChangesMut::bypass_change_detection
to avoid causing the resource to always be marked as changed.
Constructs a new instance of the type from a table of its fields.
Missing fields are filled in from the type’s default, then from the defaults of the fields’ types.
For enums use the variant key, or construct from a variant directly.
Constructs a new instance of the type from a table of its fields.
Missing fields are filled in from the type’s default, then from the defaults of the fields’ types.
For enums use the variant key, or construct from a variant directly.
Constructs a new instance of the type from a table of its fields.
Missing fields are filled in from the type’s default, then from the defaults of the fields’ types.
For enums use the variant key, or construct from a variant directly.
Constructs a new instance of the type from a table of its fields.
Missing fields are filled in from the type’s default, then from the defaults of the fields’ types.
For enums use the variant key, or construct from a variant directly.
Contains the platform-native physical key identifier
The exact values vary from platform to platform (which is part of why this is a per-platform
enum), but the values are primarily tied to the key’s physical location on the keyboard.
This enum is primarily used to store raw keycodes when Winit doesn’t map a given native
physical key identifier to a meaningful [KeyCode] variant. In the presence of identifiers we
haven’t mapped for you yet, this lets you use [KeyCode] to:
Correctly match key press and release events.
On non-web platforms, support assigning keybinds to virtually any key through a UI.
Constructs a new instance of the type from a table of its fields.
Missing fields are filled in from the type’s default, then from the defaults of the fields’ types.
For enums use the variant key, or construct from a variant directly.
Constructs a new instance of the type from a table of its fields.
Missing fields are filled in from the type’s default, then from the defaults of the fields’ types.
For enums use the variant key, or construct from a variant directly.
Stores the position data of the input devices of type T.
The values are stored as f32s, using [Axis::set].
Use [Axis::get] to retrieve the value clamped between [Axis::MIN] and [Axis::MAX]
inclusive, or unclamped using [Axis::get_unclamped].
Constructs a new instance of the type from a table of its fields.
Missing fields are filled in from the type’s default, then from the defaults of the fields’ types.
For enums use the variant key, or construct from a variant directly.
Curve functions over the [unit interval], commonly used for easing transitions. `EaseFunction` can be used on its own to interpolate between `0.0` and `1.0`. It can also be combined with [`EasingCurve`]...
A 2D rotation. # Example ``` # use approx::assert_relative_eq; # use bevy_math::{Rot2, Vec2}; use std::f32::consts::PI; // Create rotations from counterclockwise angles in radians or degrees let rotation1 = Rot2::radians(PI / 2.0); let rotation2 = Rot2::degrees(45.0); // Get the angle back as radians or degrees assert_eq!(rotation1.as_degrees(), 90.0); assert_eq!(rotation2.as_radians(), PI / 4.0); // “Add” rotations together using `*` #[cfg(feature = “approx”)] assert_relative_eq!(rotation1 * rotation2, Rot2::degrees(135.0)); // Rotate vectors #[cfg(feature = “approx”)] assert_...
Curve functions over the unit interval, commonly used for easing transitions.
EaseFunction can be used on its own to interpolate between 0.0 and 1.0.
It can also be combined with [EasingCurve] to interpolate between other
intervals and types, including vectors and rotations.
sample the smoothstep function at various points. This will return None
if the parameter is outside the unit interval.
# use bevy_math::prelude::*;
# use bevy_curve::prelude::*;
let f = EaseFunction::SmoothStep;
assert_eq!(f.sample(-1.0), None);
assert_eq!(f.sample(0.0), Some(0.0));
assert_eq!(f.sample(0.5), Some(0.5));
assert_eq!(f.sample(1.0), Some(1.0));
assert_eq!(f.sample(2.0), None);
sample_clamped will clamp the parameter to the unit interval, so it
always returns a value.
# use bevy_math::prelude::*;
# use bevy_curve::prelude::*;
# let f = EaseFunction::SmoothStep;
assert_eq!(f.sample_clamped(-1.0), 0.0);
assert_eq!(f.sample_clamped(0.0), 0.0);
assert_eq!(f.sample_clamped(0.5), 0.5);
assert_eq!(f.sample_clamped(1.0), 1.0);
assert_eq!(f.sample_clamped(2.0), 1.0);
Constructs a new instance of the type from a table of its fields.
Missing fields are filled in from the type’s default, then from the defaults of the fields’ types.
For enums use the variant key, or construct from a variant directly.
The rectangle is axis aligned, and defined by its minimum and maximum coordinates,
stored in Rect::min and Rect::max, respectively. The minimum/maximum invariant
must be upheld by the user when directly assigning the fields, otherwise some methods
produce invalid results. It is generally recommended to use one of the constructor
methods instead, which will ensure this invariant is met, unless you already have
the minimum and maximum corners.
# use approx::assert_relative_eq;
# use bevy_math::{Rot2, Vec2};
use std::f32::consts::PI;
// Create rotations from counterclockwise angles in radians or degrees
let rotation1 = Rot2::radians(PI / 2.0);
let rotation2 = Rot2::degrees(45.0);
// Get the angle back as radians or degrees
assert_eq!(rotation1.as_degrees(), 90.0);
assert_eq!(rotation2.as_radians(), PI / 4.0);
// "Add" rotations together using `*`
#[cfg(feature = "approx")]
assert_relative_eq!(rotation1 * rotation2, Rot2::degrees(135.0));
// Rotate vectors
#[cfg(feature = "approx")]
assert_relative_eq!(rotation1 * Vec2::X, Vec2::Y);
Constructs a new instance of the type from a table of its fields.
Missing fields are filled in from the type’s default, then from the defaults of the fields’ types.
For enums use the variant key, or construct from a variant directly.
The distance along the ray to the line through plane_origin with normal plane_normal, or nil if the ray never reaches it. Errors if the normal is zero or not finite.
The distance along the ray to the plane through plane_origin with normal plane_normal, or nil if the ray never reaches it. Errors if the normal is zero or not finite.
Constructs a new instance of the type from a table of its fields.
Missing fields are filled in from the type’s default, then from the defaults of the fields’ types.
For enums use the variant key, or construct from a variant directly.
Constructs a new instance of the type from a table of its fields.
Missing fields are filled in from the type’s default, then from the defaults of the fields’ types.
For enums use the variant key, or construct from a variant directly.
Constructs a new instance of the type from a table of its fields.
Missing fields are filled in from the type’s default, then from the defaults of the fields’ types.
For enums use the variant key, or construct from a variant directly.
Creates a new vector. ```multi-lang-example,lua local Vec2 = import_namespace(“math”).Vec2 local v = Vec2.new(3, 4) assert(v:length() == 5) ``` ```multi-lang-example,rune let Vec2 = import_...
Creates a new vector. ```multi-lang-example,lua local Vec3 = import_namespace(“math”).Vec3 local v = Vec3.new(1, 2, 3) assert(v + v == Vec3.new(2, 4, 6)) ``` ```multi-lang-example,rune let Vec3 = import_...
Constructs a new instance of the type from a table of its fields.
Missing fields are filled in from the type’s default, then from the defaults of the fields’ types.
For enums use the variant key, or construct from a variant directly.
Constructs a new instance of the type from a table of its fields.
Missing fields are filled in from the type’s default, then from the defaults of the fields’ types.
For enums use the variant key, or construct from a variant directly.
The rectangle is axis aligned, and defined by its minimum and maximum coordinates,
stored in URect::min and URect::max, respectively. The minimum/maximum invariant
must be upheld by the user when directly assigning the fields, otherwise some methods
produce invalid results. It is generally recommended to use one of the constructor
methods instead, which will ensure this invariant is met, unless you already have
the minimum and maximum corners.
Constructs a new instance of the type from a table of its fields.
Missing fields are filled in from the type’s default, then from the defaults of the fields’ types.
For enums use the variant key, or construct from a variant directly.
Constructs a new instance of the type from a table of its fields.
Missing fields are filled in from the type’s default, then from the defaults of the fields’ types.
For enums use the variant key, or construct from a variant directly.
Constructs a new instance of the type from a table of its fields.
Missing fields are filled in from the type’s default, then from the defaults of the fields’ types.
For enums use the variant key, or construct from a variant directly.
Constructs a new instance of the type from a table of its fields.
Missing fields are filled in from the type’s default, then from the defaults of the fields’ types.
For enums use the variant key, or construct from a variant directly.
Constructs a new instance of the type from a table of its fields.
Missing fields are filled in from the type’s default, then from the defaults of the fields’ types.
For enums use the variant key, or construct from a variant directly.
Constructs a new instance of the type from a table of its fields.
Missing fields are filled in from the type’s default, then from the defaults of the fields’ types.
For enums use the variant key, or construct from a variant directly.
Constructs a new instance of the type from a table of its fields.
Missing fields are filled in from the type’s default, then from the defaults of the fields’ types.
For enums use the variant key, or construct from a variant directly.
Constructs a new instance of the type from a table of its fields.
Missing fields are filled in from the type’s default, then from the defaults of the fields’ types.
For enums use the variant key, or construct from a variant directly.
Constructs a new instance of the type from a table of its fields.
Missing fields are filled in from the type’s default, then from the defaults of the fields’ types.
For enums use the variant key, or construct from a variant directly.
An image, optimized for usage in rendering. ## Remote Inspection To transmit an [`Image`] between two running Bevy apps, e.g. through BRP, use `SerializedImage`, which requires the `serialize` feature. This type is only meant for short-term transmission between same versions and should not be stored anywhere.
When added to a camera with [`MotionVectorPrepass`], disables the automatic background motion vector prepass. By default, any camera with [`MotionVectorPrepass`]...
An array of indices into the [`VertexAttributeValues`](super::VertexAttributeValues) for a mesh. It describes the order in which the vertex attributes should be joined into faces.
The [parallax mapping] method to use to compute depth based on the material’s [`depth_map`]. Parallax Mapping uses a depth map texture to give the illusion of depth variation on a mesh surface that is geometrically flat. See the `parallax_...
A storage buffer that is prepared as a [`RenderAsset`] and uploaded to the GPU. This buffer primarily exists in order to be embedded into a material that implements the [`bevy_render_macros::AsBindGroup`]...
To transmit an [Image] between two running Bevy apps, e.g. through BRP, use
SerializedImage, which requires the serialize feature.
This type is only meant for short-term transmission between same versions and should not be stored anywhere.
Constructs a new instance of the type from a table of its fields.
Missing fields are filled in from the type’s default, then from the defaults of the fields’ types.
For enums use the variant key, or construct from a variant directly.
A 3D object made out of vertices representing triangles, lines, or points,
with “attribute” values for each vertex.
Meshes can be automatically generated by a bevy AssetLoader (generally by loading a Gltf file),
or by converting a primitive using into.
It is also possible to create one manually. They can be edited after creation.
Meshes can be rendered with a Mesh2d and MeshMaterial2d
or Mesh3d and MeshMaterial3d for 2D and 3D respectively.
A [Mesh] in Bevy is equivalent to a “primitive” in the glTF format, for a
glTF Mesh representation, see GltfMesh.
The following function will construct a flat mesh, to be rendered with a
StandardMaterial or ColorMaterial:
# use bevy_mesh::{Mesh, Indices, PrimitiveTopology};
# use bevy_asset::RenderAssetUsages;
fn create_simple_parallelogram() -> Mesh {
// Create a new mesh using a triangle list topology, where each set of 3 vertices composes a triangle.
Mesh::new(PrimitiveTopology::TriangleList, RenderAssetUsages::default())
// Add 4 vertices, each with its own position attribute (coordinate in
// 3D space), for each of the corners of the parallelogram.
.with_inserted_attribute(
Mesh::ATTRIBUTE_POSITION,
vec![[0.0, 0.0, 0.0], [1.0, 2.0, 0.0], [2.0, 2.0, 0.0], [1.0, 0.0, 0.0]]
)
// Assign a UV coordinate to each vertex.
.with_inserted_attribute(
Mesh::ATTRIBUTE_UV_0,
vec![[0.0, 1.0], [0.5, 0.0], [1.0, 0.0], [0.5, 1.0]]
)
// Assign normals (everything points outwards)
.with_inserted_attribute(
Mesh::ATTRIBUTE_NORMAL,
vec![[0.0, 0.0, 1.0], [0.0, 0.0, 1.0], [0.0, 0.0, 1.0], [0.0, 0.0, 1.0]]
)
// After defining all the vertices and their attributes, build each triangle using the
// indices of the vertices that make it up in a counter-clockwise order.
.with_inserted_indices(Indices::U32(vec![
// First triangle
0, 3, 1,
// Second triangle
1, 3, 2
]))
}
You can see how it looks like here,
used in a Mesh3d with a square bevy logo texture, with added axis, points,
lines and text for clarity.
For further visualization, explanation, and examples, see the built-in Bevy examples,
and the implementation of the built-in shapes.
In particular, generate_custom_mesh
teaches you to access and modify the attributes of a [Mesh] after creating it.
UV maps in Bevy start at the top-left, see ATTRIBUTE_UV_0,
other APIs can have other conventions, OpenGL starts at bottom-left.
It is possible and sometimes useful for multiple vertices to have the same
position attribute value,
it’s a common technique in 3D modeling for complex UV mapping or other calculations.
Bevy performs frustum culling based on the Aabb of meshes, which is calculated
and added automatically for new meshes only. If a mesh is modified, the entity’s Aabb
needs to be updated manually or deleted so that it is re-calculated.
To render correctly with StandardMaterial, a mesh needs to have properly defined:
UVs: Bevy needs to know how to map a texture onto the mesh
(also true for ColorMaterial).
Normals: Bevy needs to know how light interacts with your mesh.
[0.0, 0.0, 1.0] is very common for simple flat meshes on the XY plane,
because simple meshes are smooth and they don’t require complex light calculations.
Vertex winding order: by default, StandardMaterial.cull_mode is Some(Face::Back),
which means that Bevy would only render the “front” of each triangle, which
is the side of the triangle from where the vertices appear in a counter-clockwise order.
To transmit a [Mesh] between two running Bevy apps, e.g. through BRP, use [SerializedMesh].
This type is only meant for short-term transmission between same versions and should not be stored anywhere.
Constructs a new instance of the type from a table of its fields.
Missing fields are filled in from the type’s default, then from the defaults of the fields’ types.
For enums use the variant key, or construct from a variant directly.
May be created directly from a [Color] or an [Image].
The StandardMaterial can be extended with more data and custom
shaders using [ExtendedMaterial]. Examples of how to do this can
be found in the Bevy examples.
Constructs a new instance of the type from a table of its fields.
Missing fields are filled in from the type’s default, then from the defaults of the fields’ types.
For enums use the variant key, or construct from a variant directly.
When added to a camera with [MotionVectorPrepass], disables the automatic background motion
vector prepass.
By default, any camera with [MotionVectorPrepass] will automatically write camera-rotation
motion vectors for background pixels (those with no geometry, i.e. depth == 0 in reversed-Z).
Add this component to opt out, for example if you are writing custom background motion vectors
for your own effect.
Constructs a new instance of the type from a table of its fields.
Missing fields are filled in from the type’s default, then from the defaults of the fields’ types.
For enums use the variant key, or construct from a variant directly.
An index into a [TextureAtlasLayout], which corresponds to a specific section of a texture.
It stores a handle to [TextureAtlasLayout] and the index of the current section of the atlas.
The texture atlas contains various sections of a given texture, allowing users to have a single
image file for either sprite animation or global mapping.
You can change the texture index of the atlas to animate the sprite or display only a section of the texture
for efficient rendering of related game objects.
Constructs a new instance of the type from a table of its fields.
Missing fields are filled in from the type’s default, then from the defaults of the fields’ types.
For enums use the variant key, or construct from a variant directly.
Constructs a new instance of the type from a table of its fields.
Missing fields are filled in from the type’s default, then from the defaults of the fields’ types.
For enums use the variant key, or construct from a variant directly.
The forward rendering main pass draws each mesh entity and shades it according to its
corresponding material and the lights that affect it. Some render features like Screen Space
Ambient Occlusion require running depth and normal prepasses, that are ‘deferred’-like
prepasses over all mesh entities to populate depth and normal textures. This means that when
using render features that require running prepasses, multiple passes over all visible geometry
are required. This can be slow if there is a lot of geometry that cannot be batched into few
draws.
Deferred rendering runs a prepass to gather not only geometric information like depth and
normals, but also all the material properties like base color, emissive color, reflectance,
metalness, etc, and writes them into a deferred ‘g-buffer’ texture. The deferred main pass is
then a fullscreen pass that reads data from these textures and executes shading. This allows
for one pass over geometry, but is at the cost of not being able to use MSAA, and has heavier
bandwidth usage which can be unsuitable for low end mobile or other bandwidth-constrained devices.
If a material indicates OpaqueRendererMethod::Auto, DefaultOpaqueRendererMethod will be used.
Constructs a new instance of the type from a table of its fields.
Missing fields are filled in from the type’s default, then from the defaults of the fields’ types.
For enums use the variant key, or construct from a variant directly.
Constructs a new instance of the type from a table of its fields.
Missing fields are filled in from the type’s default, then from the defaults of the fields’ types.
For enums use the variant key, or construct from a variant directly.
UV0 and UV1 will be Unorm16x2. UVs are remapped based on their min/max values so they can go beyond [0, 1], though a larger range will reduce precision.
Constructs a new instance of the type from a table of its fields.
Missing fields are filled in from the type’s default, then from the defaults of the fields’ types.
For enums use the variant key, or construct from a variant directly.
Constructs a new instance of the type from a table of its fields.
Missing fields are filled in from the type’s default, then from the defaults of the fields’ types.
For enums use the variant key, or construct from a variant directly.
Constructs a new instance of the type from a table of its fields.
Missing fields are filled in from the type’s default, then from the defaults of the fields’ types.
For enums use the variant key, or construct from a variant directly.
Constructs a new instance of the type from a table of its fields.
Missing fields are filled in from the type’s default, then from the defaults of the fields’ types.
For enums use the variant key, or construct from a variant directly.
Constructs a new instance of the type from a table of its fields.
Missing fields are filled in from the type’s default, then from the defaults of the fields’ types.
For enums use the variant key, or construct from a variant directly.
Constructs a new instance of the type from a table of its fields.
Missing fields are filled in from the type’s default, then from the defaults of the fields’ types.
For enums use the variant key, or construct from a variant directly.
Constructs a new instance of the type from a table of its fields.
Missing fields are filled in from the type’s default, then from the defaults of the fields’ types.
For enums use the variant key, or construct from a variant directly.
Constructs a new instance of the type from a table of its fields.
Missing fields are filled in from the type’s default, then from the defaults of the fields’ types.
For enums use the variant key, or construct from a variant directly.
A storage buffer that is prepared as a [RenderAsset] and uploaded to the GPU.
This buffer primarily exists in order to be embedded into a material that
implements the [bevy_render_macros::AsBindGroup] trait. Compared to
embedding a raw [Buffer], [ShaderBuffer] has the advantage that the
buffer can be resized without regenerating the materials that embed it.
Constructs a new instance of the type from a table of its fields.
Missing fields are filled in from the type’s default, then from the defaults of the fields’ types.
For enums use the variant key, or construct from a variant directly.
A component that signals to the renderer to capture a screenshot this frame.
This component should be spawned on a new entity with an observer that will trigger
with [ScreenshotCaptured] when the screenshot is ready.
Screenshots are captured asynchronously and may not be available immediately after the frame
that the component is spawned on. The observer should be used to handle the screenshot when it
is ready.
Note that the screenshot entity will be despawned after the screenshot is captured and the
observer is triggered.
Constructs a new instance of the type from a table of its fields.
Missing fields are filled in from the type’s default, then from the defaults of the fields’ types.
For enums use the variant key, or construct from a variant directly.
Constructs a new instance of the type from a table of its fields.
Missing fields are filled in from the type’s default, then from the defaults of the fields’ types.
For enums use the variant key, or construct from a variant directly.
Constructs a new instance of the type from a table of its fields.
Missing fields are filled in from the type’s default, then from the defaults of the fields’ types.
For enums use the variant key, or construct from a variant directly.
Constructs a new instance of the type from a table of its fields.
Missing fields are filled in from the type’s default, then from the defaults of the fields’ types.
For enums use the variant key, or construct from a variant directly.
Constructs a new instance of the type from a table of its fields.
Missing fields are filled in from the type’s default, then from the defaults of the fields’ types.
For enums use the variant key, or construct from a variant directly.
Constructs a new instance of the type from a table of its fields.
Missing fields are filled in from the type’s default, then from the defaults of the fields’ types.
For enums use the variant key, or construct from a variant directly.
Stores a map used to lookup the position of a texture in a [`TextureAtlas`]. This can be used to either use and look up a specific section of a texture, or animate frame-by-frame as a sprite sheet. Optionally it can store a mapping from sub texture handles to the related area index (see [`TextureAtlasBuilder`]...
A [material](Material2d) used for rendering a [`Mesh2d`]. See [`Material2d`] for general information about 2D materials and how to implement your own materials. # Example ``` # use bevy_sprite_render::{ColorMaterial, MeshMaterial2d}; # use bevy_ecs::prelude::*; # use bevy_mesh::{Mesh, Mesh2d}; # use bevy_color::palettes::basic::RED; # use bevy_asset::Assets; # use bevy_shape::Circle; # // Spawn an entity with a mesh using `ColorMaterial`. fn setup( mut commands: Commands, mut meshes: ResMut<Assets>, mut materials: ResMut<Assets>, ) { commands.spawn(( Mesh2d(meshes.add(Circle::new(50.0))), MeshMaterial2d(materials.add(ColorMaterial::from_color(RED))), )); } ``` [`MeshMaterial2d`]...
A [material](Material2d) used for rendering a [`Mesh2d`]. See [`Material2d`] for general information about 2D materials and how to implement your own materials. # Example ``` # use bevy_sprite_render::{ColorMaterial, MeshMaterial2d}; # use bevy_ecs::prelude::*; # use bevy_mesh::{Mesh, Mesh2d}; # use bevy_color::palettes::basic::RED; # use bevy_asset::Assets; # use bevy_shape::Circle; # // Spawn an entity with a mesh using `ColorMaterial`. fn setup( mut commands: Commands, mut meshes: ResMut<Assets>, mut materials: ResMut<Assets>, ) { commands.spawn(( Mesh2d(meshes.add(Circle::new(50.0))), MeshMaterial2d(materials.add(ColorMaterial::from_color(RED))), )); } ``` [`MeshMaterial2d`]...
Stores a map used to lookup the position of a texture in a [TextureAtlas].
This can be used to either use and look up a specific section of a texture, or animate frame-by-frame as a sprite sheet.
Optionally it can store a mapping from sub texture handles to the related area index (see
TextureAtlasBuilder).
Constructs a new instance of the type from a table of its fields.
Missing fields are filled in from the type’s default, then from the defaults of the fields’ types.
For enums use the variant key, or construct from a variant directly.
Constructs a new instance of the type from a table of its fields.
Missing fields are filled in from the type’s default, then from the defaults of the fields’ types.
For enums use the variant key, or construct from a variant directly.
Constructs a new instance of the type from a table of its fields.
Missing fields are filled in from the type’s default, then from the defaults of the fields’ types.
For enums use the variant key, or construct from a variant directly.
An enum describing how a sprite’s alpha channel will be handled.
The base color being modified is that of the texture after tinting.
The default is Blend.
Constructs a new instance of the type from a table of its fields.
Missing fields are filled in from the type’s default, then from the defaults of the fields’ types.
For enums use the variant key, or construct from a variant directly.
Constructs a new instance of the type from a table of its fields.
Missing fields are filled in from the type’s default, then from the defaults of the fields’ types.
For enums use the variant key, or construct from a variant directly.
Constructs a new instance of the type from a table of its fields.
Missing fields are filled in from the type’s default, then from the defaults of the fields’ types.
For enums use the variant key, or construct from a variant directly.
Constructs a new instance of the type from a table of its fields.
Missing fields are filled in from the type’s default, then from the defaults of the fields’ types.
For enums use the variant key, or construct from a variant directly.
Constructs a new instance of the type from a table of its fields.
Missing fields are filled in from the type’s default, then from the defaults of the fields’ types.
For enums use the variant key, or construct from a variant directly.
Slices a texture using the 9-slicing technique. This allows to reuse an image at various sizes
without needing to prepare multiple assets. The associated texture will be split into nine portions,
so that on resize the different portions scale or tile in different ways to keep the texture in proportion.
For example, when resizing a 9-sliced texture the corners will remain unscaled while the other
sections will be scaled or tiled.
Constructs a new instance of the type from a table of its fields.
Missing fields are filled in from the type’s default, then from the defaults of the fields’ types.
For enums use the variant key, or construct from a variant directly.
Constructs a new instance of the type from a table of its fields.
Missing fields are filled in from the type’s default, then from the defaults of the fields’ types.
For enums use the variant key, or construct from a variant directly.
Sets how a 2d material’s base color alpha channel is used for transparency.
Currently, this only works with [Mesh2d]. Sprites are always transparent.
This is very similar to [AlphaMode] but this only applies to 2d meshes.
We use a separate type because 2d doesn’t support all the transparency modes that 3d does.
Constructs a new instance of the type from a table of its fields.
Missing fields are filled in from the type’s default, then from the defaults of the fields’ types.
For enums use the variant key, or construct from a variant directly.
Constructs a new instance of the type from a table of its fields.
Missing fields are filled in from the type’s default, then from the defaults of the fields’ types.
For enums use the variant key, or construct from a variant directly.
# use bevy_sprite_render::{ColorMaterial, MeshMaterial2d};
# use bevy_ecs::prelude::*;
# use bevy_mesh::{Mesh, Mesh2d};
# use bevy_color::palettes::basic::RED;
# use bevy_asset::Assets;
# use bevy_shape::Circle;
#
// Spawn an entity with a mesh using `ColorMaterial`.
fn setup(
mut commands: Commands,
mut meshes: ResMut<Assets<Mesh>>,
mut materials: ResMut<Assets<ColorMaterial>>,
) {
commands.spawn((
Mesh2d(meshes.add(Circle::new(50.0))),
MeshMaterial2d(materials.add(ColorMaterial::from_color(RED))),
));
}
Constructs a new instance of the type from a table of its fields.
Missing fields are filled in from the type’s default, then from the defaults of the fields’ types.
For enums use the variant key, or construct from a variant directly.
# use bevy_sprite_render::{ColorMaterial, MeshMaterial2d};
# use bevy_ecs::prelude::*;
# use bevy_mesh::{Mesh, Mesh2d};
# use bevy_color::palettes::basic::RED;
# use bevy_asset::Assets;
# use bevy_shape::Circle;
#
// Spawn an entity with a mesh using `ColorMaterial`.
fn setup(
mut commands: Commands,
mut meshes: ResMut<Assets<Mesh>>,
mut materials: ResMut<Assets<ColorMaterial>>,
) {
commands.spawn((
Mesh2d(meshes.add(Circle::new(50.0))),
MeshMaterial2d(materials.add(ColorMaterial::from_color(RED))),
));
}
Constructs a new instance of the type from a table of its fields.
Missing fields are filled in from the type’s default, then from the defaults of the fields’ types.
For enums use the variant key, or construct from a variant directly.
`TextFont` determines the style of a text span within a [`ComputedTextBlock`], specifically the font face, the font size, the line height, and the antialiasing method.
The top-level UI text component. Adding [`Text`] to an entity will pull in required components for setting up a UI text node. The string in this component is the first ‘text span’ in a hierarchy of text spans that are collected into a [`ComputedTextBlock`]...
Determines how the font face for a text sections is selected. A [`FontSource`] can be a handle to a font asset, a font family name, a CSS font-family list, an ordered family list, or a generic font category that is resolved using Parley’s font database. Font family fallback (selection of a font when the requested font is not found) is automatically handled by [`parley::fontique`]...
Adding Text2d to an entity will pull in required components for setting up 2d text.
Example usage.
The string in this component is the first ‘text span’ in a hierarchy of text spans that are collected into
a [ComputedTextBlock]. See TextSpan for the component used by children of entities with [Text2d].
With Text2d the justify field of [TextLayout] only affects the internal alignment of a block of text and not its
relative position, which is controlled by the [Anchor] component.
This means that for a block of text consisting of only one line that doesn’t wrap, the justify field will have no effect.
# use bevy_asset::Handle;
# use bevy_color::Color;
# use bevy_color::palettes::basic::BLUE;
# use bevy_ecs::world::World;
# use bevy_text::{Font, FontSize, Justify, TextLayout, TextFont, TextColor, TextSpan};
# use bevy_sprite::Text2d;
#
# let font_handle: Handle<Font> = Default::default();
# let mut world = World::default();
#
// Basic usage.
world.spawn(Text2d::new("hello world!"));
// With non-default style.
world.spawn((
Text2d::new("hello world!"),
TextFont {
font: font_handle.clone().into(),
font_size: FontSize::Px(60.0),
..Default::default()
},
TextColor(BLUE.into()),
));
// With text justification.
world.spawn((
Text2d::new("hello world\nand bevy!"),
TextLayout::justify(Justify::Center)
));
// With spans
world.spawn(Text2d::new("hello ")).with_children(|parent| {
parent.spawn(TextSpan::new("world"));
parent.spawn((TextSpan::new("!"), TextColor(BLUE.into())));
});
Viewport-based FontSize variants for Text2d are resolved based on the primary window’s logical size.
TextFont determines the style of a text span within a [ComputedTextBlock], specifically
the font face, the font size, the line height, and the antialiasing method.
Creates a text font with the default font face and the given size in logical pixels. ```multi-lang-example,lua local TextFont = import_namespace(“text”).TextFont local font = TextFont.from_font_size(20.0) assert(font:with_font_size(30.0) ~= nil) ``` ```multi-lang-example,rune let TextFont = import_...
Constructs a new instance of the type from a table of its fields.
Missing fields are filled in from the type’s default, then from the defaults of the fields’ types.
For enums use the variant key, or construct from a variant directly.
Component with text format settings for a block of text.
A block of text is composed of text spans, which each have a separate string value and [TextFont]. Text
spans associated with a text block are collected into [ComputedTextBlock] for layout, and then inserted
to [TextLayoutInfo] for rendering.
See Text2d in bevy_sprite for the core component of 2d text, and Text in bevy_ui for UI text.
Constructs a new instance of the type from a table of its fields.
Missing fields are filled in from the type’s default, then from the defaults of the fields’ types.
For enums use the variant key, or construct from a variant directly.
A TextSpan is only valid when it exists as a child of a parent that has either Text or
Text2d. The parent’s Text / Text2d component contains the base text content. Any children
with TextSpan extend this text by appending their content to the parent’s text in sequence to
form a [ComputedTextBlock]. The parent’s [TextLayout] determines the layout of the block
but each node has its own [TextFont] and [TextColor].
Adding [Text] to an entity will pull in required components for setting up a UI text node.
The string in this component is the first ‘text span’ in a hierarchy of text spans that are collected into
a [ComputedTextBlock]. See TextSpan for the component used by children of entities with [Text].
# use bevy_asset::Handle;
# use bevy_color::Color;
# use bevy_color::palettes::basic::BLUE;
# use bevy_ecs::world::World;
# use bevy_text::{Font, FontSize, Justify, TextLayout, TextFont, TextColor, TextSpan};
# use bevy_ui::prelude::Text;
#
# let font_handle: Handle<Font> = Default::default();
# let mut world = World::default();
#
// Basic usage.
world.spawn(Text::new("hello world!"));
// With non-default style.
world.spawn((
Text::new("hello world!"),
TextFont {
font: font_handle.clone().into(),
font_size: FontSize::Px(60.0),
..Default::default()
},
TextColor(BLUE.into()),
));
// With text justification.
world.spawn((
Text::new("hello world\nand bevy!"),
TextLayout::justify(Justify::Center)
));
// With spans
world.spawn(Text::new("hello ")).with_children(|parent| {
parent.spawn(TextSpan::new("world"));
parent.spawn((TextSpan::new("!"), TextColor(BLUE.into())));
});
Creates a new text component holding `text`. ```multi-lang-example,lua local Text = import_namespace(“text”).Text local text = Text.new(“hi”) assert(text:as_str() == “hi”) `...
The font size, in logical pixels, used to resolve Val::Em values in a UI node.
Em units are relative to the font size of the node they sit on, and this is that
font size made concrete. Required by Node, so every UI node has one.
If the node has a TextFont, the EmSize is derived from it whenever the TextFont,
RemSize or render-target info changes; until then any value you place on it persists.
If it does not have a TextFont, the value is yours to set, for example by an app-level
propagation system. EmSize defaults to [DEFAULT_REM_SIZE_PX], which matches the default
[RemSize] but does not track changes to it. Use Val::Rem instead for values that
should follow the root font size.
Removing a TextFont leaves the last derived value in place.
Constructs a new instance of the type from a table of its fields.
Missing fields are filled in from the type’s default, then from the defaults of the fields’ types.
For enums use the variant key, or construct from a variant directly.
Constructs a new instance of the type from a table of its fields.
Missing fields are filled in from the type’s default, then from the defaults of the fields’ types.
For enums use the variant key, or construct from a variant directly.
OpenType features for .otf fonts that support them.
Examples features include ligatures, small-caps, and fractional number display. For the complete
list of OpenType features, see the spec at
<https://learn.microsoft.com/en-us/typography/opentype/spec/featurelist>.
use bevy_text::{FontFeatureTag, FontFeatures};
// Create using the builder
let font_features = FontFeatures::builder()
.enable(FontFeatureTag::STANDARD_LIGATURES)
.set(FontFeatureTag::WEIGHT, 300)
.build();
// Create from a list
let more_font_features: FontFeatures = [
FontFeatureTag::STANDARD_LIGATURES,
FontFeatureTag::OLDSTYLE_FIGURES,
FontFeatureTag::TABULAR_FIGURES
].into();
Constructs a new instance of the type from a table of its fields.
Missing fields are filled in from the type’s default, then from the defaults of the fields’ types.
For enums use the variant key, or construct from a variant directly.
Constructs a new instance of the type from a table of its fields.
Missing fields are filled in from the type’s default, then from the defaults of the fields’ types.
For enums use the variant key, or construct from a variant directly.
Determines which antialiasing method to use when rendering text. By default, text is
rendered with grayscale antialiasing, but this can be changed to achieve a pixelated look.
Note: Subpixel antialiasing is not currently supported.
Constructs a new instance of the type from a table of its fields.
Missing fields are filled in from the type’s default, then from the defaults of the fields’ types.
For enums use the variant key, or construct from a variant directly.
Determines how the font face for a text sections is selected.
A [FontSource] can be a handle to a font asset, a font family name,
a CSS font-family list, an ordered family list, or a generic font category
that is resolved using Parley’s font database.
Font family fallback (selection of a font when the requested font is not found)
is automatically handled by [parley::fontique].
Be sure to enable the parley/system feature for automatic discovery of system fonts.
Generally speaking, these fallbacks are OS-specific,
and do not require manual configuration.
You can check which font family is used for a given [FontSource]
by calling FontCx::get_family.
Constructs a new instance of the type from a table of its fields.
Missing fields are filled in from the type’s default, then from the defaults of the fields’ types.
For enums use the variant key, or construct from a variant directly.
Constructs a new instance of the type from a table of its fields.
Missing fields are filled in from the type’s default, then from the defaults of the fields’ types.
For enums use the variant key, or construct from a variant directly.
Constructs a new instance of the type from a table of its fields.
Missing fields are filled in from the type’s default, then from the defaults of the fields’ types.
For enums use the variant key, or construct from a variant directly.
OpenType font variations for variable fonts that support them.
Variable fonts expose named axes (e.g. wght, FILL) that accept continuous f32 values.
This is distinct from [FontFeatures], which mainly controls on/off OpenType layout features.
Constructs a new instance of the type from a table of its fields.
Missing fields are filled in from the type’s default, then from the defaults of the fields’ types.
For enums use the variant key, or construct from a variant directly.
Valid font weights range from 1 to 1000, inclusive.
Weights above 1000 are clamped to 1000.
A weight of 0 is treated as [FontWeight::DEFAULT].
Legacy names from when most fonts weren’t variable fonts
are included as const values, but are misleading if
used in documentation and examples, as valid weights
for variable fonts are all of the numbers from 1-1000, and
not all fonts which are not variable fonts have those weights
supplied. If you use a custom font that supplies only specific
weights, that will be documented where you purchased the font.
Constructs a new instance of the type from a table of its fields.
Missing fields are filled in from the type’s default, then from the defaults of the fields’ types.
For enums use the variant key, or construct from a variant directly.
The visual width of a font as a ratio of its normal width, typically 0.5 to 2.0.
<https://docs.microsoft.com/en-us/typography/opentype/spec/os2#uswidthclass>
Constructs a new instance of the type from a table of its fields.
Missing fields are filled in from the type’s default, then from the defaults of the fields’ types.
For enums use the variant key, or construct from a variant directly.
Constructs a new instance of the type from a table of its fields.
Missing fields are filled in from the type’s default, then from the defaults of the fields’ types.
For enums use the variant key, or construct from a variant directly.
Constructs a new instance of the type from a table of its fields.
Missing fields are filled in from the type’s default, then from the defaults of the fields’ types.
For enums use the variant key, or construct from a variant directly.
Constructs a new instance of the type from a table of its fields.
Missing fields are filled in from the type’s default, then from the defaults of the fields’ types.
For enums use the variant key, or construct from a variant directly.
Constructs a new instance of the type from a table of its fields.
Missing fields are filled in from the type’s default, then from the defaults of the fields’ types.
For enums use the variant key, or construct from a variant directly.
The fixed timestep game clock following virtual time. A specialization of the [`Time`] structure. **For method documentation, see [`Time#impl-Time`]...
Real time clock representing elapsed wall clock time. A specialization of the [`Time`] structure. **For method documentation, see [`Time#impl-Time`]...
Constructs a new instance of the type from a table of its fields.
Missing fields are filled in from the type’s default, then from the defaults of the fields’ types.
For enums use the variant key, or construct from a variant directly.
Repeating timers might elapse multiple times per frame if the time is advanced by more than the timer duration.
You can check how many times a timer elapsed each tick with [Timer::times_finished_this_tick].
For non-repeating timers, this will always be 0 or 1.
A timer which runs for `secs` seconds, in `mode`. Errors if `secs` is negative or not finite. ```multi-lang-example,lua local Timer = import_namespace(“time”).Timer local TimerMode = import_namespace(“time”).TimerMode local timer = Timer.from_seconds(2, TimerMode.Repeating) timer:tick(1) assert(not timer:finished()) timer:tick(1.5) assert(timer:finished()) ``` ```multi-lang-example,rune let Timer = import_...
Constructs a new instance of the type from a table of its fields.
Missing fields are filled in from the type’s default, then from the defaults of the fields’ types.
For enums use the variant key, or construct from a variant directly.
Time<Virtual> tracks virtual game time that may
be paused or scaled.
Time<Fixed> tracks fixed timesteps based on
virtual time.
[Time] is a generic clock that corresponds to “current” or “default”
time for systems. It contains Time<Virtual>
except inside the FixedMain schedule when it
contains Time<Fixed>.
The time elapsed since the previous time this clock was advanced is saved as
delta() and the total amount of time the clock has advanced
is saved as elapsed(). Both are represented as exact
[Duration] values with fixed nanosecond precision. The clock does not
support time moving backwards, but it can be updated with [Duration::ZERO]
which will set delta() to zero.
Since elapsed_secs() will grow constantly and
is f32, it will exhibit gradual precision loss. For applications that
require an f32 value but suffer from gradual precision loss there is
elapsed_secs_wrapped() available. The
same wrapped value is also available as [Duration] and f64 for
consistency. The wrap period is by default 1 hour, and can be set by
set_wrap_period().
By default, any systems requiring current delta() or
elapsed() should use Res<Time> to access the default
time configured for the program. By default, this refers to
Time<Virtual> except during the
FixedMain schedule when it refers to
Time<Fixed>. This ensures your system can be used
either in Update or
FixedUpdate schedule depending on what is needed.
# use bevy_ecs::prelude::*;
# use bevy_time::prelude::*;
#
fn ambivalent_system(time: Res<Time>) {
println!("this how I see time: delta {:?}, elapsed {:?}", time.delta(), time.elapsed());
}
If your system needs to react based on real time (wall clock time), like for
user interfaces, it should use Res<Time<Real>>. The
delta() and elapsed() values will always
correspond to real time and will not be affected by pause, time scaling or
other tweaks.
# use bevy_ecs::prelude::*;
# use bevy_time::prelude::*;
#
fn real_time_system(time: Res<Time<Real>>) {
println!("this will always be real time: delta {:?}, elapsed {:?}", time.delta(), time.elapsed());
}
If your system specifically needs to access fixed timestep clock, even when
placed in Update schedule, you should use Res<Time<Fixed>>. The
delta() and elapsed() values will
correspond to the latest fixed timestep that has been run.
# use bevy_ecs::prelude::*;
# use bevy_time::prelude::*;
#
fn fixed_time_system(time: Res<Time<Fixed>>) {
println!("this will always be the last executed fixed timestep: delta {:?}, elapsed {:?}", time.delta(), time.elapsed());
}
Finally, if your system specifically needs to know the current virtual game
time, even if placed inside FixedUpdate, for
example to know if the game is was_paused() or to use
effective_speed(), you can use
Res<Time<Virtual>>. However, if the system is placed in
FixedUpdate, extra care must be used because your
system might be run multiple times with the same delta()
and elapsed() values as the virtual game time has not
changed between the iterations.
# use bevy_ecs::prelude::*;
# use bevy_time::prelude::*;
#
fn fixed_time_system(time: Res<Time<Virtual>>) {
println!("this will be virtual time for this update: delta {:?}, elapsed {:?}", time.delta(), time.elapsed());
println!("also the relative speed of the game is now {}", time.effective_speed());
}
If you need to change the settings for any of the clocks, for example to
pause() the game, you should use ResMut<Time<Virtual>>.
# use bevy_ecs::prelude::*;
# use bevy_time::prelude::*;
#
#[derive(Message)]
struct Pause(bool);
fn pause_system(mut time: ResMut<Time<Virtual>>, mut pause_reader: MessageReader<Pause>) {
for pause in pause_reader.read() {
if pause.0 {
time.pause();
} else {
time.unpause();
}
}
}
New custom clocks can be created by creating your own struct as a context
and passing it to new_with(). These clocks can be
inserted as resources as normal and then accessed by systems. You can use
the advance_by() or advance_to()
methods to move the clock forwards based on your own logic.
If you want to add methods for your time instance and they require access to
both your context and the generic time part, it’s probably simplest to add a
custom trait for them and implement it for Time<Custom>.
Your context struct will need to implement the [Default] trait because
[Time] structures support reflection. It also makes initialization trivial
by being able to call app.init_resource::<Time<Custom>>().
You can also replace the “generic” Time clock resource if the “default”
time for your game should not be the default virtual time provided. You can
get a “generic” snapshot of your clock by calling as_generic() and then
overwrite the [Time] resource with it. The default systems added by
TimePlugin will overwrite the [Time] clock during
First and FixedUpdate
schedules.
Constructs a new instance of the type from a table of its fields.
Missing fields are filled in from the type’s default, then from the defaults of the fields’ types.
For enums use the variant key, or construct from a variant directly.
Time<Virtual> tracks virtual game time that may
be paused or scaled.
Time<Fixed> tracks fixed timesteps based on
virtual time.
[Time] is a generic clock that corresponds to “current” or “default”
time for systems. It contains Time<Virtual>
except inside the FixedMain schedule when it
contains Time<Fixed>.
The time elapsed since the previous time this clock was advanced is saved as
delta() and the total amount of time the clock has advanced
is saved as elapsed(). Both are represented as exact
[Duration] values with fixed nanosecond precision. The clock does not
support time moving backwards, but it can be updated with [Duration::ZERO]
which will set delta() to zero.
Since elapsed_secs() will grow constantly and
is f32, it will exhibit gradual precision loss. For applications that
require an f32 value but suffer from gradual precision loss there is
elapsed_secs_wrapped() available. The
same wrapped value is also available as [Duration] and f64 for
consistency. The wrap period is by default 1 hour, and can be set by
set_wrap_period().
By default, any systems requiring current delta() or
elapsed() should use Res<Time> to access the default
time configured for the program. By default, this refers to
Time<Virtual> except during the
FixedMain schedule when it refers to
Time<Fixed>. This ensures your system can be used
either in Update or
FixedUpdate schedule depending on what is needed.
# use bevy_ecs::prelude::*;
# use bevy_time::prelude::*;
#
fn ambivalent_system(time: Res<Time>) {
println!("this how I see time: delta {:?}, elapsed {:?}", time.delta(), time.elapsed());
}
If your system needs to react based on real time (wall clock time), like for
user interfaces, it should use Res<Time<Real>>. The
delta() and elapsed() values will always
correspond to real time and will not be affected by pause, time scaling or
other tweaks.
# use bevy_ecs::prelude::*;
# use bevy_time::prelude::*;
#
fn real_time_system(time: Res<Time<Real>>) {
println!("this will always be real time: delta {:?}, elapsed {:?}", time.delta(), time.elapsed());
}
If your system specifically needs to access fixed timestep clock, even when
placed in Update schedule, you should use Res<Time<Fixed>>. The
delta() and elapsed() values will
correspond to the latest fixed timestep that has been run.
# use bevy_ecs::prelude::*;
# use bevy_time::prelude::*;
#
fn fixed_time_system(time: Res<Time<Fixed>>) {
println!("this will always be the last executed fixed timestep: delta {:?}, elapsed {:?}", time.delta(), time.elapsed());
}
Finally, if your system specifically needs to know the current virtual game
time, even if placed inside FixedUpdate, for
example to know if the game is was_paused() or to use
effective_speed(), you can use
Res<Time<Virtual>>. However, if the system is placed in
FixedUpdate, extra care must be used because your
system might be run multiple times with the same delta()
and elapsed() values as the virtual game time has not
changed between the iterations.
# use bevy_ecs::prelude::*;
# use bevy_time::prelude::*;
#
fn fixed_time_system(time: Res<Time<Virtual>>) {
println!("this will be virtual time for this update: delta {:?}, elapsed {:?}", time.delta(), time.elapsed());
println!("also the relative speed of the game is now {}", time.effective_speed());
}
If you need to change the settings for any of the clocks, for example to
pause() the game, you should use ResMut<Time<Virtual>>.
# use bevy_ecs::prelude::*;
# use bevy_time::prelude::*;
#
#[derive(Message)]
struct Pause(bool);
fn pause_system(mut time: ResMut<Time<Virtual>>, mut pause_reader: MessageReader<Pause>) {
for pause in pause_reader.read() {
if pause.0 {
time.pause();
} else {
time.unpause();
}
}
}
New custom clocks can be created by creating your own struct as a context
and passing it to new_with(). These clocks can be
inserted as resources as normal and then accessed by systems. You can use
the advance_by() or advance_to()
methods to move the clock forwards based on your own logic.
If you want to add methods for your time instance and they require access to
both your context and the generic time part, it’s probably simplest to add a
custom trait for them and implement it for Time<Custom>.
Your context struct will need to implement the [Default] trait because
[Time] structures support reflection. It also makes initialization trivial
by being able to call app.init_resource::<Time<Custom>>().
You can also replace the “generic” Time clock resource if the “default”
time for your game should not be the default virtual time provided. You can
get a “generic” snapshot of your clock by calling as_generic() and then
overwrite the [Time] resource with it. The default systems added by
TimePlugin will overwrite the [Time] clock during
First and FixedUpdate
schedules.
Constructs a new instance of the type from a table of its fields.
Missing fields are filled in from the type’s default, then from the defaults of the fields’ types.
For enums use the variant key, or construct from a variant directly.
Time<Virtual> tracks virtual game time that may
be paused or scaled.
Time<Fixed> tracks fixed timesteps based on
virtual time.
[Time] is a generic clock that corresponds to “current” or “default”
time for systems. It contains Time<Virtual>
except inside the FixedMain schedule when it
contains Time<Fixed>.
The time elapsed since the previous time this clock was advanced is saved as
delta() and the total amount of time the clock has advanced
is saved as elapsed(). Both are represented as exact
[Duration] values with fixed nanosecond precision. The clock does not
support time moving backwards, but it can be updated with [Duration::ZERO]
which will set delta() to zero.
Since elapsed_secs() will grow constantly and
is f32, it will exhibit gradual precision loss. For applications that
require an f32 value but suffer from gradual precision loss there is
elapsed_secs_wrapped() available. The
same wrapped value is also available as [Duration] and f64 for
consistency. The wrap period is by default 1 hour, and can be set by
set_wrap_period().
By default, any systems requiring current delta() or
elapsed() should use Res<Time> to access the default
time configured for the program. By default, this refers to
Time<Virtual> except during the
FixedMain schedule when it refers to
Time<Fixed>. This ensures your system can be used
either in Update or
FixedUpdate schedule depending on what is needed.
# use bevy_ecs::prelude::*;
# use bevy_time::prelude::*;
#
fn ambivalent_system(time: Res<Time>) {
println!("this how I see time: delta {:?}, elapsed {:?}", time.delta(), time.elapsed());
}
If your system needs to react based on real time (wall clock time), like for
user interfaces, it should use Res<Time<Real>>. The
delta() and elapsed() values will always
correspond to real time and will not be affected by pause, time scaling or
other tweaks.
# use bevy_ecs::prelude::*;
# use bevy_time::prelude::*;
#
fn real_time_system(time: Res<Time<Real>>) {
println!("this will always be real time: delta {:?}, elapsed {:?}", time.delta(), time.elapsed());
}
If your system specifically needs to access fixed timestep clock, even when
placed in Update schedule, you should use Res<Time<Fixed>>. The
delta() and elapsed() values will
correspond to the latest fixed timestep that has been run.
# use bevy_ecs::prelude::*;
# use bevy_time::prelude::*;
#
fn fixed_time_system(time: Res<Time<Fixed>>) {
println!("this will always be the last executed fixed timestep: delta {:?}, elapsed {:?}", time.delta(), time.elapsed());
}
Finally, if your system specifically needs to know the current virtual game
time, even if placed inside FixedUpdate, for
example to know if the game is was_paused() or to use
effective_speed(), you can use
Res<Time<Virtual>>. However, if the system is placed in
FixedUpdate, extra care must be used because your
system might be run multiple times with the same delta()
and elapsed() values as the virtual game time has not
changed between the iterations.
# use bevy_ecs::prelude::*;
# use bevy_time::prelude::*;
#
fn fixed_time_system(time: Res<Time<Virtual>>) {
println!("this will be virtual time for this update: delta {:?}, elapsed {:?}", time.delta(), time.elapsed());
println!("also the relative speed of the game is now {}", time.effective_speed());
}
If you need to change the settings for any of the clocks, for example to
pause() the game, you should use ResMut<Time<Virtual>>.
# use bevy_ecs::prelude::*;
# use bevy_time::prelude::*;
#
#[derive(Message)]
struct Pause(bool);
fn pause_system(mut time: ResMut<Time<Virtual>>, mut pause_reader: MessageReader<Pause>) {
for pause in pause_reader.read() {
if pause.0 {
time.pause();
} else {
time.unpause();
}
}
}
New custom clocks can be created by creating your own struct as a context
and passing it to new_with(). These clocks can be
inserted as resources as normal and then accessed by systems. You can use
the advance_by() or advance_to()
methods to move the clock forwards based on your own logic.
If you want to add methods for your time instance and they require access to
both your context and the generic time part, it’s probably simplest to add a
custom trait for them and implement it for Time<Custom>.
Your context struct will need to implement the [Default] trait because
[Time] structures support reflection. It also makes initialization trivial
by being able to call app.init_resource::<Time<Custom>>().
You can also replace the “generic” Time clock resource if the “default”
time for your game should not be the default virtual time provided. You can
get a “generic” snapshot of your clock by calling as_generic() and then
overwrite the [Time] resource with it. The default systems added by
TimePlugin will overwrite the [Time] clock during
First and FixedUpdate
schedules.
Constructs a new instance of the type from a table of its fields.
Missing fields are filled in from the type’s default, then from the defaults of the fields’ types.
For enums use the variant key, or construct from a variant directly.
Time<Virtual> tracks virtual game time that may
be paused or scaled.
Time<Fixed> tracks fixed timesteps based on
virtual time.
[Time] is a generic clock that corresponds to “current” or “default”
time for systems. It contains Time<Virtual>
except inside the FixedMain schedule when it
contains Time<Fixed>.
The time elapsed since the previous time this clock was advanced is saved as
delta() and the total amount of time the clock has advanced
is saved as elapsed(). Both are represented as exact
[Duration] values with fixed nanosecond precision. The clock does not
support time moving backwards, but it can be updated with [Duration::ZERO]
which will set delta() to zero.
Since elapsed_secs() will grow constantly and
is f32, it will exhibit gradual precision loss. For applications that
require an f32 value but suffer from gradual precision loss there is
elapsed_secs_wrapped() available. The
same wrapped value is also available as [Duration] and f64 for
consistency. The wrap period is by default 1 hour, and can be set by
set_wrap_period().
By default, any systems requiring current delta() or
elapsed() should use Res<Time> to access the default
time configured for the program. By default, this refers to
Time<Virtual> except during the
FixedMain schedule when it refers to
Time<Fixed>. This ensures your system can be used
either in Update or
FixedUpdate schedule depending on what is needed.
# use bevy_ecs::prelude::*;
# use bevy_time::prelude::*;
#
fn ambivalent_system(time: Res<Time>) {
println!("this how I see time: delta {:?}, elapsed {:?}", time.delta(), time.elapsed());
}
If your system needs to react based on real time (wall clock time), like for
user interfaces, it should use Res<Time<Real>>. The
delta() and elapsed() values will always
correspond to real time and will not be affected by pause, time scaling or
other tweaks.
# use bevy_ecs::prelude::*;
# use bevy_time::prelude::*;
#
fn real_time_system(time: Res<Time<Real>>) {
println!("this will always be real time: delta {:?}, elapsed {:?}", time.delta(), time.elapsed());
}
If your system specifically needs to access fixed timestep clock, even when
placed in Update schedule, you should use Res<Time<Fixed>>. The
delta() and elapsed() values will
correspond to the latest fixed timestep that has been run.
# use bevy_ecs::prelude::*;
# use bevy_time::prelude::*;
#
fn fixed_time_system(time: Res<Time<Fixed>>) {
println!("this will always be the last executed fixed timestep: delta {:?}, elapsed {:?}", time.delta(), time.elapsed());
}
Finally, if your system specifically needs to know the current virtual game
time, even if placed inside FixedUpdate, for
example to know if the game is was_paused() or to use
effective_speed(), you can use
Res<Time<Virtual>>. However, if the system is placed in
FixedUpdate, extra care must be used because your
system might be run multiple times with the same delta()
and elapsed() values as the virtual game time has not
changed between the iterations.
# use bevy_ecs::prelude::*;
# use bevy_time::prelude::*;
#
fn fixed_time_system(time: Res<Time<Virtual>>) {
println!("this will be virtual time for this update: delta {:?}, elapsed {:?}", time.delta(), time.elapsed());
println!("also the relative speed of the game is now {}", time.effective_speed());
}
If you need to change the settings for any of the clocks, for example to
pause() the game, you should use ResMut<Time<Virtual>>.
# use bevy_ecs::prelude::*;
# use bevy_time::prelude::*;
#
#[derive(Message)]
struct Pause(bool);
fn pause_system(mut time: ResMut<Time<Virtual>>, mut pause_reader: MessageReader<Pause>) {
for pause in pause_reader.read() {
if pause.0 {
time.pause();
} else {
time.unpause();
}
}
}
New custom clocks can be created by creating your own struct as a context
and passing it to new_with(). These clocks can be
inserted as resources as normal and then accessed by systems. You can use
the advance_by() or advance_to()
methods to move the clock forwards based on your own logic.
If you want to add methods for your time instance and they require access to
both your context and the generic time part, it’s probably simplest to add a
custom trait for them and implement it for Time<Custom>.
Your context struct will need to implement the [Default] trait because
[Time] structures support reflection. It also makes initialization trivial
by being able to call app.init_resource::<Time<Custom>>().
You can also replace the “generic” Time clock resource if the “default”
time for your game should not be the default virtual time provided. You can
get a “generic” snapshot of your clock by calling as_generic() and then
overwrite the [Time] resource with it. The default systems added by
TimePlugin will overwrite the [Time] clock during
First and FixedUpdate
schedules.
Constructs a new instance of the type from a table of its fields.
Missing fields are filled in from the type’s default, then from the defaults of the fields’ types.
For enums use the variant key, or construct from a variant directly.
The fixed timestep game clock following virtual time.
A specialization of the [Time] structure. For method documentation, see
[Time<Fixed>#impl-Time<Fixed>].
It is automatically inserted as a resource by
TimePlugin and updated based on
Time<Virtual>. The fixed clock is automatically set as the
generic [Time] resource during FixedUpdate
schedule processing.
The fixed timestep clock advances in fixed-size increments, which is
extremely useful for writing logic (like physics) that should have
consistent behavior, regardless of framerate.
The default timestep() is 64 hertz, or 15625
microseconds. This value was chosen because using 60 hertz has the potential
for a pathological interaction with the monitor refresh rate where the game
alternates between running two fixed timesteps and zero fixed timesteps per
frame (for example when running two fixed timesteps takes longer than a
frame). Additionally, the value is a power of two which losslessly converts
into [f32] and [f64].
To run a system on a fixed timestep, add it to one of the [FixedMain]
schedules, most commonly FixedUpdate.
This schedule is run a number of times between
PreUpdate and Update
according to the accumulated overstep() time divided by
the timestep(). This means the schedule may run 0, 1 or
more times during a single update (which typically corresponds to a rendered
frame).
Time<Fixed> and the generic [Time] resource will report a
delta() equal to timestep() and always
grow elapsed() by one timestep() per
iteration.
The fixed timestep clock follows the Time<Virtual> clock, which
means it is affected by pause(),
set_relative_speed() and
set_max_delta() from virtual time. If the virtual
clock is paused, the FixedUpdate schedule will
not run. It is guaranteed that the elapsed() time in
Time<Fixed> is always between the previous elapsed() and the current
elapsed() value in Time<Virtual>, so the values are compatible.
Changing the timestep size while the game is running should not normally be
done, as having a regular interval is the point of this schedule, but it may
be necessary for effects like “bullet-time” if the normal granularity of the
fixed timestep is too big for the slowed down time. In this case,
set_timestep() and be called to set a new value. The
new value will be used immediately for the next run of the
FixedUpdate schedule, meaning that it will affect
the delta() value for the very next
FixedUpdate, even if it is still during the same
frame. Any overstep() present in the accumulator will be
processed according to the new timestep() value.
Constructs a new instance of the type from a table of its fields.
Missing fields are filled in from the type’s default, then from the defaults of the fields’ types.
For enums use the variant key, or construct from a variant directly.
Real time clock representing elapsed wall clock time.
A specialization of the [Time] structure. For method documentation, see
[Time<Real>#impl-Time<Real>].
It is automatically inserted as a resource by
TimePlugin and updated with time instants according
to TimeUpdateStrategy.1
Note:
Using TimeUpdateStrategy::ManualDuration
allows for mocking the wall clock for testing purposes.
Besides this use case, it is not recommended to do this, as it will no longer
represent “wall clock” time as intended.
The delta() and elapsed() values of this
clock should be used for anything which deals specifically with real time
(wall clock time). It will not be affected by relative game speed
adjustments, pausing or other adjustments.1
The clock does not count time from startup() to
first_update() into elapsed, but instead will
start counting time from the first update call. delta() and
elapsed() will report zero on the first update as there
is no previous update instant. This means that a delta() of
zero must be handled without errors in application logic, as it may
theoretically also happen at other times.
Constructs a new instance of the type from a table of its fields.
Missing fields are filled in from the type’s default, then from the defaults of the fields’ types.
For enums use the variant key, or construct from a variant directly.
Constructs a new instance of the type from a table of its fields.
Missing fields are filled in from the type’s default, then from the defaults of the fields’ types.
For enums use the variant key, or construct from a variant directly.
A specialization of the [Time] structure. For method documentation, see
[Time<Virtual>#impl-Time<Virtual>].
Normally used as Time<Virtual>. It is automatically inserted as a resource
by TimePlugin and updated based on
Time<Real>. The virtual clock is automatically set as the default
generic [Time] resource for the update.
The virtual clock differs from real time clock in that it can be paused, sped up
and slowed down. It also limits how much it can advance in a single update
in order to prevent unexpected behavior in cases where updates do not happen
at regular intervals (e.g. coming back after the program was suspended a long time).
The virtual clock can be paused by calling pause(),
unpaused by calling unpause(), or toggled by calling
toggle(). When the game clock is
paused delta() will be zero on each update, and
elapsed() will not grow.
effective_speed() will return 0.0. Calling
pause() will not affect value the delta()
value for the update currently being processed.
The maximum amount of delta time that can be added by a single update can be
set by set_max_delta(). This value serves a dual
purpose in the virtual clock.
If the game temporarily freezes due to any reason, such as disk access, a
blocking system call, or operating system level suspend, reporting the full
elapsed delta time is likely to cause bugs in game logic. Usually if a
laptop is suspended for an hour, it doesn’t make sense to try to simulate
the game logic for the elapsed hour when resuming. Instead it is better to
lose the extra time and pretend a shorter duration of time passed. Setting
max_delta() to a relatively short time means that the
impact on game logic will be minimal.
If the game lags for some reason, meaning that it will take a longer time to
compute a frame than the real time that passes during the computation, then
we would fall behind in processing virtual time. If this situation persists,
and computing a frame takes longer depending on how much virtual time has
passed, the game would enter a “death spiral” where computing each frame
takes longer and longer and the game will appear to freeze. By limiting the
maximum time that can be added at once, we also limit the amount of virtual
time the game needs to compute for each frame. This means that the game will
run slow, and it will run slower than real time, but it will not freeze and
it will recover as soon as computation becomes fast again.
You should set max_delta() to a value that is
approximately the minimum FPS your game should have even if heavily lagged
for a moment. The actual FPS when lagged will be somewhat lower than this,
depending on how much more time it takes to compute a frame compared to real
time. You should also consider how stable your FPS is, as the limit will
also dictate how big of an FPS drop you can accept without losing time and
falling behind real time.
Constructs a new instance of the type from a table of its fields.
Missing fields are filled in from the type’s default, then from the defaults of the fields’ types.
For enums use the variant key, or construct from a variant directly.
[`GlobalTransform`] is an affine transformation from entity-local coordinates to worldspace coordinates. You cannot directly mutate [`GlobalTransform`]...
[GlobalTransform] is an affine transformation from entity-local coordinates to worldspace coordinates.
You cannot directly mutate [GlobalTransform]; instead, you change an entity’s transform by manipulating
its [Transform], which indirectly causes Bevy to update its [GlobalTransform].
To get the global transform of an entity, you should get its [GlobalTransform].
For transform hierarchies to work correctly, you must have both a [Transform] and a [GlobalTransform].
[GlobalTransform] is automatically inserted whenever [Transform] is inserted.
[Transform] transforms an entity relative to its parent’s reference frame, or relative to world space coordinates,
if it doesn’t have a ChildOf component.
[GlobalTransform] is managed by Bevy; it is computed by successively applying the [Transform] of each ancestor
entity which has a Transform. This is done automatically by Bevy-internal systems in the TransformSystems::Propagate
system set.
This system runs during PostUpdate. If you
update the [Transform] of an entity in this schedule or after, you will notice a 1 frame lag
before the [GlobalTransform] is updated.
Constructs a new instance of the type from a table of its fields.
Missing fields are filled in from the type’s default, then from the defaults of the fields’ types.
For enums use the variant key, or construct from a variant directly.
Describe the position of an entity. If the entity has a parent, the position is relative
to its parent position.
To place or move an entity, you should set its [Transform].
To get the global transform of an entity, you should get its [GlobalTransform].
To be displayed, an entity must have both a [Transform] and a [GlobalTransform].
[GlobalTransform] is automatically inserted whenever [Transform] is inserted.
Transforms compose from right to left: if t1 and t2 are transforms, then t1 * t2
corresponds to applying t2first, then applying t1.
This system runs during PostUpdate. If you
update the [Transform] of an entity during this set or after, you will notice a 1 frame lag
before the [GlobalTransform] is updated.
A transform at the position `x`, `y`, `z`. ```multi-lang-example,lua local Transform = import_namespace(“transform”).Transform local t = Transform.from_xyz(1, 2, 3) assert(t.translation.x == 1) `...
Constructs a new instance of the type from a table of its fields.
Missing fields are filled in from the type’s default, then from the defaults of the fields’ types.
For enums use the variant key, or construct from a variant directly.
Provides the computed size and layout properties of a UI [`Node`]. All of the fields are automatically calculated by [`ui_layout_system`](`super::layout::ui_layout_system`)...
`GlobalZIndex` allows a [`Node`] entity anywhere in the UI hierarchy to escape the implicit draw ordering of the UI’s layout tree and be rendered above or below other UI nodes. Root UI nodes without a `GlobalZIndex` component receive an implicit global z-index of `0`. Nodes with a `GlobalZIndex` of greater than 0 will be drawn on top of nodes without a `GlobalZIndex` or nodes with a lower `GlobalZIndex`. Nodes with a `GlobalZIndex` of less than 0 will be drawn below nodes without a `GlobalZIndex` or nodes with a greater `GlobalZIndex`. The order of nodes with the same `GlobalZIndex` is stable between frames. If two Nodes have the same `GlobalZIndex`, ties are decided in order by: * The node with the higher `ZIndex`. * The node that was newly added this frame. * The node with a changed `GlobalZIndex` or `ZIndex`. Otherwise the order is preserved from the previous frame.
A [`GridTrack`] is a Row or Column of a CSS Grid. This struct specifies what size the track should be. See below for the different “track sizing functions” you can specify.
Represents a *possibly* repeated [`GridTrack`]. The repetition parameter can either be: - The integer `1`, in which case the track is non-repeated. - a `u16` count to repeat the track N times. - A `GridTrackRepetition::AutoFit` or `GridTrackRepetition::AutoFill`. Note: that in the common case you want a non-repeating track (repetition count 1), you may use the constructor methods on [`GridTrack`]...
Indicates that this [`Node`] entity’s front-to-back ordering is not controlled solely by its location in the UI hierarchy. A node with a higher z-index will appear on top of sibling nodes with a lower z-index. UI nodes that have the same z-index will appear according to the order in which they appear in the UI hierarchy. In such a case, the last node to be added to its parent will appear in front of its siblings. Nodes without this component will be treated as if they had a value of [ZIndex]...
Represents the possible value types for layout properties.
This enum allows specifying values for various Node properties in different units,
such as logical pixels, percentages, or automatically determined values.
Val also implements [core::str::FromStr] to allow parsing values from strings in the format #.#px. Whitespaces between the value and unit is allowed. The following units are supported:
px: logical pixels
%: percentage
vw: percentage of the viewport width
vh: percentage of the viewport height
vmin: percentage of the viewport’s smaller dimension
vmax: percentage of the viewport’s larger dimension
A length in logical pixels, such as `Val.px(100)` for 100 pixels. ```multi-lang-example,lua local Node = import_namespace(“ui”).Node local Val = import_namespace(“ui”).Val local node = Node.new({ width = Val.px(100) }) assert(node.width[1] == 100) ``` ```multi-lang-example,rune let Node = import_namespace(“ui”).Node; let Val = import_namespace(“ui”).Val; let node = Node.new(#{ width: Val.px(100.0) }); assert!(node.width[0]...
A length in logical pixels, such as Val.px(100) for 100 pixels.
local Node = import_namespace("ui").Node
local Val = import_namespace("ui").Val
local node = Node.new({ width = Val.px(100) })
assert(node.width[1] == 100)
let Node = import_namespace("ui").Node;
let Val = import_namespace("ui").Val;
let node = Node.new(#{ width: Val.px(100.0) });
assert!(node.width[0] == 100.0);
Constructs a new instance of the type from a table of its fields.
Missing fields are filled in from the type’s default, then from the defaults of the fields’ types.
For enums use the variant key, or construct from a variant directly.
Constructs a new instance of the type from a table of its fields.
Missing fields are filled in from the type’s default, then from the defaults of the fields’ types.
For enums use the variant key, or construct from a variant directly.
Used to add rounded corners to a UI node. You can set a UI node to have uniformly
rounded corners, elliptical corners, or specify a different radius for each corner. If a given
radius exceeds half the length of the node’s height or width, the radius will be calculated
as half the height or width.
Percentage horizontal and vertical values are based on the node’s width and height, respectively.
Constructs a new instance of the type from a table of its fields.
Missing fields are filled in from the type’s default, then from the defaults of the fields’ types.
For enums use the variant key, or construct from a variant directly.
The fields are measured in physical pixels.
You can multiply by the inverse_scale_factor field to convert back to logical pixels.
Fields in this struct are public but should only rarely be directly modified.
For example, in a scrollbar you may want to derive the handle’s size from the proportion of
scrollable content in-view. You can directly modify ComputedNode after layout to set the
handle size without a frame delay.
Constructs a new instance of the type from a table of its fields.
Missing fields are filled in from the type’s default, then from the defaults of the fields’ types.
For enums use the variant key, or construct from a variant directly.
Constructs a new instance of the type from a table of its fields.
Missing fields are filled in from the type’s default, then from the defaults of the fields’ types.
For enums use the variant key, or construct from a variant directly.
The horizontal and vertical radii of a rounded corner’s elliptical arc.
If one field is auto, the resolved radius will be circular and clamped to half the length of the node’s shortest side.
If either field is zero, or both are auto, the node will have square corners.
Constructs a new instance of the type from a table of its fields.
Missing fields are filled in from the type’s default, then from the defaults of the fields’ types.
For enums use the variant key, or construct from a variant directly.
Constructs a new instance of the type from a table of its fields.
Missing fields are filled in from the type’s default, then from the defaults of the fields’ types.
For enums use the variant key, or construct from a variant directly.
Constructs a new instance of the type from a table of its fields.
Missing fields are filled in from the type’s default, then from the defaults of the fields’ types.
For enums use the variant key, or construct from a variant directly.
Constructs a new instance of the type from a table of its fields.
Missing fields are filled in from the type’s default, then from the defaults of the fields’ types.
For enums use the variant key, or construct from a variant directly.
Constructs a new instance of the type from a table of its fields.
Missing fields are filled in from the type’s default, then from the defaults of the fields’ types.
For enums use the variant key, or construct from a variant directly.
Constructs a new instance of the type from a table of its fields.
Missing fields are filled in from the type’s default, then from the defaults of the fields’ types.
For enums use the variant key, or construct from a variant directly.
Constructs a new instance of the type from a table of its fields.
Missing fields are filled in from the type’s default, then from the defaults of the fields’ types.
For enums use the variant key, or construct from a variant directly.
Constructs a new instance of the type from a table of its fields.
Missing fields are filled in from the type’s default, then from the defaults of the fields’ types.
For enums use the variant key, or construct from a variant directly.
Constructs a new instance of the type from a table of its fields.
Missing fields are filled in from the type’s default, then from the defaults of the fields’ types.
For enums use the variant key, or construct from a variant directly.
GlobalZIndex allows a [Node] entity anywhere in the UI hierarchy to escape the implicit draw ordering of the UI’s layout tree and
be rendered above or below other UI nodes.
Root UI nodes without a GlobalZIndex component receive an implicit global z-index of 0.
Nodes with a GlobalZIndex of greater than 0 will be drawn on top of nodes without a GlobalZIndex or nodes with a lower GlobalZIndex.
Nodes with a GlobalZIndex of less than 0 will be drawn below nodes without a GlobalZIndex or nodes with a greater GlobalZIndex.
The order of nodes with the same GlobalZIndex is stable between frames.
If two Nodes have the same GlobalZIndex, ties are decided in order by:
The node with the higher ZIndex.
The node that was newly added this frame.
The node with a changed GlobalZIndex or ZIndex.
Otherwise the order is preserved from the previous frame.
Constructs a new instance of the type from a table of its fields.
Missing fields are filled in from the type’s default, then from the defaults of the fields’ types.
For enums use the variant key, or construct from a variant directly.
Controls whether grid items are placed row-wise or column-wise as well as whether the sparse or dense packing algorithm is used.
The “dense” packing algorithm attempts to fill in holes earlier in the grid, if smaller items come up later.
This may cause items to appear out-of-order when doing so would fill in holes left by larger items.
Constructs a new instance of the type from a table of its fields.
Missing fields are filled in from the type’s default, then from the defaults of the fields’ types.
For enums use the variant key, or construct from a variant directly.
Represents the position of a grid item in a single axis.
There are 3 fields which may be set:
start: which grid line the item should start at
end: which grid line the item should end at
span: how many tracks the item should span
The default span is 1. If neither start or end is set then the item will be placed automatically.
Generally, at most two fields should be set. If all three fields are specified then span will be ignored. If end specifies an earlier
grid line than start then end will be ignored and the item will have a span of 1.
Constructs a new instance of the type from a table of its fields.
Missing fields are filled in from the type’s default, then from the defaults of the fields’ types.
For enums use the variant key, or construct from a variant directly.
A [GridTrack] is a Row or Column of a CSS Grid. This struct specifies what size the track should be.
See below for the different “track sizing functions” you can specify.
Constructs a new instance of the type from a table of its fields.
Missing fields are filled in from the type’s default, then from the defaults of the fields’ types.
For enums use the variant key, or construct from a variant directly.
Constructs a new instance of the type from a table of its fields.
Missing fields are filled in from the type’s default, then from the defaults of the fields’ types.
For enums use the variant key, or construct from a variant directly.
Constructs a new instance of the type from a table of its fields.
Missing fields are filled in from the type’s default, then from the defaults of the fields’ types.
For enums use the variant key, or construct from a variant directly.
Constructs a new instance of the type from a table of its fields.
Missing fields are filled in from the type’s default, then from the defaults of the fields’ types.
For enums use the variant key, or construct from a variant directly.
Constructs a new instance of the type from a table of its fields.
Missing fields are filled in from the type’s default, then from the defaults of the fields’ types.
For enums use the variant key, or construct from a variant directly.
Constructs a new instance of the type from a table of its fields.
Missing fields are filled in from the type’s default, then from the defaults of the fields’ types.
For enums use the variant key, or construct from a variant directly.
Constructs a new instance of the type from a table of its fields.
Missing fields are filled in from the type’s default, then from the defaults of the fields’ types.
For enums use the variant key, or construct from a variant directly.
Constructs a new instance of the type from a table of its fields.
Missing fields are filled in from the type’s default, then from the defaults of the fields’ types.
For enums use the variant key, or construct from a variant directly.
A Complete Guide To Flexbox by CSS Tricks. This is detailed guide with illustrations and comprehensive written explanation of the different Flexbox properties and how they work.
Flexbox Froggy. An interactive tutorial/game that teaches the essential parts of Flexbox in a fun engaging way.
A Complete Guide To CSS Grid by CSS Tricks. This is detailed guide with illustrations and comprehensive written explanation of the different CSS Grid properties and how they work.
CSS Grid Garden. An interactive tutorial/game that teaches the essential parts of CSS Grid in a fun engaging way.
Constructs a new instance of the type from a table of its fields.
Missing fields are filled in from the type’s default, then from the defaults of the fields’ types.
For enums use the variant key, or construct from a variant directly.
The [Outline] component adds an outline outside the edge of a UI node.
Outlines do not take up space in the layout.
To add an [Outline] to a UI node you can spawn a (Node, Outline) tuple bundle:
# use bevy_ecs::prelude::*;
# use bevy_ui::prelude::*;
# use bevy_color::palettes::basic::{RED, BLUE};
fn setup_ui(mut commands: Commands) {
commands.spawn((
Node {
width: Val::Px(100.),
height: Val::Px(100.),
..Default::default()
},
BackgroundColor(BLUE.into()),
Outline::new(Val::Px(10.), Val::ZERO, RED.into())
));
}
[Outline] components can also be added later to existing UI nodes:
# use bevy_ecs::prelude::*;
# use bevy_ui::prelude::*;
# use bevy_picking::hover::Hovered;
# use bevy_color::Color;
fn outline_hovered_button_system(
mut commands: Commands,
mut node_query: Query<(Entity, &Hovered, Option<&mut Outline>), Changed<Hovered>>,
) {
for (entity, hovered, mut maybe_outline) in node_query.iter_mut() {
let outline_color =
if hovered.get() {
Color::WHITE
} else {
Color::NONE
};
if let Some(mut outline) = maybe_outline {
outline.color = outline_color;
} else {
commands.entity(entity).insert(Outline::new(Val::Px(10.), Val::ZERO, outline_color));
}
}
}
Inserting and removing an [Outline] component repeatedly will result in table moves, so it is generally preferable to
set Outline::color to [Color::NONE] to hide an outline.
Constructs a new instance of the type from a table of its fields.
Missing fields are filled in from the type’s default, then from the defaults of the fields’ types.
For enums use the variant key, or construct from a variant directly.
Constructs a new instance of the type from a table of its fields.
Missing fields are filled in from the type’s default, then from the defaults of the fields’ types.
For enums use the variant key, or construct from a variant directly.
Constructs a new instance of the type from a table of its fields.
Missing fields are filled in from the type’s default, then from the defaults of the fields’ types.
For enums use the variant key, or construct from a variant directly.
Constructs a new instance of the type from a table of its fields.
Missing fields are filled in from the type’s default, then from the defaults of the fields’ types.
For enums use the variant key, or construct from a variant directly.
The integer 1, in which case the track is non-repeated.
a u16 count to repeat the track N times.
A GridTrackRepetition::AutoFit or GridTrackRepetition::AutoFill.
Note: that in the common case you want a non-repeating track (repetition count 1), you may use the constructor methods on [GridTrack]
to create a RepeatedGridTrack. i.e. GridTrack::px(10.0) is equivalent to RepeatedGridTrack::px(1, 10.0).
You may only use one auto-repetition per track list. And if your track list contains an auto repetition
then all tracks (in and outside of the repetition) must be fixed size (px or percent). Integer repetitions are just shorthand for writing out
N tracks longhand and are not subject to the same limitations.
Constructs a new instance of the type from a table of its fields.
Missing fields are filled in from the type’s default, then from the defaults of the fields’ types.
For enums use the variant key, or construct from a variant directly.
Constructs a new instance of the type from a table of its fields.
Missing fields are filled in from the type’s default, then from the defaults of the fields’ types.
For enums use the variant key, or construct from a variant directly.
Constructs a new instance of the type from a table of its fields.
Missing fields are filled in from the type’s default, then from the defaults of the fields’ types.
For enums use the variant key, or construct from a variant directly.
Indicates that this [Node] entity’s front-to-back ordering is not controlled solely
by its location in the UI hierarchy. A node with a higher z-index will appear on top
of sibling nodes with a lower z-index.
UI nodes that have the same z-index will appear according to the order in which they
appear in the UI hierarchy. In such a case, the last node to be added to its parent
will appear in front of its siblings.
Nodes without this component will be treated as if they had a value of
[ZIndex][ZIndex](0).
Use [GlobalZIndex] if you need to order separate UI hierarchies or nodes that are
not siblings in a given UI hierarchy.
Constructs a new instance of the type from a table of its fields.
Missing fields are filled in from the type’s default, then from the defaults of the fields’ types.
For enums use the variant key, or construct from a variant directly.
Constructs a new instance of the type from a table of its fields.
Missing fields are filled in from the type’s default, then from the defaults of the fields’ types.
For enums use the variant key, or construct from a variant directly.
Constructs a new instance of the type from a table of its fields.
Missing fields are filled in from the type’s default, then from the defaults of the fields’ types.
For enums use the variant key, or construct from a variant directly.
Constructs a new instance of the type from a table of its fields.
Missing fields are filled in from the type’s default, then from the defaults of the fields’ types.
For enums use the variant key, or construct from a variant directly.
The defining [`Component`] for window entities, storing information about how it should appear and behave. Each window corresponds to an entity, and is uniquely identified by the value of their [`Entity`]...
Controls the size of a [`Window`] ## Physical, logical and requested sizes There are three sizes associated with a window: - the physical size, which represents the actual height and width in physical pixels the window occupies on the monitor, - the logical size, which represents the size that should be used to scale elements inside the window, measured in logical pixels, - the requested size, measured in logical pixels, which is the value submitted to the API when creating the window, or requesting that it be resized. ## Scale factor The reason logical size and physical size are separated and can be different is to account for the cases where: - several monitors have different pixel densities, - the user has set up a pixel density preference in its operating system, - the Bevy `App` has specified a specific scale factor between both. The factor between physical size and logical size can be retrieved with [`WindowResolution::scale_factor`]...
Specifies which [`Window`] control buttons should be enabled. ## Platform-specific **`iOS`**, **`Android`**, and the **`Web`** do not have window control buttons. On some **`Linux`** environments these values have no effect.
The edges of a screen. Corresponds to [`winit::platform::ios::ScreenEdge`]. # Platform-specific - Only used on iOS. [`winit::platform::ios::ScreenEdge`]...
Specifies where a [`Window`] should appear relative to other overlapping windows (on top or under) . Levels are groups of windows with respect to their z-position. The relative ordering between windows in different window levels is fixed. The z-order of windows within the same window level may change dynamically on user interaction. ## Platform-specific - **iOS / Android / Web / Wayland:** Unsupported.
The defining [Component] for window entities,
storing information about how it should appear and behave.
Each window corresponds to an entity, and is uniquely identified by the value of their [Entity].
When the [Window] component is added to an entity, a new window will be opened.
When it is removed or the entity is despawned, the window will close.
The primary window entity (and the corresponding window) is spawned by default
by WindowPlugin and is marked with the [PrimaryWindow] component.
This component is synchronized with winit through bevy_winit:
it will reflect the current state of the window and can be modified to change this state.
Because this component is synchronized with winit, it can be used to perform
OS-integrated windowing operations. For example, here’s a simple system
to change the window mode:
# use bevy_ecs::query::With;
# use bevy_ecs::system::Query;
# use bevy_window::{WindowMode, PrimaryWindow, Window, MonitorSelection, VideoModeSelection};
fn change_window_mode(mut windows: Query<&mut Window, With<PrimaryWindow>>) {
// Query returns one window typically.
for mut window in windows.iter_mut() {
window.mode =
WindowMode::Fullscreen(MonitorSelection::Current, VideoModeSelection::Current);
}
}
Constructs a new instance of the type from a table of its fields.
Missing fields are filled in from the type’s default, then from the defaults of the fields’ types.
For enums use the variant key, or construct from a variant directly.
The reason logical size and physical size are separated and can be different
is to account for the cases where:
several monitors have different pixel densities,
the user has set up a pixel density preference in its operating system,
the Bevy App has specified a specific scale factor between both.
The factor between physical size and logical size can be retrieved with
[WindowResolution::scale_factor].
For the first two cases, a scale factor is set automatically by the operating
system through the window backend. You can get it with
[WindowResolution::base_scale_factor].
For the third case, you can override this automatic scale factor with
[WindowResolution::set_scale_factor_override].
The logical size should be equal to the requested size after creating/resizing,
when possible.
The reason the requested size and logical size might be different
is because the corresponding physical size might exceed limits (either the
size limits of the monitor, or limits defined in [WindowResizeConstraints]).
Note: The requested size is not kept in memory, for example requesting a size
too big for the screen, making the logical size different from the requested size,
and then setting a scale factor that makes the previous requested size within
the limits of the screen will not get back that previous requested size.
Creates a resolution from a size in physical pixels. ```multi-lang-example,lua local WindowResolution = import_namespace(“window”).WindowResolution local resolution = WindowResolution.new(800, 600) assert(resolution:width() == 800) ``` ```multi-lang-example,rune let WindowResolution = import_...
Constructs a new instance of the type from a table of its fields.
Missing fields are filled in from the type’s default, then from the defaults of the fields’ types.
For enums use the variant key, or construct from a variant directly.
Constructs a new instance of the type from a table of its fields.
Missing fields are filled in from the type’s default, then from the defaults of the fields’ types.
For enums use the variant key, or construct from a variant directly.
Constructs a new instance of the type from a table of its fields.
Missing fields are filled in from the type’s default, then from the defaults of the fields’ types.
For enums use the variant key, or construct from a variant directly.
Constructs a new instance of the type from a table of its fields.
Missing fields are filled in from the type’s default, then from the defaults of the fields’ types.
For enums use the variant key, or construct from a variant directly.
Constructs a new instance of the type from a table of its fields.
Missing fields are filled in from the type’s default, then from the defaults of the fields’ types.
For enums use the variant key, or construct from a variant directly.
The presentation mode specifies when a frame is presented to the window. The Fifo
option corresponds to a traditional VSync, where the framerate is capped by the
display refresh rate. Both Immediate and Mailbox are low-latency and are not
capped by the refresh rate, but may not be available on all platforms. Tearing
may be observed with Immediate mode, but will not be observed with Mailbox or
Fifo.
Constructs a new instance of the type from a table of its fields.
Missing fields are filled in from the type’s default, then from the defaults of the fields’ types.
For enums use the variant key, or construct from a variant directly.
Constructs a new instance of the type from a table of its fields.
Missing fields are filled in from the type’s default, then from the defaults of the fields’ types.
For enums use the variant key, or construct from a variant directly.
Constructs a new instance of the type from a table of its fields.
Missing fields are filled in from the type’s default, then from the defaults of the fields’ types.
For enums use the variant key, or construct from a variant directly.
Specifies where a [Window] should appear relative to other overlapping windows (on top or under) .
Levels are groups of windows with respect to their z-position.
The relative ordering between windows in different window levels is fixed.
The z-order of windows within the same window level may change dynamically on user interaction.
Constructs a new instance of the type from a table of its fields.
Missing fields are filled in from the type’s default, then from the defaults of the fields’ types.
For enums use the variant key, or construct from a variant directly.
Constructs a new instance of the type from a table of its fields.
Missing fields are filled in from the type’s default, then from the defaults of the fields’ types.
For enums use the variant key, or construct from a variant directly.
Constructs a new instance of the type from a table of its fields.
Missing fields are filled in from the type’s default, then from the defaults of the fields’ types.
For enums use the variant key, or construct from a variant directly.
Constructs a new instance of the type from a table of its fields.
Missing fields are filled in from the type’s default, then from the defaults of the fields’ types.
For enums use the variant key, or construct from a variant directly.
These values are measured in logical pixels (see [WindowResolution]), so the user’s
scale factor does affect the size limits on the window.
Please note that if the window is resizable, then when the window is
maximized it may have a size outside of these limits. The functionality
required to disable maximizing is not yet exposed by winit.
Constructs a new instance of the type from a table of its fields.
Missing fields are filled in from the type’s default, then from the defaults of the fields’ types.
For enums use the variant key, or construct from a variant directly.
Constructs a new instance of the type from a table of its fields.
Missing fields are filled in from the type’s default, then from the defaults of the fields’ types.
For enums use the variant key, or construct from a variant directly.
Triggers an observer-style event: either a Rust event type with `#[reflect(Event)]`, or an event registered with `register_new_event`. Observers run immediately, and the commands they queue are applied before this function returns.
Adds an observer which runs the callback callback of the calling script whenever an event of the given type is triggered.
The callback is a normal script callback: either a function registered with register_callback, or a top level function with that name.
It receives a copy of the event (or the payload, for script events), and is run by the same machinery as other callbacks:
right after the operation which triggered the event, unless the script’s context is busy running another callback
(e.g. the one that triggered the event), in which case it runs as soon as that callback finishes.
Observers never re-enter a running script. The order in which the observers of an event run is unspecified.
Removed component data is no longer available inside the callback. Errors of the callback are reported like those of other callbacks.
The event type must have #[reflect(Event)]. The lifecycle events (AddEvent, InsertEvent, DiscardEvent,
RemoveEvent, DespawnEvent, imported with import_namespace("bevy_ecs")) are supported out of the box, and run for the given components.
Retrieves a system set by its full name, i.e. transform.TransformSystems.Propagate for a set of the game,
or script.my_game.ai for a set registered by a script.
The sets of the game are also members of their namespace, i.e. import_namespace("transform.TransformSystems").Propagate.
Returns either a ScriptComponentRegistration, ScriptResourceRegistration or ScriptBundleRegistration depending on the type of the type requested.
If the type is none of these returns a ScriptTypeRegistration.
The component will behave like any other native component for all intents and purposes.
The type that will be instantiated to back this component will be DynamicComponent which contains just one field:
data
This field can be set to any value and modified freely.
Constructs a new instance of the type from a table of its fields.
Missing fields are filled in from the type’s default, then from the defaults of the fields’ types.
For enums use the variant key, or construct from a variant directly.
Constructs a new instance of the type from a table of its fields.
Missing fields are filled in from the type’s default, then from the defaults of the fields’ types.
For enums use the variant key, or construct from a variant directly.
Constructs a new instance of the type from a table of its fields.
Missing fields are filled in from the type’s default, then from the defaults of the fields’ types.
For enums use the variant key, or construct from a variant directly.
Constructs a new instance of the type from a table of its fields.
Missing fields are filled in from the type’s default, then from the defaults of the fields’ types.
For enums use the variant key, or construct from a variant directly.
Constructs a new instance of the type from a table of its fields.
Missing fields are filled in from the type’s default, then from the defaults of the fields’ types.
For enums use the variant key, or construct from a variant directly.
Constructs a new instance of the type from a table of its fields.
Missing fields are filled in from the type’s default, then from the defaults of the fields’ types.
For enums use the variant key, or construct from a variant directly.
Constructs a new instance of the type from a table of its fields.
Missing fields are filled in from the type’s default, then from the defaults of the fields’ types.
For enums use the variant key, or construct from a variant directly.
Constructs a new instance of the type from a table of its fields.
Missing fields are filled in from the type’s default, then from the defaults of the fields’ types.
For enums use the variant key, or construct from a variant directly.
Constructs a new instance of the type from a table of its fields.
Missing fields are filled in from the type’s default, then from the defaults of the fields’ types.
For enums use the variant key, or construct from a variant directly.
Constructs a new instance of the type from a table of its fields.
Missing fields are filled in from the type’s default, then from the defaults of the fields’ types.
For enums use the variant key, or construct from a variant directly.
Constructs a new instance of the type from a table of its fields.
Missing fields are filled in from the type’s default, then from the defaults of the fields’ types.
For enums use the variant key, or construct from a variant directly.
Constructs a new instance of the type from a table of its fields.
Missing fields are filled in from the type’s default, then from the defaults of the fields’ types.
For enums use the variant key, or construct from a variant directly.
Whether two values are equal, compared through reflection.
This is what == uses for types which don’t define their own equality.
Values of different types, and values which can’t be compared, are not equal.
An enum value is equal to an enum variant, i.e. MyEnum.Variant, if it is in that variant.
Constructs a new instance of the type from a table of its fields.
Missing fields are filled in from the type’s default, then from the defaults of the fields’ types.
For enums use the variant key, or construct from a variant directly.
Constructs a new instance of the type from a table of its fields.
Missing fields are filled in from the type’s default, then from the defaults of the fields’ types.
For enums use the variant key, or construct from a variant directly.
Constructs a new instance of the type from a table of its fields.
Missing fields are filled in from the type’s default, then from the defaults of the fields’ types.
For enums use the variant key, or construct from a variant directly.
Constructs a new instance of the type from a table of its fields.
Missing fields are filled in from the type’s default, then from the defaults of the fields’ types.
For enums use the variant key, or construct from a variant directly.
Constructs a new instance of the type from a table of its fields.
Missing fields are filled in from the type’s default, then from the defaults of the fields’ types.
For enums use the variant key, or construct from a variant directly.
Constructs a new instance of the type from a table of its fields.
Missing fields are filled in from the type’s default, then from the defaults of the fields’ types.
For enums use the variant key, or construct from a variant directly.
Constructs a new instance of the type from a table of its fields.
Missing fields are filled in from the type’s default, then from the defaults of the fields’ types.
For enums use the variant key, or construct from a variant directly.
Constructs a new instance of the type from a table of its fields.
Missing fields are filled in from the type’s default, then from the defaults of the fields’ types.
For enums use the variant key, or construct from a variant directly.
Constructs a new instance of the type from a table of its fields.
Missing fields are filled in from the type’s default, then from the defaults of the fields’ types.
For enums use the variant key, or construct from a variant directly.
Constructs a new instance of the type from a table of its fields.
Missing fields are filled in from the type’s default, then from the defaults of the fields’ types.
For enums use the variant key, or construct from a variant directly.
On top of callbacks which are registered by your application, BMS provides a set of core callbacks which are always available (unless disabled via plugin settings).
This will be called right after a script has been loaded or reloaded. This is a good place to initialize your script. You should avoid placing a lot of logic into the global body of your script, and instead put it into this callback. Otherwise errors in the initialization will fail the loading of the script.
print("you can also use this space, but it's not recommended")
function on_script_loaded()
print("Hello world")
end
This will be called right before a script is unloaded. This is a good place to clean up any resources that your script has allocated. This is called both before a script is removed as well as before a script is reloaded. If you want to preserve the state of your script across reloads, you can return a value from this callback, which will be passed to on_script_reloaded when the script is reloaded.
function on_script_unloaded()
print("Goodbye world")
return "house key"
end
Called right after on_script_loaded but only if the script was reloaded.
The callback is passed a state argument, this state is exactly what is returned by the script through on_script_unloaded before a reload happens.
mode = 1
function on_script_reloaded(value)
if value then
print("I'm back. Thanks for the keys!")
else
print('I have not saved any state before unloading')
end
end
Using on_script_reloaded one can make a script reload preserve its current state.
BMS provides built-in support for working with Bevy assets from scripts. You can load assets, add new ones, and read or change asset data through the reflection system.
Starts loading an asset of this type from a path relative to the assets folder, and returns a handle to it right away. The asset is loaded in the background, see world.is_loaded(handle).
Returns the asset a handle points to, or nil if it isn’t loaded or added yet. Changes to the returned value change the asset.
local ScriptAsset = import_namespace("bms").ScriptAsset
local handle = ScriptAsset.load("scripts/my_script.lua")
-- later, once `world.is_loaded(handle)` is true
local script = ScriptAsset.get_asset(handle)
This section is for developers who want to contribute to the BMS project. It covers various topics necessary for understanding the project and contributing to it.
The bevy bindings shipped with BMS are hand written, using the #[script_bindings] macro (see Controlling Script Bindings).
They live in crates/bindings/bevy_mod_scripting_bevy_<category>_bindings, one crate per area of functionality (i.e. math, ecs, ui) rather than one per upstream bevy crate.
The aim is a small, well documented scripting surface, which reads naturally from a script and compiles quickly.
Scripts reach types through the namespace of their category, i.e. import_namespace("math").Vec3 (see Namespaces). So each bindings crate:
passes namespace = "<category>" to every #[script_bindings] attribute, except on impl World blocks and on functions shared by all types (impl StaticTypeRef), which would otherwise move those BMS types into the category
calls set_crate_script_namespaces from its plugin’s finish, with the bevy crates it covers, so their types without bindings land in the same namespace
containers support len, iter, push etc., and every value can be printed
So only bind what scripts can’t already do, or can’t do comfortably:
methods with behaviour, i.e. Vec3:normalize() or Transform:look_at(...)
constructors for types which are built often (vectors, colors), or whose fields are private (i.e. Name), or whose meaning isn’t obvious from their fields (i.e. Quat.from_rotation_y)
operators, by registering functions named add, sub, mul, div, rem, neg, pow, eq or lt. With a number on the left, i.e. 2 * v, add and mul are called with the operands swapped, the others call rsub, rdiv, rrem or rpow on the right operand, which you register where they make sense
Do not write getters or setters for public fields.
Take non primitive arguments as R<T> or M<T>. These are a downcast of the reference the script holds, while V<T> rebuilds the value through reflection. Clone natively if you need ownership.
Return a reference with an extended path when returning a part of the receiver; it costs nothing, and writes to it reach the original value. Returning V<T> allocates a new value.
There is one function per name: registering a name again replaces the function. For mixed operands accept a Union, i.e. rhs: Union<R<Vec3>, f32>.
Mutate in place by taking M<Self>; pure functions return new values.
Keep examples short and only add them where they help. Write one block per language, one after the other, using the multi-lang-example code block attribute followed by the language:
```multi-lang-example,lua
local Vec3 = import_namespace("math").Vec3
local v = Vec3.new(1, 2, 3)
assert(v.x == 1)
```
```multi-lang-example,rune
let Vec3 = import_namespace("math").Vec3;
let v = Vec3.new(1, 2, 3);
assert!(v.x == 1.0);
```
which renders as a single block with a tab per language:
local Vec3 = import_namespace("math").Vec3
local v = Vec3.new(1, 2, 3)
assert(v.x == 1)
let Vec3 = import_namespace("math").Vec3;
let v = Vec3.new(1, 2, 3);
assert!(v.x == 1.0);
Examples are tests: they must run as written and check their own results with assert.
Every multi-lang-example block in the doc comments under crates/ and in the book is run by the doc_example_tests test, with the scenario in assets/doc_examples/default.bmsscenario (the same app as the script tests):
cargo test --test doc_example_tests --features lua54,rune,bevy_math_bindings -- math
Examples of a bindings crate whose feature is disabled are ignored.
If an example needs a different setup, i.e. a window, name a scenario with the scenario attribute, i.e. multi-lang-example,lua,scenario=window, which refers to assets/doc_examples/window.bmsscenario.
Mark examples which can’t run in a test app with ignore, i.e. multi-lang-example,lua,ignore, and say what they need in the surrounding docs.
Each bindings crate has its script tests under assets/tests/bindings/<category>/, with a script per language per type or topic, i.e. assets/tests/bindings/math/vec.lua and assets/tests/bindings/math/vec.rn.
Run them with:
cargo test --test script_tests --features lua54,rune -- bindings/math
If you are interested in adding a new language to BMS, please read this section first. Adding a new language is a non-trivial task depending on the language you want to add. It also requires a lot of effort and time to maintain a new language, so unless the language you want to add is widely used and has a large community, it might not be worth the effort.
In order for a language to work well with BMS it’s necessary it supports the following features:
Interoperability with Rust. If you can’t call it from Rust easilly and there is no existing crate that can do it for you, it’s a no-go.
First class functions. Or at least the ability to call an arbitrary function with an arbitrary number of arguments from a script. Without this feature, you would need to separately generate code for the bevy bindings which is painful and goes against the grain of BMS.
They don’t necessarily have to be first class from the script POV, but they need to be first class from the POV of the host language. This means that the host language needs to be able to call a function with an arbitrary number of arguments.
Let’s say your language supports a Value type which can be returned to the script. And it has a Value::Function variant. The type on the Rust side would look something like this:
pub enum Value {
Function(Arc<Fn(&[Value]) -> Value>),
// other variants
}
This is fine, and can be integrated with BMS. Since an Fn function can be a closure capturing a DynamicScriptFunction. If there is no support for FnMut closures though, you might face issues in the implementation. Iterators in bevy_mod_scripting_functions for example use DynamicScriptFunctionMut which cannot work with Fn closures.
Now let’s imagine instead another language with a similar enum, supports this type instead:
#![allow(unused)]
fn main() {
pub enum Value {
Function(Arc<dyn Function>),
// other variants
}
pub trait Function {
fn call(&self, args: Vec<Value>) -> Value;
fn num_params() -> usize;
}
}
This implies that to call this function, you need to be able to know the amount of arguments it expects at COMPILE time. This is not compatibile with dynamic functions, and would require a lot of code generation to make work with BMS.
Languages with no support for dynamic functions are not compatible with BMS.
Not all languages can easilly be called from Rust. Lua has a wonderful crate which works out the ffi and safety issues for us. But not all languages have this luxury. If you can’t call it from Rust easilly and there is no existing crate that can do it for you, integrating with BMS might not be the best idea.
Every ScriptValue variant needs a counterpart in the language, in both directions. Two of them are handles rather than data:
ScriptValue::Reference: a reference to a reflected value, usually a userdata/custom type wrapping ReflectReference.
ScriptValue::Type: a type itself, i.e. what scripts use to reach a type’s static members. It carries only a TypeId and never allocates.
Every context gets the builtins as globals, nothing else: bevy_mod_scripting_bindings::builtins::builtins() (import_namespace, export_namespace) plus the language’s own register_callback:
for (name, function) in builtins() {
context.globals().set(name, LuaScriptValue::from(ScriptValue::Function(function)))?;
}
Script handlers, loaders etc. must be implemented such that the ThreadWorldContainer is set for every interaction with script contexts, or anywhere else it might be needed.
index returns functions which are called on the indexed value as Member::Method, without the value bound to them. Delivering the receiver is the language’s job:
Lua passes it with its colon syntax, v:length(), so the function is returned as is.
Rune has method syntax, but resolves v.name by a table fixed when its context is built, so every identifier in the script is registered when it is compiled (and v["name"] always works), see bevy_mod_scripting_rune::types. The receiver is passed to the function as the first argument.
functions shared by all types (registered on StaticTypeRef, i.e. the new constructor), with the type bound as their first argument by DynamicScriptFunction::bind_to_type
the type’s enum variants
Types reach scripts in two forms, which resolve static members the same way:
ScriptValue::Type, i.e. world (import_namespace("bms").world), indexed with dispatch::static_index
type registrations, i.e. the entries of import_namespace, which are references and are indexed with dispatch::index like any other value
Binding the type is done by BMS, so languages never handle StaticTypeRef arguments themselves.
An RFC is a request for comments from contributors, users and various other stakeholders. When an RFC is created it is usually done with the intention of gathering opinions and knowledge around the topic.
for BMS the process looks as follows:
An RFC is created and modified wia PR’s to the RFC file under docs/RequestsForComments/XXX-RFCTITLE/XX-RFCTITLE.md.
The creator of an RFC also starts an issue for discussion of the RFC on github linking to the file.
Once happy enough perspecties are gathered an RFC can be closed and the decision implemented.
Codegen has played a big role from the start for BMS, giving us great benefits, but mainly the ability to quickly ‘sync’ our scripting API with each new bevy release (instead of a manual review each time).
This was especially important before the introduction of a unified scripting function registry, which would have meant writing a version of each API per language.
However, I believe that as Bevy is maturing, the APIs that people rely on are likely to be more stable than ever, and I have a hypothesis that a large majority of the generated bindings are simply left unused.
The other side of the coin is that codegen is somewhat problematic due to:
Largely increased compilation times
The need to stay in sync with the rustc codebase (the compiler internals we use to plug into the compilation)
I believe it’s time to finally abandon this concept, and commit to hand writing a core scripting API over the important parts of bevy.
The code under /codegen contains crates which create a custom cargo executable (a driver and the entry point). This works with cargo to execute a custom rustc compilation, which on top of compiling and error checking the code, also runs our custom hooks.
In these hooks we scan through the available types and generate artifacts as we go (caches and lookups), which are then used to render code templates. All of the crates found under crates/bindings are fully rendered via this process.
These templates direct the generator to produce #[script_bindings] invocations, just as if they were handwritten, based on the functions available.
The process is slightly limited due to the presence of generics and other rust quirks which can’t be handled automatically, e.g. fn hello<A: ToString>(a: A) wouldn’t work, even though it’s somewhat trivial to deal with by hand.
At the end we are left with tonnes of bindings crates for all the important bevy crates.
An example can be seen in our book with the SRGBA struct:
Note that the docgen is not tied to codegen, the same documentation would be produced if you manually wrote the script bindings macro invocations yourself.
Not all of our bindings are automatically generated like this however. The crate bevy_mod_scripting_functions contains a load of hand-written core types and functions, like World::get_type_by_name or World::get_component, and way more.
These already live happily alongside the generated bindings, and are the model for what this RFC proposes we do everywhere.
With the above in mind, I form the following hypotheses:
Automatically generated bindings are okay at best, with many functions missing and docs mentioning rust constructs explicitly (since they come directly from docstrings)
Most of them are unused
The lack of functionality for generating bindings for 3rd party libraries means the codegen is not as useful as it could be
Bevy’s API is stabilizing, and upcoming work on enums and constructors will make many bindings unnecessary anyway
Handwriting binding crates with high quality multi language docstrings
Empowering the community to build their own bindings crates for their favourite libraries
In this new world we would focus on writing the most critical pieces of integration with bevy. Instead of putting in effort to maintain a bespoke rustc compiler plugin, we could provide github action templates to let people build out bindings quickly.
The codegen runs on our side, not on the consumer’s machine, and its output is committed. So no feature flag or default-on/off transition is needed:
Remove the codegen project, keeping whatever it last generated as plain source.
Iterate on and clean up those bindings, deleting ones that are no longer needed as the enum and constructor work lands.
This starts from a non-breaking state, and I’d like to begin as early as the 0.20 cycle rather than waiting for the editor.
Bindings for 3rd party libraries (e.g. bevy_hanabi, UI, physics) would not live in core, which would only keep Bevy-native bindings. Instead we’d make it easy to set up small community bindings crates that sync to the main test suite and publish easily.
We could also of course make our current codegen better, more flexible and generally stronger, however some of its core problems will always remain (like bad documentation at best, and long compilation times).
Each bevy version upgrade might take longer, due to the need to identify API surface changes. However, this should be fairly well mitigated by our test suite and also thanks to AI tools nowadays
Potentially smaller (but hopefully higher quality) API surface immediately after the switch