bevy_proto
Create entities in the Bevy game engine with a simple config file.
name: "Simple Enemy"
template: Creature
components:
- type: Enemy
- type: Attack
value:
damage: 10
- type: Armed
value:
weapons:
primary: "laser sword"
📋 Features
-
Define entities easily with config files:
name: Player components: - type: Controllable - type: Health value: max: 20 -
Inherit functionality from other prototypes:
name: Skeleton templates: Enemy, Creature components: # ... -
Include assets to be loaded:
name: Bullet components: - type: CustomSprite value: texture: "path/to/texture.png"
📲 Installation
[]
= "0.6"
= "0.2"
Then add it to your app like so:
use *;
use ProtoPlugin;
📓 Examples
Check out these examples for more details as to how to use this crate:
- attributes - A showcase of the available derive helper attributes
- basic - The most basic way to add prototypes
- bundles - A demonstration of a more complex prototype that includes assets
- templates - An example of how templates affect your prototypes
🕊 Bevy Compatibility
| bevy | bevy_proto |
|---|---|
| 0.9 | 0.6.0 |
| 0.8 | 0.5.0 |
| 0.7 | 0.4.0 |
| 0.6 | 0.3.0 |
| 0.5 | 0.2.1 |
✨ Usage
Creating Components
First, create a struct that implements ProtoComponent. This can be done one of two ways:
For simple components, ProtoComponent may be derived:
use ;
use *;
use *;
// Also works on tuple structs:
;
By default, the
ProtoComponentis cloned into spawned entities.
Otherwise, you can define them manually (the two attributes are required with this method):
use *;
use EntityCommands;
use *;
use ;
// Required
;
// Required
A
ProtoComponentdoes not need to be a component itself. It can be used purely as a DTO to generate other components or bundles. You have full access to theEntityCommandsso you can insert bundles or even multiple components at once.Other ways of generating components from non-component
ProtoComponentstructs can be found in the attributes example.
Defining the Prototype
Define the Prototype in a config file. By default YAML (and by extension, valid JSON) files are supported:
# assets/prototypes/adventurer.yaml
---
name: "Adventurer"
components:
- type: Inventory
value:
- type: Movement
value:
speed: 10
By default, all
.yamland.jsonfiles in theassets/prototypes/directory are processed as Prototypes.
Using Templates
A Prototype can also include a template. A template can be any Prototype and is used to define common components that should be inserted into its inheritors. This is helpful for reducing duplicate markup and quickly refactoring collections of Prototypes.
First, define the template:
# assets/prototypes/npc.yaml
---
name: "NPC"
components:
- type: Inventory
value: ~
- type: Movement
value:
speed: 10
Then inherit the template in another Prototype:
# assets/prototypes/peasant.yaml
---
name: "Peasant"
template: NPC
You can also override template components:
# assets/prototypes/adventurer.yaml
---
name: "Adventurer"
template: NPC
components:
- type: Inventory
value:
Multiple templates can be specified as well. However, conflicting components will be overridden in reverse order (templates listed first can override templates listed last):
# assets/prototypes/fast_adventurer.yaml
---
name: "Fast Adventurer"
templates: Speedy, NPC # "Speedy" may override "NPC"
components:
- type: Inventory
value:
Templates can be specified as a standard YAML list or as a comma-separated string (like in the example above). Additionally,
templatesis an alias fortemplate, so either one may be used.
Spawning the Prototype
To spawn a prototype, add a system that has access to:
-
mut Commands -
Res<ProtoData> -
Res<AssetServer>
Then write something like the following:
use *;
use *;
The spawn(...) method returns the EntityCommands used to create the entity. This allows you to add additional
components, bundles, etc.:
use *;
use *;
;
;
Using Assets
For Prototypes that need access to assets, you can get access one of two ways:
The first is by loading the asset when being spawned in. This is preferred because it means the asset can be unloaded when no longer needed.
use EntityCommands;
use *;
use ;
use *;
The second, is by preparing the asset for later use. This retains the asset in the ProtoData resource, which then must
be disposed of manually when no longer needed. Setting up an asset is done via the prepare method:
use EntityCommands;
use *;
use ;
use *;
Custom Prototypes
The default Prototype object looks like this:
use *;
use ;
However, you can use your own Prototype object. Any struct that implements Prototypical can be used in place of the default Prototype. Then you just have to supply your own deserializer to the ProtoPlugin object.
use ;
use *;
use EntityCommands;
use *;
;
;
All fields in
ProtoDataOptionsmust be specified if you wish to use a custom deserializer. Even if you want to continue using the defaults, you must still specify them. The additional fields shown above are also the defaults if you wish to copy them.
⚠️ Disclaimer
Before you install it into your project, please understand the limitations of this crate. While it makes working with some entities easier, it may come at a bit of a performance cost depending on your project.
According to the bench example, spawning a Prototype can be about 1.8x slower than defining the
entity in the system manually (this may vary depending on the data being loaded). This difference becomes much smaller for release builds, but is still a tad slower. For some projects,— except maybe for those that are really intensive or
spawn lots of entities very frequently,— this may not be a problem.
Still, it's good to consider the drawbacks of using this system and see if it's right for your own project. Here's a breakdown of the top current/potential issues:
-
Dynamic Dispatch - This crate uses dynamic trait objects to add or remove any component on a Prototype. However, this comes at a cost since the compiler can no longer know the types in advance, preventing things like static dispatch, monomorphization, etc.
-
Load Time - This is likely not noticeable for smaller projects (although I haven't tested with hundreds of Prototypes myself), but the crate currently does need to load all Prototypes at startup. This is so it can prepare any other needed resources and assets in order to spawn the Prototype.
-
Assets - This crate also (currently) stores all required assets in its own resource
ProtoData. This means that resources that may only be needed once will be kept loaded during the entire lifetime of the application, since it holds onto the handle. This can be prevented by hosting the asset on a separate component and manually creating the handles when spawning that Prototype:use *; use *; ;
With all of that said, this package is meant to speed up development and make changes to entity archetypes easier for humans (especially non-programmers) to interact with. If the performance hit is too much for your project, you are better off sticking with the standard methods of defining entities.