Expand description
§🍎 Moonshine Kind
Simple type safety solution for Bevy.
§Overview
An Entity is a generic way to reference entities within Bevy:
use bevy::prelude::*;
#[derive(Component)]
struct FruitBasket {
fruits: Vec<Entity>
}A problem with using entities in this way is the lack of information about the “kind” of the entity. This results in code that is error prone, hard to debug, and read.
This crate attempts to solve this problem by introducing a new Instance<T> type which behaves like an Entity but also contains information about the “kind” of the entity:
use bevy::prelude::*;
use moonshine_kind::prelude::*;
#[derive(Component)]
struct Fruit;
#[derive(Component)]
struct FruitBasket {
fruits: Vec<Instance<Fruit>>
}§Features
- Improved type safety and readability for Bevy code
- Ability to define custom entity kinds
- Ability to define commands for specific entity kinds
- No runtime overhead
- Zero boilerplate
This crate may be used separately, but is also included as part of 🍸 Moonshine Core.
§Usage
§Kind and Instance<T>
By definition, an Entity is of kind T if it matches Query<(), <T as Kind>::Filter>.
Any Component automatically implements the Kind trait:
impl<T: Component> Kind for T {
type Filter = With<T>;
}An Instance<T> represents Entity of kind T. It is designed to behave exactly like an Entity with some added benefits.
This means you may use any component as an argument to Instance:
use bevy::prelude::*;
use moonshine_kind::prelude::*;
#[derive(Component)]
struct Apple;
fn count_apples(apples: Query<Instance<Apple>>) {
println!("Apples: {}", apples.iter().count());
}Alternatively, you may also define your own kind by implementing the Kind trait:
use bevy::prelude::*;
use moonshine_kind::prelude::*;
#[derive(Component)]
struct Apple;
#[derive(Component)]
struct Orange;
struct Fruit;
impl Kind for Fruit {
type Filter = Or<(With<Apple>, With<Orange>)>;
}
fn count_fruits(fruits: Query<Instance<Fruit>>) {
println!("Fruits: {}", fruits.iter().count());
}§InstanceRef<T> and InstanceMut<T>
If a Kind is also a Component, you may use InstanceRef<T> and InstanceMut<T> to access the Instance<T> and the associated component data with a single query term:
use bevy::prelude::*;
use moonshine_kind::prelude::*;
#[derive(Component)]
struct Apple {
freshness: f32
}
impl Apple {
fn is_fresh(&self) -> bool {
self.freshness >= 1.0
}
}
fn fresh_apples(
apples: Query<InstanceRef<Apple>>
) -> Vec<Instance<Apple>> {
let mut fresh_apples = Vec::new();
for apple in apples.iter() {
if apple.is_fresh() {
fresh_apples.push(apple.instance());
}
}
fresh_apples
}In other words, InstanceRef<T> is analogous to (Instance<T>, &T) and InstanceMut<T> is analogous to (Instance<T>, &mut T).
§InstanceCommands<T>
You may also extend InstanceCommands<T> to define Commands specific to a Kind:
use bevy::prelude::*;
use moonshine_kind::prelude::*;
struct Fruit;
impl Kind for Fruit {
type Filter = (/* ... */);
}
#[derive(Component)]
struct Human;
trait Eat {
fn eat(&mut self, fruit: Instance<Fruit>);
}
// Humans can eat:
impl Eat for InstanceCommands<'_, Human> {
fn eat(&mut self, fruit: Instance<Fruit>) {
// ...
}
}
fn eat(
human: Query<Instance<Human>>,
fruits: Query<Instance<Fruit>>, mut commands: Commands
) {
let human = human.single().unwrap();
if let Some(fruit) = fruits.iter().next() {
commands.instance(human).eat(fruit);
}
}InstanceCommands<T> behaves like EntityCommands, and is accessible via commands.instance(...).
§Instance<Any>
When writing generic code, it may be desirable to have an instance that can be of Any kind:
use moonshine_kind::{prelude::*, Any};
struct Container<T: Kind = Any> {
items: Vec<Instance<T>>
}Instance<Any> is functionally and semantically identical to a regular Entity.
§CastInto
By definition, any Instance<T> is safely convertible to any Instance<U> if CastInto<U> is implemented for T.
This is done using the CastInto trait. The kind macro may be used to conveniently implement this:
use bevy::prelude::*;
use moonshine_kind::prelude::*;
#[derive(Component)]
struct Apple;
struct Fruit;
impl Kind for Fruit {
type Filter = With<Apple>;
}
// An Apple is a Fruit because we said so:
impl CastInto<Fruit> for Apple {}
fn init_apple(apple: Instance<Apple>, commands: &mut Commands) {
init_fruit(apple.cast_into(), commands);
// ...
}
fn init_fruit(fruit: Instance<Fruit>, commands: &mut Commands) {
// ...
}Required Components are a great way to enforce this type of “kind polymorphism” at runtime:
use bevy::prelude::*;
use moonshine_kind::prelude::*;
#[derive(Component, Default)]
struct Apple;
#[derive(Component)]
#[require(Apple)] // Require all GrannySmith instances to also have Apple
struct GrannySmith;
impl CastInto<Apple> for GrannySmith {} // GrannySmith is an Apple; Guaranteed!§Examples
See examples/fruits.rs for a complete example.
§Limitations
§Instance Invalidation
This crate does not monitor instances for invalidation.
This means that if an entity is modified in such a way that it no longer matches some Kind T (such as removing Component T), any Instance<T> which references it would be invalid.
It is recommended to avoid using kind semantics for components that may be removed at runtime without despawning their associated entity.
However, if necessary, you may check instances for validity prior to usage:
use bevy::prelude::*;
use moonshine_kind::prelude::*;
struct Fruit;
impl Kind for Fruit {
type Filter = (/* ... */);
}
fn prune_fruits(
mut fruits: Vec<Instance<Fruit>>,
query: &Query<(), <Fruit as Kind>::Filter>
) -> Vec<Instance<Fruit>> {
fruits.retain(|fruit| {
// Is the Fruit still a Fruit?
query.get(fruit.entity()).is_ok()
});
fruits
}§Support
Please post an issue for any bugs, questions, or suggestions.
You may also contact me on the official Bevy Discord server as @Zeenobit.
Modules§
Macros§
- impl_
entity_ event_ from_ instance - A macro which implements
EntityEventfrom anInstance<T>.
Structs§
- Any
- Represents the kind of any
Entity. - Instance
- Represents an
EntityofKindT. - Instance
Commands EntityCommandswith kind semantics.- Instance
Mut - A
QueryDataitem which represents a mutable reference to anInstance<T>and its associatedComponent. - Instance
Ref - A
QueryDataitem which represents a reference to anInstance<T>and its associatedComponent.
Traits§
- Cast
Into - A trait which allows safe casting from one
Kindto another. - Component
Instance - Extension trait used to get
Componentdata from anInstance<T>viaWorld. - Contains
Instance - Similar to
ContainsEntity, but forInstance<T>. - GetInstance
Commands - Extension trait to access
InstanceCommands<T>fromCommands. - Insert
Instance - Extension trait used to insert instances via
EntityCommands. - Insert
Instance World - Extension trait used to insert instances via
EntityWorldMut. - Kind
- A type which represents the kind of an
Entity. - Spawn
Instance - Extension trait used to spawn instances via
Commands. - Spawn
Instance World - Extension trait used to spawn instances via
World.