Unconventional framework for making games in Bevy
https://github.com/Zeenobit/moonshine_core.git
Unconventional cocktail of libraries to make ECS-driven development easier and safer in Bevy.
See individual crates for detailed documentation and examples.
Type safety solution for Bevy entities:
use bevy::prelude::*;
use moonshine_core::prelude::*;
#[derive(Component)]
struct Fruit;
#[derive(Component)]
struct FruitBasket {
fruits: Vec<Instance<Fruit>>
}
Ergonomic wrapper for managing complex enttiy hierarchies:
use bevy::prelude::*;
use moonshine_core::prelude::*;
#[derive(Component)]
struct Bird;
#[derive(Component)]
struct Flying;
fn setup_bird(birds: Objects<Bird, Added<Flying>>, mut commands: Commands) {
for bird in birds.iter() {
if let Some(wings) = bird.find_by_path("./Wings") {
for wing in wings.children() {
// TODO: Flap! Flap!
}
}
}
}
Save/Load framework for managing persistent game state:
use bevy::prelude::*;
use moonshine_core::prelude::*;
#[derive(Component, Reflect)]
#[reflect(Component)]
#[require(Save)]
struct Player { /* ... */ }
fn main() {
let mut app = App::new();
app.add_plugins(DefaultPlugins)
.register_type::<Player>()
.add_observer(save_on_default_event)
.add_observer(load_on_default_event)
.add_systems(Startup, spawn_player)
.add_systems(
Update,
(trigger_save, trigger_load)
);
// app.run();
}
fn spawn_player(mut commands: Commands) {
commands.spawn(Player { /* ... */ });
}
fn trigger_save(key: Res<ButtonInput<KeyCode>>, mut commands: Commands) {
if key.just_pressed(KeyCode::KeyS) {
commands.trigger_save(SaveWorld::default_into_file("world.ron"));
}
}
fn trigger_load(key: Res<ButtonInput<KeyCode>>, mut commands: Commands) {
if key.just_pressed(KeyCode::KeyL) {
commands.trigger_load(LoadWorld::default_from_file("world.ron"));
}
}
Cheap, fast, mostly unique identifiers designed for Bevy:
use bevy::prelude::*;
use moonshine_tag::prelude::*;
tags! { APPLE, ORANGE, JUICY, CRUNCHY, POISONED }
let mut world = World::new();
// Define some fruits!
let fruits = [
Tags::from([APPLE, CRUNCHY]),
Tags::from([ORANGE, JUICY]),
Tags::from([APPLE, CRUNCHY, POISONED])
];
// Only crunchy, edible apples, please! :)
let filter: TagFilter = tag_filter!([APPLE, CRUNCHY] & ![POISONED]);
for fruit in &fruits {
if filter.allows(fruit) {
world.spawn(fruit.clone());
}
}
# assert!(fruits[0].matches(&filter));
# assert!(!fruits[1].matches(&filter));
# assert!(!fruits[2].matches(&filter));
Collection of generic utilities for improved safety, diagnostics, and ergonomics.
kind! macro in favor of manual implementation of CastInto.Instance<T>::as_trigger_target()Filter back to TagFilterallows functions into matches functionstag_filter!SingleEvent featurePlease post an issue for any bugs, questions, or suggestions.
You may also contact me on the official Bevy Discord server as @Zeenobit.