chore(): cargo fmt (#21)

This commit is contained in:
GitGhillie 2023-10-13 12:53:26 +02:00 committed by GitHub
parent a1c32ae1d6
commit f41a315563
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
46 changed files with 1136 additions and 1280 deletions

View File

@ -1,66 +1,66 @@
use bevy::prelude::*;
use bevy::ecs::system::Command;
use bevy::prelude::*;
// modified version from https://github.com/bevyengine/bevy/issues/1515,
// more specifically https://gist.github.com/nwtnni/85d6b87ae75337a522166c500c9a8418
// to work with Bevy 0.11
pub struct CloneEntity {
pub source: Entity,
pub destination: Entity,
pub source: Entity,
pub destination: Entity,
}
impl CloneEntity {
// Copy all components from an entity to another.
// Using an entity with no components as the destination creates a copy of the source entity.
// Panics if:
// - the components are not registered in the type registry,
// - the world does not have a type registry
// - the source or destination entity do not exist
fn clone_entity(self, world: &mut World) {
let components = {
let registry = world.get_resource::<AppTypeRegistry>().unwrap().read();
// Copy all components from an entity to another.
// Using an entity with no components as the destination creates a copy of the source entity.
// Panics if:
// - the components are not registered in the type registry,
// - the world does not have a type registry
// - the source or destination entity do not exist
fn clone_entity(self, world: &mut World) {
let components = {
let registry = world.get_resource::<AppTypeRegistry>().unwrap().read();
world
.get_entity(self.source)
.unwrap()
.archetype()
.components()
.map(|component_id| {
world
.components()
.get_info(component_id)
.unwrap()
.type_id()
.unwrap()
})
.map(|type_id| {
// println!("type_id {:?}", type_id);
registry
.get(type_id)
.unwrap()
.data::<ReflectComponent>()
.unwrap()
.clone()
})
.collect::<Vec<_>>()
};
world
.get_entity(self.source)
.unwrap()
.archetype()
.components()
.map(|component_id| {
world
.components()
.get_info(component_id)
.unwrap()
.type_id()
.unwrap()
})
.map(|type_id| {
// println!("type_id {:?}", type_id);
registry
.get(type_id)
.unwrap()
.data::<ReflectComponent>()
.unwrap()
.clone()
})
.collect::<Vec<_>>()
};
for component in components {
let source = component
.reflect(world.get_entity(self.source).unwrap())
.unwrap()
.clone_value();
for component in components {
let source = component
.reflect(world.get_entity(self.source).unwrap())
.unwrap()
.clone_value();
let mut destination = world.get_entity_mut(self.destination).unwrap();
let mut destination = world.get_entity_mut(self.destination).unwrap();
component.apply_or_insert(&mut destination, &*source);
}
}
component.apply_or_insert(&mut destination, &*source);
}
}
}
// This allows the command to be used in systems
impl Command for CloneEntity {
fn apply(self, world: &mut World) {
self.clone_entity(world)
}
fn apply(self, world: &mut World) {
self.clone_entity(world)
}
}

View File

@ -1,5 +1,3 @@
pub mod spawn_from_blueprints;
pub use spawn_from_blueprints::*;
@ -16,28 +14,27 @@ use bevy_gltf_components::GltfComponentsSet;
#[derive(SystemSet, Debug, Hash, PartialEq, Eq, Clone)]
/// set for the two stages of blueprint based spawning :
pub enum GltfBlueprintsSet{
Spawn,
AfterSpawn,
pub enum GltfBlueprintsSet {
Spawn,
AfterSpawn,
}
#[derive(Bundle)]
pub struct BluePrintBundle {
pub blueprint: BlueprintName,
pub spawn_here: SpawnHere,
pub transform: TransformBundle
pub transform: TransformBundle,
}
impl Default for BluePrintBundle {
fn default() -> Self {
BluePrintBundle {
blueprint: BlueprintName("default".into()),
spawn_here: SpawnHere,
transform: TransformBundle::default()
fn default() -> Self {
BluePrintBundle {
blueprint: BlueprintName("default".into()),
spawn_here: SpawnHere,
transform: TransformBundle::default(),
}
}
}
}
#[derive(Clone, Resource)]
pub(crate) struct BluePrintsConfig {
pub(crate) library_folder: PathBuf,
@ -45,49 +42,49 @@ pub(crate) struct BluePrintsConfig {
#[derive(Debug, Clone)]
pub struct BlueprintsPlugin {
/// The base folder where library/blueprints assets are loaded from, relative to the executable.
pub library_folder: PathBuf,
/// The base folder where library/blueprints assets are loaded from, relative to the executable.
pub library_folder: PathBuf,
}
impl Default for BlueprintsPlugin {
fn default() -> Self {
Self {
library_folder: PathBuf::from("assets/models/library"),
}
}
fn default() -> Self {
Self {
library_folder: PathBuf::from("assets/models/library"),
}
}
}
impl Plugin for BlueprintsPlugin {
fn build(&self, app: &mut App) {
app
.register_type::<BlueprintName>()
.register_type::<SpawnHere>()
.insert_resource(BluePrintsConfig{library_folder: self.library_folder.clone()})
.configure_sets(
Update,
(GltfBlueprintsSet::Spawn, GltfBlueprintsSet::AfterSpawn).chain().after(GltfComponentsSet::Injection)
)
.add_systems(Update,
(spawn_from_blueprints)
// .run_if(in_state(AppState::AppRunning).or_else(in_state(AppState::LoadingGame))) // FIXME: how to replace this with a crate compatible version ?
.in_set(GltfBlueprintsSet::Spawn),
)
.add_systems(
Update,
(
// spawn_entities,
update_spawned_root_first_child,
apply_deferred,
cleanup_scene_instances,
apply_deferred,
)
.chain()
// .run_if(in_state(AppState::LoadingGame).or_else(in_state(AppState::AppRunning))) // FIXME: how to replace this with a crate compatible version ?
.in_set(GltfBlueprintsSet::AfterSpawn),
)
;
}
fn build(&self, app: &mut App) {
app.register_type::<BlueprintName>()
.register_type::<SpawnHere>()
.insert_resource(BluePrintsConfig {
library_folder: self.library_folder.clone(),
})
.configure_sets(
Update,
(GltfBlueprintsSet::Spawn, GltfBlueprintsSet::AfterSpawn)
.chain()
.after(GltfComponentsSet::Injection),
)
.add_systems(
Update,
(spawn_from_blueprints)
// .run_if(in_state(AppState::AppRunning).or_else(in_state(AppState::LoadingGame))) // FIXME: how to replace this with a crate compatible version ?
.in_set(GltfBlueprintsSet::Spawn),
)
.add_systems(
Update,
(
// spawn_entities,
update_spawned_root_first_child,
apply_deferred,
cleanup_scene_instances,
apply_deferred,
)
.chain()
// .run_if(in_state(AppState::LoadingGame).or_else(in_state(AppState::AppRunning))) // FIXME: how to replace this with a crate compatible version ?
.in_set(GltfBlueprintsSet::AfterSpawn),
);
}
}

View File

@ -1,6 +1,6 @@
use std::path::Path;
use bevy::{prelude::*, gltf::Gltf};
use bevy::{gltf::Gltf, prelude::*};
use crate::BluePrintsConfig;
@ -9,12 +9,12 @@ use crate::BluePrintsConfig;
pub struct GameWorldTag;
/// Main component for the blueprints
#[derive(Component, Reflect, Default, Debug, )]
#[derive(Component, Reflect, Default, Debug)]
#[reflect(Component)]
pub struct BlueprintName(pub String);
/// flag component needed to signify the intent to spawn a Blueprint
#[derive(Component, Reflect, Default, Debug, )]
#[derive(Component, Reflect, Default, Debug)]
#[reflect(Component)]
pub struct SpawnHere;
@ -22,7 +22,6 @@ pub struct SpawnHere;
/// FlagComponent for spawned entity
pub struct Spawned;
#[derive(Component)]
/// helper component, just to transfer some data
pub(crate) struct Original(pub Entity);
@ -34,53 +33,66 @@ pub struct SpawnedRoot;
/// main spawning functions,
/// * also takes into account the already exisiting "override" components, ie "override components" > components from blueprint
pub(crate) fn spawn_from_blueprints(
spawn_placeholders: Query<(Entity, &Name, &BlueprintName, &Transform), (Added<BlueprintName>, Added<SpawnHere>, Without<Spawned>, Without<SpawnedRoot>)>,
spawn_placeholders: Query<
(Entity, &Name, &BlueprintName, &Transform),
(
Added<BlueprintName>,
Added<SpawnHere>,
Without<Spawned>,
Without<SpawnedRoot>,
),
>,
mut commands: Commands,
mut game_world: Query<(Entity, &Children), With<GameWorldTag>>,
assets_gltf: Res<Assets<Gltf>>,
asset_server: Res<AssetServer>,
blueprints_config: Res<BluePrintsConfig>
){
blueprints_config: Res<BluePrintsConfig>,
) {
for (entity, name, blupeprint_name, global_transform) in spawn_placeholders.iter() {
info!("need to spawn {:?}", blupeprint_name.0);
let what = &blupeprint_name.0;
let model_file_name = format!("{}.glb",&what);
let model_path = Path::new(&blueprints_config.library_folder)
.join(Path::new(model_file_name.as_str()));
let model_file_name = format!("{}.glb", &what);
let model_path =
Path::new(&blueprints_config.library_folder).join(Path::new(model_file_name.as_str()));
info!("attempting to spawn {:?}",model_path);
let scene:Handle<Gltf> = asset_server.load(model_path);
info!("attempting to spawn {:?}", model_path);
let scene: Handle<Gltf> = asset_server.load(model_path);
// let scene = game_assets.models.get(&model_path).expect(&format!("no matching model {:?} found", model_path));
let world = game_world.single_mut();
let world = world.1[0]; // FIXME: dangerous hack because our gltf data have a single child like this, but might not always be the case
let gltf = assets_gltf.get(&scene).expect("this gltf should have been loaded");
let gltf = assets_gltf
.get(&scene)
.expect("this gltf should have been loaded");
// WARNING we work under the assumtion that there is ONLY ONE named scene, and that the first one is the right one
let main_scene_name =gltf.named_scenes.keys().nth(0).expect("there should be at least one named scene in the gltf file to spawn");
let main_scene_name = gltf
.named_scenes
.keys()
.nth(0)
.expect("there should be at least one named scene in the gltf file to spawn");
let scene = &gltf.named_scenes[main_scene_name];
//spawn_requested_events.send(SpawnRequestedEvent { what: "enemy".into(), position, amount: 1, spawner_id: None });
let child_scene = commands.spawn(
(
SceneBundle {
scene: scene.clone(),
transform: global_transform.clone(),
..Default::default()
},
bevy::prelude::Name::from(["scene_wrapper", &name.clone()].join("_") ),
// Parent(world) // FIXME/ would be good if this worked directly
SpawnedRoot,
/*AnimationHelper{ // TODO: insert this at the ENTITY level, not the scene level
named_animations: gltf.named_animations.clone(),
// animations: gltf.named_animations.values().clone()
},*/
Original(entity)
)).id();
commands.entity(world).add_child(child_scene);
let child_scene = commands
.spawn((
SceneBundle {
scene: scene.clone(),
transform: global_transform.clone(),
..Default::default()
},
bevy::prelude::Name::from(["scene_wrapper", &name.clone()].join("_")),
// Parent(world) // FIXME/ would be good if this worked directly
SpawnedRoot,
/*AnimationHelper{ // TODO: insert this at the ENTITY level, not the scene level
named_animations: gltf.named_animations.clone(),
// animations: gltf.named_animations.values().clone()
},*/
Original(entity),
))
.id();
commands.entity(world).add_child(child_scene);
}
}

View File

@ -4,20 +4,17 @@ use bevy::utils::HashMap;
use super::{CloneEntity, SpawnHere};
use super::{Original, SpawnedRoot};
// FIXME: move to more relevant module
#[derive(Component, Reflect, Default, Debug, )]
#[derive(Component, Reflect, Default, Debug)]
#[reflect(Component)]
pub struct AnimationHelper {
pub named_animations: HashMap<String, Handle<AnimationClip>>,
pub named_animations: HashMap<String, Handle<AnimationClip>>,
}
#[derive(Component)]
/// FlagComponent for dynamically spawned scenes
pub(crate) struct SpawnedRootProcessed;
/// this system updates the first (and normally only) child of a scene flaged SpawnedRoot
/// - adds a name based on parent component (spawned scene) which is named on the scene name/prefab to be instanciated
/// - adds the initial physics impulse (FIXME: we would need to add a temporary physics component to those who do not have it)
@ -26,15 +23,18 @@ pub(crate) struct SpawnedRootProcessed;
// - scene instance -> does not work
// it might be due to how we add components to the PARENT item in gltf to components
pub(crate) fn update_spawned_root_first_child(
// all_children: Query<(Entity, &Children)>,
unprocessed_entities :Query<(Entity, &Children, &Name, &Parent, &Original ), (With<SpawnedRoot>, Without<SpawnedRootProcessed>)>,
mut commands: Commands,
// all_children: Query<(Entity, &Children)>,
unprocessed_entities: Query<
(Entity, &Children, &Name, &Parent, &Original),
(With<SpawnedRoot>, Without<SpawnedRootProcessed>),
>,
mut commands: Commands,
// FIXME: should be done at a more generic gltf level
animation_helpers: Query<&AnimationHelper>,
added_animation_helpers : Query<(Entity, &AnimationPlayer), Added<AnimationPlayer>>
){
/*
// FIXME: should be done at a more generic gltf level
animation_helpers: Query<&AnimationHelper>,
added_animation_helpers: Query<(Entity, &AnimationPlayer), Added<AnimationPlayer>>,
) {
/*
currently we have
- scene instance
- root node ?
@ -56,82 +56,80 @@ pub(crate) fn update_spawned_root_first_child(
FIME: this is all highly dependent on the hierachy ;..
*/
for (scene_instance, children, name, parent, original) in unprocessed_entities.iter() {
//
if children.len() == 0 {
warn!("timing issue ! no children found, please restart your bevy app (bug being investigated)");
// println!("children of scene {:?}", children);
continue;
}
// the root node is the first & normally only child inside a scene, it is the one that has all relevant components
let root_entity = children.first().unwrap(); //FIXME: and what about childless ones ?? => should not be possible normally
// let root_entity_data = all_children.get(*root_entity).unwrap();
// fixme : randomization should be controlled via parameters, perhaps even the seed could be specified ?
// use this https://rust-random.github.io/book/guide-seeding.html#a-simple-number, blenders seeds are also uInts
// also this is not something we want every time, this should be a settable parameter when requesting a spawn
// add missing name of entity, based on the wrapper's name
let name= name.clone().replace("scene_wrapper_", "");
// this is our new actual entity
commands.entity(*root_entity).insert((
bevy::prelude::Name::from(name.clone()),
// ItemType {name},
// Spawned, // FIXME: not sure
));
// flag the spawned_root as being processed
commands.entity(scene_instance).insert(SpawnedRootProcessed);
// let original_transforms =
// parent is either the world or an entity with a marker (BlueprintName)
commands.entity(parent.get()).add_child(*root_entity);
// commands.entity(*root_entity).despawn_recursive();
// commands.entity(parent.get()).push_children(&actual_stuff);
//commands.entity(*root_entity).log_components();
let matching_animation_helper = animation_helpers.get(scene_instance);
// println!("WE HAVE SOME ADDED ANIMATION PLAYERS {:?}", matching_animation_helper);
if let Ok(anim_helper) = matching_animation_helper{
for (added, _) in added_animation_helpers.iter(){
commands.entity(added).insert(
AnimationHelper{ // TODO: insert this at the ENTITY level, not the scene level
named_animations: anim_helper.named_animations.clone(),
// animations: gltf.named_animations.values().clone()
},
);
for (scene_instance, children, name, parent, original) in unprocessed_entities.iter() {
//
if children.len() == 0 {
warn!("timing issue ! no children found, please restart your bevy app (bug being investigated)");
// println!("children of scene {:?}", children);
continue;
}
}
// the root node is the first & normally only child inside a scene, it is the one that has all relevant components
let root_entity = children.first().unwrap(); //FIXME: and what about childless ones ?? => should not be possible normally
// let root_entity_data = all_children.get(*root_entity).unwrap();
commands.add(CloneEntity {
source: original.0,
destination: *root_entity,
});
// fixme : randomization should be controlled via parameters, perhaps even the seed could be specified ?
// use this https://rust-random.github.io/book/guide-seeding.html#a-simple-number, blenders seeds are also uInts
// also this is not something we want every time, this should be a settable parameter when requesting a spawn
// remove the original entity, now that we have cloned it into the spawned scenes first child
commands.entity(original.0).despawn_recursive();
commands.entity(*root_entity).remove::<SpawnHere>();
}
// add missing name of entity, based on the wrapper's name
let name = name.clone().replace("scene_wrapper_", "");
// this is our new actual entity
commands.entity(*root_entity).insert((
bevy::prelude::Name::from(name.clone()),
// ItemType {name},
// Spawned, // FIXME: not sure
));
// flag the spawned_root as being processed
commands.entity(scene_instance).insert(SpawnedRootProcessed);
// let original_transforms =
// parent is either the world or an entity with a marker (BlueprintName)
commands.entity(parent.get()).add_child(*root_entity);
// commands.entity(*root_entity).despawn_recursive();
// commands.entity(parent.get()).push_children(&actual_stuff);
//commands.entity(*root_entity).log_components();
let matching_animation_helper = animation_helpers.get(scene_instance);
// println!("WE HAVE SOME ADDED ANIMATION PLAYERS {:?}", matching_animation_helper);
if let Ok(anim_helper) = matching_animation_helper {
for (added, _) in added_animation_helpers.iter() {
commands.entity(added).insert(AnimationHelper {
// TODO: insert this at the ENTITY level, not the scene level
named_animations: anim_helper.named_animations.clone(),
// animations: gltf.named_animations.values().clone()
});
}
}
commands.add(CloneEntity {
source: original.0,
destination: *root_entity,
});
// remove the original entity, now that we have cloned it into the spawned scenes first child
commands.entity(original.0).despawn_recursive();
commands.entity(*root_entity).remove::<SpawnHere>();
}
}
/// cleans up dynamically spawned scenes so that they get despawned if they have no more children
pub(crate) fn cleanup_scene_instances(
scene_instances: Query<(Entity, &Children), With<SpawnedRootProcessed>>,
without_children: Query<Entity, (With<SpawnedRootProcessed>, Without<Children>)>,// if there are not children left, bevy removes Children ?
mut commands: Commands
){
for (entity, children) in scene_instances.iter(){
if children.len() == 0{ // it seems this does not happen ?
info!("cleaning up emptied spawned scene instance");
commands.entity(entity).despawn_recursive();
}
}
for entity in without_children.iter() {
info!("cleaning up emptied spawned scene instance");
commands.entity(entity).despawn_recursive();
}
scene_instances: Query<(Entity, &Children), With<SpawnedRootProcessed>>,
without_children: Query<Entity, (With<SpawnedRootProcessed>, Without<Children>)>, // if there are not children left, bevy removes Children ?
mut commands: Commands,
) {
for (entity, children) in scene_instances.iter() {
if children.len() == 0 {
// it seems this does not happen ?
info!("cleaning up emptied spawned scene instance");
commands.entity(entity).despawn_recursive();
}
}
for entity in without_children.iter() {
info!("cleaning up emptied spawned scene instance");
commands.entity(entity).despawn_recursive();
}
}

View File

@ -3,13 +3,13 @@ use core::ops::Deref;
use ron::Value;
use serde::de::DeserializeSeed;
use bevy::prelude::{ResMut, Assets, info, debug, Name, Parent, warn};
use bevy::ecs::{entity::Entity, reflect::ReflectComponent};
use bevy::gltf::{Gltf, GltfExtras};
use bevy::prelude::{debug, info, warn, Assets, Name, Parent, ResMut};
use bevy::reflect::serde::UntypedReflectDeserializer;
use bevy::reflect::{Reflect, TypeInfo, TypeRegistryInternal};
use bevy::scene::Scene;
use bevy::utils::HashMap;
use bevy::reflect::serde::UntypedReflectDeserializer;
use bevy::reflect::{TypeRegistryInternal, TypeInfo, Reflect};
use bevy::gltf::{Gltf, GltfExtras};
use super::capitalize_first_letter;
@ -18,216 +18,227 @@ pub fn gltf_extras_to_components(
gltf: &mut Gltf,
scenes: &mut ResMut<Assets<Scene>>,
type_registry: impl Deref<Target = TypeRegistryInternal>,
gltf_name: &str
){
gltf_name: &str,
) {
let mut added_components = 0;
for (_name, scene) in &gltf.named_scenes {
debug!("gltf: {:?} scene name {:?}", gltf_name, _name);
debug!("gltf: {:?} scene name {:?}", gltf_name, _name);
let scene = scenes.get_mut(scene).unwrap();
let scene = scenes.get_mut(scene).unwrap();
let mut query = scene.world.query::<(Entity, &Name, &GltfExtras, &Parent)>();
let mut entity_components: HashMap<Entity, Vec<Box<dyn Reflect>> > = HashMap::new();
for (entity, name, extras, parent) in query.iter(&scene.world) {
debug!("Name: {}, entity {:?}, parent: {:?}", name, entity, parent);
let reflect_components = ronstring_to_reflect_component(&extras.value, &type_registry);
added_components = reflect_components.len();
debug!("Found components {}", added_components);
let mut query = scene.world.query::<(Entity, &Name, &GltfExtras, &Parent)>();
let mut entity_components: HashMap<Entity, Vec<Box<dyn Reflect>>> = HashMap::new();
for (entity, name, extras, parent) in query.iter(&scene.world) {
debug!("Name: {}, entity {:?}, parent: {:?}", name, entity, parent);
let reflect_components = ronstring_to_reflect_component(&extras.value, &type_registry);
added_components = reflect_components.len();
debug!("Found components {}", added_components);
// we assign the components specified /xxx_components objects to their parent node
let mut target_entity = entity;
// if the node contains "components" or ends with "_pa" (ie add to parent), the components will not be added to the entity itself but to its parent
// this is mostly used for Blender collections
if name.as_str().contains("components") || name.as_str().ends_with("_pa") {
debug!("adding components to parent");
target_entity = parent.get();
// we assign the components specified /xxx_components objects to their parent node
let mut target_entity = entity;
// if the node contains "components" or ends with "_pa" (ie add to parent), the components will not be added to the entity itself but to its parent
// this is mostly used for Blender collections
if name.as_str().contains("components") || name.as_str().ends_with("_pa") {
debug!("adding components to parent");
target_entity = parent.get();
}
debug!("adding to {:?}", target_entity);
// if there where already components set to be added to this entity (for example when entity_data was refering to a parent), update the vec of entity_components accordingly
// this allows for example blender collection to provide basic ecs data & the instances to override/ define their own values
if entity_components.contains_key(&target_entity) {
let mut updated_components: Vec<Box<dyn Reflect>> = Vec::new();
let current_components = &entity_components[&target_entity];
// first inject the current components
for component in current_components {
updated_components.push(component.clone_value());
}
// then inject the new components: this also enables overwrite components set in the collection
for component in reflect_components {
updated_components.push(component.clone_value());
}
entity_components.insert(target_entity, updated_components);
} else {
entity_components.insert(target_entity, reflect_components);
}
// shorthand, did not manage to get it working
/* entity_components.insert(
target_entity,
if entity_components.contains_key(&target_entity) {
entity_components[&target_entity].push(reflect_components) } else { reflect_components }
);*/
debug!("-----value {:?}", &extras.value);
}
debug!("adding to {:?}", target_entity);
// GltfNode
// find a way to link this name to the current entity ? => WOULD BE VERY USEFULL for animations & co !!
debug!("done pre-processing components, now adding them to entities");
for (entity, components) in entity_components {
if !components.is_empty() {
debug!("--entity {:?}, components {}", entity, components.len());
}
for component in components {
let mut entity_mut = scene.world.entity_mut(entity);
debug!("------adding {} {:?}", component.type_name(), component);
// if there where already components set to be added to this entity (for example when entity_data was refering to a parent), update the vec of entity_components accordingly
// this allows for example blender collection to provide basic ecs data & the instances to override/ define their own values
if entity_components.contains_key(&target_entity) {
let mut updated_components: Vec<Box<dyn Reflect>> = Vec::new();
let current_components = &entity_components[&target_entity];
// first inject the current components
for component in current_components {
updated_components.push(component.clone_value());
}
// then inject the new components: this also enables overwrite components set in the collection
for component in reflect_components {
updated_components.push(component.clone_value());
}
entity_components.insert(target_entity, updated_components);
type_registry
.get_with_name(component.type_name())
.unwrap() // Component was successfully deserialized, it has to be in the registry
.data::<ReflectComponent>()
.unwrap() // Hopefully, the component deserializer ensures those are components
.insert(&mut entity_mut, &*component);
}else {
entity_components.insert(target_entity, reflect_components);
}
// shorthand, did not manage to get it working
/* entity_components.insert(
target_entity,
if entity_components.contains_key(&target_entity) {
entity_components[&target_entity].push(reflect_components) } else { reflect_components }
);*/
debug!("-----value {:?}", &extras.value);
}
// GltfNode
// find a way to link this name to the current entity ? => WOULD BE VERY USEFULL for animations & co !!
debug!("done pre-processing components, now adding them to entities");
for (entity, components) in entity_components {
if !components.is_empty() {
debug!("--entity {:?}, components {}", entity, components.len());
}
for component in components {
let mut entity_mut = scene.world.entity_mut(entity);
debug!("------adding {} {:?}", component.type_name(), component);
type_registry
.get_with_name(component.type_name())
.unwrap() // Component was successfully deserialized, it has to be in the registry
.data::<ReflectComponent>()
.unwrap() // Hopefully, the component deserializer ensures those are components
.insert(&mut entity_mut, &*component)
;
// info!("all components {:?}", scene.world.entity(entity).archetype().components());
// scene.world.components().
// info!("all components {:?}", scene.world.entity(entity).archetype().components());
// scene.world.components().
// TODO: how can we insert any additional components "by hand" here ?
}
// let entity_mut = scene.world.entity_mut(entity);
// let archetype = entity_mut.archetype().clone();
// let _all_components = archetype.components();
}
// let entity_mut = scene.world.entity_mut(entity);
// let archetype = entity_mut.archetype().clone();
// let _all_components = archetype.components();
if added_components > 0 {
debug!("------done adding {} components", added_components);
if added_components > 0 {
debug!("------done adding {} components", added_components);
}
}
}
}
info!("done extracting gltf_extras /n");
}
}
pub fn ronstring_to_reflect_component(
pub fn ronstring_to_reflect_component(
ron_string: &String,
type_registry: &TypeRegistryInternal
) -> Vec<Box<dyn Reflect>> {
type_registry: &TypeRegistryInternal,
) -> Vec<Box<dyn Reflect>> {
let lookup: HashMap<String, Value> = ron::from_str(ron_string.as_str()).unwrap();
let mut components: Vec<Box<dyn Reflect>> = Vec::new();
for (key, value) in lookup.into_iter() {
let type_string = key.replace("component: ", "").trim().to_string();
let capitalized_type_name = capitalize_first_letter(type_string.as_str());
let mut parsed_value:String;
match value.clone() {
Value::String(str) => {
parsed_value = str;
}
_=> {
parsed_value = ron::to_string(&value).unwrap().to_string()
}
}
if let Some(type_registration) = type_registry.get_with_short_name(capitalized_type_name.as_str()) {
// println!("TYPE INFO {:?}", type_registration.type_info());
match type_registration.type_info() {
TypeInfo::TupleStruct (info) => {
// we handle tupple strucs with only one field differently, as Blender's custom properties with custom ui (float, int, bool, etc) always give us a tupple struct
if info.field_len() == 1 {
let field = info.field_at(0).expect("we should always have at least one field here");
let field_name = field.type_name();
// TODO: find a way to cast with typeId instead of this matching
/*match field.type_id(){
TypeId::of::<f32>() => {
println!("WE HAVE A f32");
}
}
Vec3 => {
println!("WE HAVE A VEC3");
let bla:Vec3 = ron::from_str(&parsed_value).unwrap();
println!("bla {}", bla)
}
_ =>{}
}*/
let mut formated = parsed_value.clone();
match field_name {
"f32" => {
formated = parsed_value.parse::<f32>().unwrap().to_string();
}
"f64" => {
formated = parsed_value.parse::<f64>().unwrap().to_string();
}
"u8" => {
formated = parsed_value.parse::<u8>().unwrap().to_string();
}
"u16" => {
formated = parsed_value.parse::<u16>().unwrap().to_string();
}
"u32" => {
formated = parsed_value.parse::<u32>().unwrap().to_string();
}
"u64" => {
formated = parsed_value.parse::<u64>().unwrap().to_string();
}
"u128" => {
formated = parsed_value.parse::<u128>().unwrap().to_string();
}
"glam::f32::vec2::Vec2" => {
let parsed: Vec<f32> = ron::from_str(&parsed_value).unwrap();
formated = format!("(x:{},y:{})", parsed[0], parsed[1]);
}
"glam::f32::vec3::Vec3" => {
let parsed: Vec<f32> = ron::from_str(&parsed_value).unwrap();
formated = format!("(x:{},y:{},z:{})", parsed[0], parsed[1], parsed[2]);
},
"bevy_render::color::Color" => {
let parsed: Vec<f32> = ron::from_str(&parsed_value).unwrap();
if parsed.len() == 3 {
formated = format!("Rgba(red:{},green:{},blue:{}, alpha: 1.0)", parsed[0], parsed[1], parsed[2]);
}
if parsed.len() == 4 {
formated = format!("Rgba(red:{},green:{},blue:{}, alpha:{})", parsed[0], parsed[1], parsed[2], parsed[3]);
}
}
_ => {}
}
parsed_value = format!("({formated})");
let type_string = key.replace("component: ", "").trim().to_string();
let capitalized_type_name = capitalize_first_letter(type_string.as_str());
let mut parsed_value: String;
match value.clone() {
Value::String(str) => {
parsed_value = str;
}
}
_ => {}
_ => parsed_value = ron::to_string(&value).unwrap().to_string(),
}
// println!("parsed value {}",parsed_value);
if parsed_value.is_empty() {
parsed_value = "()".to_string();
if let Some(type_registration) =
type_registry.get_with_short_name(capitalized_type_name.as_str())
{
// println!("TYPE INFO {:?}", type_registration.type_info());
match type_registration.type_info() {
TypeInfo::TupleStruct(info) => {
// we handle tupple strucs with only one field differently, as Blender's custom properties with custom ui (float, int, bool, etc) always give us a tupple struct
if info.field_len() == 1 {
let field = info
.field_at(0)
.expect("we should always have at least one field here");
let field_name = field.type_name();
// TODO: find a way to cast with typeId instead of this matching
/*match field.type_id(){
TypeId::of::<f32>() => {
println!("WE HAVE A f32");
}
}
Vec3 => {
println!("WE HAVE A VEC3");
let bla:Vec3 = ron::from_str(&parsed_value).unwrap();
println!("bla {}", bla)
}
_ =>{}
}*/
let mut formated = parsed_value.clone();
match field_name {
"f32" => {
formated = parsed_value.parse::<f32>().unwrap().to_string();
}
"f64" => {
formated = parsed_value.parse::<f64>().unwrap().to_string();
}
"u8" => {
formated = parsed_value.parse::<u8>().unwrap().to_string();
}
"u16" => {
formated = parsed_value.parse::<u16>().unwrap().to_string();
}
"u32" => {
formated = parsed_value.parse::<u32>().unwrap().to_string();
}
"u64" => {
formated = parsed_value.parse::<u64>().unwrap().to_string();
}
"u128" => {
formated = parsed_value.parse::<u128>().unwrap().to_string();
}
"glam::f32::vec2::Vec2" => {
let parsed: Vec<f32> = ron::from_str(&parsed_value).unwrap();
formated = format!("(x:{},y:{})", parsed[0], parsed[1]);
}
"glam::f32::vec3::Vec3" => {
let parsed: Vec<f32> = ron::from_str(&parsed_value).unwrap();
formated =
format!("(x:{},y:{},z:{})", parsed[0], parsed[1], parsed[2]);
}
"bevy_render::color::Color" => {
let parsed: Vec<f32> = ron::from_str(&parsed_value).unwrap();
if parsed.len() == 3 {
formated = format!(
"Rgba(red:{},green:{},blue:{}, alpha: 1.0)",
parsed[0], parsed[1], parsed[2]
);
}
if parsed.len() == 4 {
formated = format!(
"Rgba(red:{},green:{},blue:{}, alpha:{})",
parsed[0], parsed[1], parsed[2], parsed[3]
);
}
}
_ => {}
}
parsed_value = format!("({formated})");
}
}
_ => {}
}
// println!("parsed value {}",parsed_value);
if parsed_value.is_empty() {
parsed_value = "()".to_string();
}
let ron_string = format!(
"{{ \"{}\":{} }}",
type_registration.type_name(),
parsed_value
);
// usefull to determine what an entity looks like Serialized
/*let test_struct = TuppleTestStr::default();
let serializer = ReflectSerializer::new(&test_struct, &type_registry);
let serialized =
ron::ser::to_string_pretty(&serializer, ron::ser::PrettyConfig::default()).unwrap();
println!("serialized Component {}", serialized);*/
// println!("component data ron string {}", ron_string);
let mut deserializer = ron::Deserializer::from_str(ron_string.as_str()).unwrap();
let reflect_deserializer = UntypedReflectDeserializer::new(type_registry);
let component = reflect_deserializer.deserialize(&mut deserializer).expect(
format!(
"failed to deserialize component {} with value: {:?}",
key, value
)
.as_str(),
);
components.push(component);
debug!("found type registration for {}", capitalized_type_name);
} else {
warn!("no type registration for {}", capitalized_type_name);
}
let ron_string = format!("{{ \"{}\":{} }}",
type_registration.type_name(),
parsed_value
);
// usefull to determine what an entity looks like Serialized
/*let test_struct = TuppleTestStr::default();
let serializer = ReflectSerializer::new(&test_struct, &type_registry);
let serialized =
ron::ser::to_string_pretty(&serializer, ron::ser::PrettyConfig::default()).unwrap();
println!("serialized Component {}", serialized);*/
// println!("component data ron string {}", ron_string);
let mut deserializer = ron::Deserializer::from_str(ron_string.as_str()).unwrap();
let reflect_deserializer = UntypedReflectDeserializer::new(type_registry);
let component = reflect_deserializer.deserialize(&mut deserializer).expect(format!("failed to deserialize component {} with value: {:?}", key, value).as_str());
components.push(component);
debug!("found type registration for {}", capitalized_type_name);
} else {
warn!("no type registration for {}", capitalized_type_name);
}
}
components
}
}

View File

@ -7,10 +7,7 @@ pub use gltf_to_components::*;
pub mod process_gltfs;
pub use process_gltfs::*;
use bevy::prelude::{
App,Plugin, Update, SystemSet, IntoSystemConfigs
};
use bevy::prelude::{App, IntoSystemConfigs, Plugin, SystemSet, Update};
/// A Bevy plugin for extracting components from gltf files and automatically adding them to the relevant entities
/// It will automatically run every time you load a gltf file
@ -45,30 +42,21 @@ use bevy::prelude::{
///}
/// ```
#[derive(SystemSet, Debug, Hash, PartialEq, Eq, Clone)]
/// systemset to order your systems after the component injection when needed
pub enum GltfComponentsSet{
Injection,
pub enum GltfComponentsSet {
Injection,
}
#[derive(Default)]
pub struct ComponentsFromGltfPlugin;
impl Plugin for ComponentsFromGltfPlugin {
fn build(&self, app: &mut App) {
app
.insert_resource(GltfLoadingTracker::new())
.add_systems(Update, (
track_new_gltf,
process_loaded_scenes,
))
.add_systems(Update,
(process_loaded_scenes)
.in_set(GltfComponentsSet::Injection)
)
;
}
fn build(&self, app: &mut App) {
app.insert_resource(GltfLoadingTracker::new())
.add_systems(Update, (track_new_gltf, process_loaded_scenes))
.add_systems(
Update,
(process_loaded_scenes).in_set(GltfComponentsSet::Injection),
);
}
}

View File

@ -1,33 +1,32 @@
use bevy::utils::HashSet;
use bevy::{prelude::*, asset::LoadState};
use bevy::gltf::Gltf;
use bevy::utils::HashSet;
use bevy::{asset::LoadState, prelude::*};
use super::gltf_extras_to_components;
#[derive(Resource)]
/// component to keep track of gltfs' loading state
pub struct GltfLoadingTracker{
pub struct GltfLoadingTracker {
pub loading_gltfs: HashSet<Handle<Gltf>>,
pub loaded_gltfs: HashSet<Handle<Gltf>>
}
pub loaded_gltfs: HashSet<Handle<Gltf>>,
}
impl GltfLoadingTracker {
pub fn new() -> GltfLoadingTracker {
GltfLoadingTracker {
loaded_gltfs : HashSet::new(),
loading_gltfs: HashSet::new()
impl GltfLoadingTracker {
pub fn new() -> GltfLoadingTracker {
GltfLoadingTracker {
loaded_gltfs: HashSet::new(),
loading_gltfs: HashSet::new(),
}
}
pub fn add_scene(&mut self, handle: Handle<Gltf>) {
}
pub fn add_scene(&mut self, handle: Handle<Gltf>) {
self.loading_gltfs.insert(handle);
}
}
}
}
pub fn track_new_gltf(
pub fn track_new_gltf(
mut tracker: ResMut<GltfLoadingTracker>,
mut events: EventReader<AssetEvent<Gltf>>,
) {
) {
for event in events.iter() {
if let AssetEvent::Created { handle } = event {
tracker.add_scene(handle.clone());
@ -37,21 +36,22 @@ pub struct GltfLoadingTracker{
events.clear();
}
pub fn process_loaded_scenes(
pub fn process_loaded_scenes(
mut gltfs: ResMut<Assets<Gltf>>,
mut scenes: ResMut<Assets<Scene>>,
mut tracker: ResMut<GltfLoadingTracker>,
app_type_registry: Res<AppTypeRegistry>,
asset_server: Res<AssetServer>,
) {
) {
let mut loaded_gltfs = Vec::new();
for gltf in &tracker.loading_gltfs {
info!("checking for loaded gltfs {:?}", asset_server.get_load_state(gltf));
info!(
"checking for loaded gltfs {:?}",
asset_server.get_load_state(gltf)
);
if asset_server.get_load_state(gltf.clone()) == LoadState::Loaded {
debug!("Adding scene to processing list");
debug!("Adding scene to processing list");
loaded_gltfs.push(gltf.clone());
}
}
@ -60,21 +60,17 @@ pub struct GltfLoadingTracker{
for gltf_handle in &loaded_gltfs {
if let Some(gltf) = gltfs.get_mut(gltf_handle) {
// TODO this is a temporary workaround for library management
if let Some(asset_path) = asset_server.get_handle_path(gltf_handle) {
let gltf_name = asset_path.path().file_stem().unwrap().to_str().unwrap();
gltf_extras_to_components(gltf, &mut scenes, &*type_registry, gltf_name);
//gltf_extras_to_prefab_infos(gltf, &mut scenes, &*type_registry, gltf_name);
}
else {
gltf_extras_to_components(gltf, &mut scenes, &*type_registry, "");
}
// TODO this is a temporary workaround for library management
if let Some(asset_path) = asset_server.get_handle_path(gltf_handle) {
let gltf_name = asset_path.path().file_stem().unwrap().to_str().unwrap();
gltf_extras_to_components(gltf, &mut scenes, &*type_registry, gltf_name);
//gltf_extras_to_prefab_infos(gltf, &mut scenes, &*type_registry, gltf_name);
} else {
gltf_extras_to_components(gltf, &mut scenes, &*type_registry, "");
}
}
tracker.loading_gltfs.remove(gltf_handle);
tracker.loaded_gltfs.insert(gltf_handle.clone());
debug!("Done loading scene");
}
}
}

View File

@ -1,4 +1,3 @@
pub fn capitalize_first_letter(s: &str) -> String {
s[0..1].to_uppercase() + &s[1..]
}

View File

@ -2,6 +2,4 @@ use bevy::prelude::*;
use bevy_asset_loader::prelude::*;
#[derive(AssetCollection, Resource)]
pub struct CoreAssets {
}
pub struct CoreAssets {}

View File

@ -7,28 +7,29 @@ pub use assets_game::*;
use bevy::prelude::*;
use bevy_asset_loader::prelude::*;
use crate::state::{AppState};
use crate::state::AppState;
pub struct AssetsPlugin;
impl Plugin for AssetsPlugin {
fn build(&self, app: &mut App) {
app
// load core assets (ie assets needed in the main menu, and everywhere else before loading more assets in game)
.add_loading_state(LoadingState::new(AppState::CoreLoading).continue_to_state(AppState::MenuRunning))
.add_dynamic_collection_to_loading_state::<_, StandardDynamicAssetCollection>(
AppState::CoreLoading,
"advanced/assets_core.assets.ron",
)
.add_collection_to_loading_state::<_, CoreAssets>(AppState::CoreLoading)
// load game assets
.add_loading_state(LoadingState::new(AppState::AppLoading).continue_to_state(AppState::AppRunning))
.add_dynamic_collection_to_loading_state::<_, StandardDynamicAssetCollection>(
AppState::AppLoading,
"advanced/assets_game.assets.ron",
)
.add_collection_to_loading_state::<_, GameAssets>(AppState::AppLoading)
;
fn build(&self, app: &mut App) {
app
// load core assets (ie assets needed in the main menu, and everywhere else before loading more assets in game)
.add_loading_state(
LoadingState::new(AppState::CoreLoading).continue_to_state(AppState::MenuRunning),
)
.add_dynamic_collection_to_loading_state::<_, StandardDynamicAssetCollection>(
AppState::CoreLoading,
"advanced/assets_core.assets.ron",
)
.add_collection_to_loading_state::<_, CoreAssets>(AppState::CoreLoading)
// load game assets
.add_loading_state(
LoadingState::new(AppState::AppLoading).continue_to_state(AppState::AppRunning),
)
.add_dynamic_collection_to_loading_state::<_, StandardDynamicAssetCollection>(
AppState::AppLoading,
"advanced/assets_game.assets.ron",
)
.add_collection_to_loading_state::<_, GameAssets>(AppState::AppLoading);
}
}

View File

@ -1,34 +1,24 @@
use bevy::core_pipeline::bloom::{BloomCompositeMode, BloomSettings};
use bevy::core_pipeline::tonemapping::{DebandDither, Tonemapping};
use bevy::prelude::*;
use bevy::core_pipeline::bloom::{BloomSettings, BloomCompositeMode};
use bevy::core_pipeline::tonemapping::{Tonemapping, DebandDither};
use super::CameraTrackingOffset;
pub fn camera_replace_proxies (
pub fn camera_replace_proxies(
mut commands: Commands,
mut added_cameras: Query<(Entity, &mut Camera), (Added<Camera>, With<CameraTrackingOffset>)>,
) {
for (entity, mut camera) in added_cameras.iter_mut(){
for (entity, mut camera) in added_cameras.iter_mut() {
info!("detected added camera, updating proxy");
camera.hdr = true;
commands.entity(entity)
.insert(
DebandDither::Enabled
)
.insert(
Tonemapping::BlenderFilmic
)
.insert(
BloomSettings{
intensity: 0.01,
composite_mode:BloomCompositeMode::Additive,
..default()
}
)
;
}
commands
.entity(entity)
.insert(DebandDither::Enabled)
.insert(Tonemapping::BlenderFilmic)
.insert(BloomSettings {
intensity: 0.01,
composite_mode: BloomCompositeMode::Additive,
..default()
});
}
}

View File

@ -1,20 +1,20 @@
use bevy::prelude::*;
#[derive(Component, Reflect, Debug)]
#[reflect(Component)]
/// Component for cameras, with an offset from the Trackable target
///
pub struct CameraTracking{
pub offset: Vec3
pub struct CameraTracking {
pub offset: Vec3,
}
impl Default for CameraTracking {
fn default() -> Self {
CameraTracking { offset: Vec3::new(0.0, 6.0, 8.0) }
CameraTracking {
offset: Vec3::new(0.0, 6.0, 8.0),
}
}
}
#[derive(Component, Reflect, Debug, Deref, DerefMut)]
#[reflect(Component)]
/// Component for cameras, with an offset from the Trackable target
@ -26,32 +26,33 @@ impl Default for CameraTrackingOffset {
}
impl CameraTrackingOffset {
fn new (input: Vec3) -> Self {
fn new(input: Vec3) -> Self {
CameraTrackingOffset(input)
}
}
#[derive(Component, Reflect, Default, Debug, )]
#[derive(Component, Reflect, Default, Debug)]
#[reflect(Component)]
/// Add this component to an entity if you want it to be tracked by a Camera
pub struct CameraTrackable;
pub fn camera_track(
mut tracking_cameras: Query<(&mut Transform, &CameraTrackingOffset), (With<Camera>, With<CameraTrackingOffset>, Without<CameraTrackable>)>,
mut tracking_cameras: Query<
(&mut Transform, &CameraTrackingOffset),
(
With<Camera>,
With<CameraTrackingOffset>,
Without<CameraTrackable>,
),
>,
camera_tracked: Query<&Transform, With<CameraTrackable>>,
) {
for (mut camera_transform, tracking_offset) in tracking_cameras.iter_mut() {
for tracked_transform in camera_tracked.iter(){
for tracked_transform in camera_tracked.iter() {
let target_position = tracked_transform.translation + tracking_offset.0;
let eased_position = camera_transform.translation.lerp(target_position, 0.1);
camera_transform.translation = eased_position;// + tracking.offset;// tracked_transform.translation + tracking.offset;
camera_transform.translation = eased_position; // + tracking.offset;// tracked_transform.translation + tracking.offset;
*camera_transform = camera_transform.looking_at(tracked_transform.translation, Vec3::Y);
}
}
}

View File

@ -9,18 +9,16 @@ use bevy_gltf_blueprints::GltfBlueprintsSet;
pub struct CameraPlugin;
impl Plugin for CameraPlugin {
fn build(&self, app: &mut App) {
app
.register_type::<CameraTrackable>()
.register_type::<CameraTracking>()
.register_type::<CameraTrackingOffset>()
.add_systems(Update,
(
camera_replace_proxies.after(GltfBlueprintsSet::AfterSpawn),
camera_track,
)
)
;
}
fn build(&self, app: &mut App) {
app.register_type::<CameraTrackable>()
.register_type::<CameraTracking>()
.register_type::<CameraTrackingOffset>()
.add_systems(
Update,
(
camera_replace_proxies.after(GltfBlueprintsSet::AfterSpawn),
camera_track,
),
);
}
}

View File

@ -1,29 +1,25 @@
use bevy::prelude::*;
use bevy::pbr::{CascadeShadowConfigBuilder, CascadeShadowConfig};
use bevy::pbr::{CascadeShadowConfig, CascadeShadowConfigBuilder};
// fixme might be too specific to might needs, should it be moved out ? also these are all for lights, not models
pub fn lighting_replace_proxies(
mut added_dirights: Query<(Entity, &mut DirectionalLight), Added<DirectionalLight>>,
mut added_spotlights: Query<&mut SpotLight, Added<SpotLight>>,
mut commands: Commands,
){
for (entity, mut light) in added_dirights.iter_mut(){
light.illuminance *= 5.0;
light.shadows_enabled = true;
let shadow_config:CascadeShadowConfig = CascadeShadowConfigBuilder {
first_cascade_far_bound: 15.0,
maximum_distance: 135.0,
..default()
}
.into();
commands.entity(entity)
.insert(shadow_config);
}
for mut light in added_spotlights.iter_mut(){
light.shadows_enabled = true;
}
mut added_dirights: Query<(Entity, &mut DirectionalLight), Added<DirectionalLight>>,
mut added_spotlights: Query<&mut SpotLight, Added<SpotLight>>,
mut commands: Commands,
) {
for (entity, mut light) in added_dirights.iter_mut() {
light.illuminance *= 5.0;
light.shadows_enabled = true;
let shadow_config: CascadeShadowConfig = CascadeShadowConfigBuilder {
first_cascade_far_bound: 15.0,
maximum_distance: 135.0,
..default()
}
.into();
commands.entity(entity).insert(shadow_config);
}
for mut light in added_spotlights.iter_mut() {
light.shadows_enabled = true;
}
}

View File

@ -1,18 +1,18 @@
mod lighting_replace_proxies;
use lighting_replace_proxies::*;
use bevy::pbr::{DirectionalLightShadowMap, NotShadowCaster};
use bevy::prelude::*;
use bevy::pbr::{NotShadowCaster, DirectionalLightShadowMap};
pub struct LightingPlugin;
impl Plugin for LightingPlugin {
fn build(&self, app: &mut App) {
app
fn build(&self, app: &mut App) {
app
.insert_resource(DirectionalLightShadowMap { size: 4096 })
// FIXME: adding these since they are missing
.register_type::<NotShadowCaster>()
.add_systems(PreUpdate, lighting_replace_proxies) // FIXME: you should actually run this in a specific state most likely
;
}
}
}

View File

@ -22,73 +22,63 @@ use bevy_gltf_blueprints::*;
use rand::Rng;
fn spawn_test(
keycode: Res<Input<KeyCode>>,
mut commands: Commands,
mut game_world: Query<(Entity, &Children), With<GameWorldTag>>,
keycode: Res<Input<KeyCode>>,
mut commands: Commands,
mut game_world: Query<(Entity, &Children), With<GameWorldTag>>,
) {
if keycode.just_pressed(KeyCode::T) {
let world = game_world.single_mut();
let world = world.1[0];
if keycode.just_pressed(KeyCode::T) {
let world = game_world.single_mut();
let world = world.1[0];
let mut rng = rand::thread_rng();
let range = 5.5;
let x: f32 = rng.gen_range(-range..range);
let y: f32 = rng.gen_range(-range..range);
let mut rng = rand::thread_rng();
let range = 5.5;
let x: f32 = rng.gen_range(-range..range);
let y: f32 = rng.gen_range(-range..range);
let mut rng = rand::thread_rng();
let range = 0.8;
let vel_x: f32 = rng.gen_range(-range..range);
let vel_y: f32 = rng.gen_range(2.0..2.5);
let vel_z: f32 = rng.gen_range(-range..range);
let mut rng = rand::thread_rng();
let range = 0.8;
let vel_x: f32 = rng.gen_range(-range..range);
let vel_y: f32 = rng.gen_range(2.0..2.5);
let vel_z: f32 = rng.gen_range(-range..range);
let name_index: u64 = rng.gen();
let name_index:u64 = rng.gen();
let new_entity = commands.spawn((
BluePrintBundle{
blueprint: BlueprintName("Health_Pickup".to_string()),
transform: TransformBundle::from_transform(Transform::from_xyz(x, 2.0, y)),
..Default::default()
},
bevy::prelude::Name::from(format!("test{}", name_index)),
// BlueprintName("Health_Pickup".to_string()),
// SpawnHere,
// TransformBundle::from_transform(Transform::from_xyz(x, 2.0, y)),
Velocity {
linvel: Vec3::new(vel_x, vel_y, vel_z),
angvel: Vec3::new(0.0, 0.0, 0.0),
},
)).id();
commands.entity(world).add_child(new_entity);
}
let new_entity = commands
.spawn((
BluePrintBundle {
blueprint: BlueprintName("Health_Pickup".to_string()),
transform: TransformBundle::from_transform(Transform::from_xyz(x, 2.0, y)),
..Default::default()
},
bevy::prelude::Name::from(format!("test{}", name_index)),
// BlueprintName("Health_Pickup".to_string()),
// SpawnHere,
// TransformBundle::from_transform(Transform::from_xyz(x, 2.0, y)),
Velocity {
linvel: Vec3::new(vel_x, vel_y, vel_z),
angvel: Vec3::new(0.0, 0.0, 0.0),
},
))
.id();
commands.entity(world).add_child(new_entity);
}
}
pub struct CorePlugin;
impl Plugin for CorePlugin {
fn build(&self, app: &mut App) {
app
.add_plugins((
fn build(&self, app: &mut App) {
app.add_plugins((
LightingPlugin,
CameraPlugin,
PhysicsPlugin,
SaveLoadPlugin,
BlueprintsPlugin{
library_folder: "advanced/models/library".into()
}
BlueprintsPlugin {
library_folder: "advanced/models/library".into(),
},
))
// just for testing
.add_systems(
Update,
spawn_test
)
;
}
// just for testing
.add_systems(Update, spawn_test);
}
}

View File

@ -1,12 +1,12 @@
use bevy::prelude::{ResMut, info};
use bevy::prelude::{info, ResMut};
use bevy_rapier3d::prelude::RapierConfiguration;
pub fn pause_physics(mut physics_config: ResMut<RapierConfiguration>){
pub fn pause_physics(mut physics_config: ResMut<RapierConfiguration>) {
info!("pausing physics");
physics_config.physics_pipeline_active = false;
}
pub fn resume_physics(mut physics_config: ResMut<RapierConfiguration>){
pub fn resume_physics(mut physics_config: ResMut<RapierConfiguration>) {
info!("unpausing physics");
physics_config.physics_pipeline_active = true;
}

View File

@ -6,33 +6,24 @@ pub mod utils;
pub mod controls;
pub use controls::*;
use bevy::prelude::*;
use crate::state::GameState;
use bevy::prelude::*;
// use super::blueprints::GltfBlueprintsSet;
use bevy_gltf_blueprints::GltfBlueprintsSet;
// use crate::Collider;
pub struct PhysicsPlugin;
impl Plugin for PhysicsPlugin {
fn build(&self, app: &mut App) {
app
.register_type::<AutoAABBCollider>()
.register_type::<physics_replace_proxies::Collider>()
// find a way to make serde's stuff serializable
// .register_type::<bevy_rapier3d::dynamics::CoefficientCombineRule>()
//bevy_rapier3d::dynamics::CoefficientCombineRule
.add_systems(Update, physics_replace_proxies.after(GltfBlueprintsSet::AfterSpawn))
.add_systems(
OnEnter(GameState::InGame),
resume_physics
)
.add_systems(
OnExit(GameState::InGame),
pause_physics
)
;
}
fn build(&self, app: &mut App) {
app.register_type::<AutoAABBCollider>()
.register_type::<physics_replace_proxies::Collider>()
// find a way to make serde's stuff serializable
// .register_type::<bevy_rapier3d::dynamics::CoefficientCombineRule>()
//bevy_rapier3d::dynamics::CoefficientCombineRule
.add_systems(
Update,
physics_replace_proxies.after(GltfBlueprintsSet::AfterSpawn),
)
.add_systems(OnEnter(GameState::InGame), resume_physics)
.add_systems(OnExit(GameState::InGame), pause_physics);
}
}

View File

@ -1,11 +1,11 @@
use bevy::prelude::*;
// use bevy::render::primitives::Aabb;
use bevy_rapier3d::geometry::Collider as RapierCollider;
use bevy_rapier3d::prelude::{ComputedColliderShape, ActiveEvents, ActiveCollisionTypes};
use bevy_rapier3d::prelude::{ActiveCollisionTypes, ActiveEvents, ComputedColliderShape};
use super::utils::*;
#[derive(Component, Reflect, Default, Debug, )]
#[derive(Component, Reflect, Default, Debug)]
#[reflect(Component)]
pub enum Collider {
Ball(f32),
@ -15,21 +15,23 @@ pub enum Collider {
Mesh,
}
#[derive(Component, Reflect, Default, Debug, )]
#[derive(Component, Reflect, Default, Debug)]
#[reflect(Component)]
pub enum AutoAABBCollider {
#[default]
Cuboid,
Ball,
Capsule
Capsule,
}
// replaces all physics stand-ins with the actual rapier types
pub fn physics_replace_proxies (
pub fn physics_replace_proxies(
meshes: Res<Assets<Mesh>>,
mesh_handles: Query<&Handle<Mesh>>,
mut proxy_colliders: Query<(Entity, &Collider, &Name, &mut Visibility), (Without<RapierCollider>, Added<Collider>)>,
mut proxy_colliders: Query<
(Entity, &Collider, &Name, &mut Visibility),
(Without<RapierCollider>, Added<Collider>),
>,
// needed for tri meshes
children: Query<&Children>,
@ -38,12 +40,12 @@ pub fn physics_replace_proxies (
for proxy_colider in proxy_colliders.iter_mut() {
let (entity, collider_proxy, name, mut visibility) = proxy_colider;
// we hide the collider meshes: perhaps they should be removed altogether once processed ?
if name.ends_with( "_collider" ) || name.ends_with( "_sensor" ) {
if name.ends_with("_collider") || name.ends_with("_sensor") {
*visibility = Visibility::Hidden;
}
let mut rapier_collider:RapierCollider;
match collider_proxy{
let mut rapier_collider: RapierCollider;
match collider_proxy {
Collider::Ball(radius) => {
info!("generating collider from proxy: ball");
rapier_collider = RapierCollider::ball(*radius);
@ -70,15 +72,25 @@ pub fn physics_replace_proxies (
}
Collider::Mesh => {
info!("generating collider from proxy: mesh");
for (_, collider_mesh) in Mesh::search_in_children(entity, &children, &meshes, &mesh_handles)
for (_, collider_mesh) in
Mesh::search_in_children(entity, &children, &meshes, &mesh_handles)
{
rapier_collider = RapierCollider::from_bevy_mesh(collider_mesh, &ComputedColliderShape::TriMesh).unwrap();
commands.entity(entity)
rapier_collider = RapierCollider::from_bevy_mesh(
collider_mesh,
&ComputedColliderShape::TriMesh,
)
.unwrap();
commands
.entity(entity)
.insert(rapier_collider)
// FIXME: this is just for demo purposes !!!
.insert(ActiveCollisionTypes::default() | ActiveCollisionTypes::KINEMATIC_STATIC | ActiveCollisionTypes::STATIC_STATIC | ActiveCollisionTypes::DYNAMIC_STATIC)
.insert(ActiveEvents::COLLISION_EVENTS)
;
// FIXME: this is just for demo purposes !!!
.insert(
ActiveCollisionTypes::default()
| ActiveCollisionTypes::KINEMATIC_STATIC
| ActiveCollisionTypes::STATIC_STATIC
| ActiveCollisionTypes::DYNAMIC_STATIC,
)
.insert(ActiveEvents::COLLISION_EVENTS);
// .insert(ActiveEvents::COLLISION_EVENTS)
// break;
// RapierCollider::convex_hull(points)

View File

@ -5,7 +5,7 @@ use bevy::prelude::*;
pub struct EcsRelationshipsPlugin;
impl Plugin for EcsRelationshipsPlugin {
fn build(&self, app: &mut App) {
app;
}
fn build(&self, app: &mut App) {
app;
}
}

View File

@ -1,16 +1,15 @@
use bevy::prelude::*;
pub fn insert_dependant_component<Dependant: Component, Dependency: Component+ std::default::Default>(
pub fn insert_dependant_component<
Dependant: Component,
Dependency: Component + std::default::Default,
>(
mut commands: Commands,
entities_without_depency: Query<(Entity, &Name), (With<Dependant>, Without<Dependency>)>,
) {
for (entity, name) in entities_without_depency.iter() {
let name = name.clone().to_string();
commands.entity(entity)
.insert(
Dependency::default()
)
;
commands.entity(entity).insert(Dependency::default());
warn!("found an entity called {} with a {} component but without an {}, please check your assets", name.clone(), std::any::type_name::<Dependant>(), std::any::type_name::<Dependency>());
}
}

View File

@ -1,19 +1,19 @@
use bevy::prelude::*;
use bevy_gltf_blueprints::{clone_entity::CloneEntity, SpawnHere, GameWorldTag};
use bevy_gltf_blueprints::{clone_entity::CloneEntity, GameWorldTag, SpawnHere};
use crate::{
assets::GameAssets,
state::{InAppRunning, AppState, GameState}
state::{AppState, GameState, InAppRunning},
};
use super::Saveable;
const SCENE_FILE_PATH: &str = "scenes/save.scn.ron";
#[derive(Component, Debug, )]
#[derive(Component, Debug)]
pub struct TempLoadedSceneMarker;
#[derive(Component, Debug, )]
#[derive(Component, Debug)]
pub struct SaveablesToRemove(Vec<(Entity, Name)>);
#[derive(Component, Event)]
@ -21,18 +21,14 @@ pub struct LoadRequest {
pub path: String,
}
pub fn should_load(
save_requested_events: EventReader<LoadRequest>,
) -> bool {
return save_requested_events.len() > 0
pub fn should_load(save_requested_events: EventReader<LoadRequest>) -> bool {
return save_requested_events.len() > 0;
}
pub fn load_prepare(
mut next_app_state: ResMut<NextState<AppState>>,
mut next_game_state: ResMut<NextState<GameState>>,
){
) {
next_app_state.set(AppState::LoadingGame);
next_game_state.set(GameState::None);
info!("--loading: prepare")
@ -42,7 +38,7 @@ pub fn load_prepare(
pub fn _unload_world_old(world: &mut World) {
let entities: Vec<Entity> = world
// .query_filtered::<Entity, Or<(With<Save>, With<Unload>)>>()
.query_filtered::<Entity, With<GameWorldTag>>()// our level/world contains this component
.query_filtered::<Entity, With<GameWorldTag>>() // our level/world contains this component
.iter(world)
.collect();
for entity in entities {
@ -53,11 +49,8 @@ pub fn _unload_world_old(world: &mut World) {
}
}
pub fn unload_world(
mut commands: Commands,
gameworlds: Query<Entity, With<GameWorldTag>>
){
for e in gameworlds.iter(){
pub fn unload_world(mut commands: Commands, gameworlds: Query<Entity, With<GameWorldTag>>) {
for e in gameworlds.iter() {
info!("--loading: despawn old world/level");
commands.entity(e).despawn_recursive();
}
@ -66,9 +59,9 @@ pub fn unload_world(
// almost identical to setup_game, !!??
pub fn load_world(
mut commands: Commands,
game_assets: Res<GameAssets>,
// scenes: ResMut<Scene>,
){
game_assets: Res<GameAssets>,
// scenes: ResMut<Scene>,
) {
info!("--loading: loading world/level");
commands.spawn((
@ -78,25 +71,18 @@ pub fn load_world(
},
bevy::prelude::Name::from("world"),
GameWorldTag,
InAppRunning
InAppRunning,
));
}
pub fn load_saved_scene(
mut commands: Commands,
asset_server: Res<AssetServer>
) {
commands.spawn(
(
pub fn load_saved_scene(mut commands: Commands, asset_server: Res<AssetServer>) {
commands.spawn((
DynamicSceneBundle {
// Scenes are loaded just like any other asset.
scene: asset_server.load(SCENE_FILE_PATH),
..default()
},
TempLoadedSceneMarker
TempLoadedSceneMarker,
));
// commands.entity(world).add_child(child_scene);
info!("--loading: loaded saved scene");
@ -109,21 +95,21 @@ pub fn process_loaded_scene(
mut game_world: Query<(Entity, &Children), With<GameWorldTag>>,
saveables: Query<(Entity, &Name), With<Saveable>>,
asset_server: Res<AssetServer>
){
for (loaded_scene, children) in loaded_scene.iter(){
asset_server: Res<AssetServer>,
) {
for (loaded_scene, children) in loaded_scene.iter() {
info!("--loading: post processing loaded scene");
let mut entities_to_load:Vec<(Entity, Name)> = vec![];
let mut entities_to_load: Vec<(Entity, Name)> = vec![];
for loaded_entity in children.iter() {
if let Ok((source, name, _)) = named_entities.get(*loaded_entity) {
if let Ok((source, name, _)) = named_entities.get(*loaded_entity) {
entities_to_load.push((source, name.clone()));
let mut found = false;
for (e, n, p) in named_entities.iter(){
for (e, n, p) in named_entities.iter() {
// if we have an entity with the same name as in same file, overwrite
if e != source && name.as_str() == n.as_str(){
if e != source && name.as_str() == n.as_str() {
// println!("found entity with same name {} {} {:?} {:?}", name, n, source, e);
// source is entity within the newly loaded scene (source), e is within the existing world (destination)
info!("copying data from {:?} to {:?}", source, e);
@ -145,10 +131,9 @@ pub fn process_loaded_scene(
let world = game_world.single_mut();
let world = world.1[0];
let new_entity = commands.spawn((
bevy::prelude::Name::from(name.clone()),
SpawnHere,
)).id();
let new_entity = commands
.spawn((bevy::prelude::Name::from(name.clone()), SpawnHere))
.id();
commands.add(CloneEntity {
source: source,
@ -157,17 +142,11 @@ pub fn process_loaded_scene(
commands.entity(world).add_child(new_entity);
info!("copying data from {:?} to {:?}", source, new_entity);
}
}
}
}
commands.spawn(SaveablesToRemove(entities_to_load.clone()));
// if an entity is present in the world but NOT in the saved entities , it should be removed from the world
// ideally this should be run between spawning of the world/level AND spawn_placeholders
@ -177,7 +156,6 @@ pub fn process_loaded_scene(
asset_server.mark_unused_assets();
asset_server.free_unused_assets();
}
//for saveable in saveables.iter(){
// println!("SAVEABLE BEFORE {:?}", saveable)
@ -190,17 +168,15 @@ pub fn final_cleanup(
saveables: Query<(Entity, &Name), With<Saveable>>,
mut next_app_state: ResMut<NextState<AppState>>,
mut next_game_state: ResMut<NextState<GameState>>,
){
if let Ok((e, entities_to_load)) = saveables_to_remove.get_single()
{
) {
if let Ok((e, entities_to_load)) = saveables_to_remove.get_single() {
info!("saveables to remove {:?}", entities_to_load);
for (e, n) in saveables.iter(){
for (e, n) in saveables.iter() {
let mut found = false;
println!("SAVEABLE {}", n);
//let entities_to_load = saveables_to_remove.single();
for (en, na) in entities_to_load.0.iter(){
for (en, na) in entities_to_load.0.iter() {
found = na.as_str() == n.as_str();
if found {
break;
@ -226,18 +202,17 @@ fn process_loaded_scene_load_alt(
entities: Query<(Entity, &Children), With<TempLoadedSceneMarker>>,
named_entities: Query<(Entity, &Name, &Parent)>, // FIXME: very inneficient
mut commands: Commands,
){
for (entity, children) in entities.iter(){
let mut entities_to_load:Vec<(Entity, Name)> = vec![];
) {
for (entity, children) in entities.iter() {
let mut entities_to_load: Vec<(Entity, Name)> = vec![];
for saved_source in children.iter() {
if let Ok((source, name, _)) = named_entities.get(*saved_source) {
println!("AAAAAAA {}", name);
entities_to_load.push((source, name.clone()));
}
if let Ok((source, name, _)) = named_entities.get(*saved_source) {
println!("AAAAAAA {}", name);
entities_to_load.push((source, name.clone()));
}
}
println!("entities to load {:?}", entities_to_load);
commands.entity(entity).despawn_recursive();
commands.entity(entity).despawn_recursive();
}
}

View File

@ -10,23 +10,21 @@ pub mod loading;
pub use loading::*;
use bevy::prelude::*;
use bevy::prelude::{App, Plugin, IntoSystemConfigs};
use bevy::prelude::{App, IntoSystemConfigs, Plugin};
use bevy::utils::Uuid;
use bevy_gltf_blueprints::GltfBlueprintsSet;
#[derive(SystemSet, Debug, Hash, PartialEq, Eq, Clone)]
pub enum LoadingSet{
Load,
PostLoad,
pub enum LoadingSet {
Load,
PostLoad,
}
pub struct SaveLoadPlugin;
impl Plugin for SaveLoadPlugin {
fn build(&self, app: &mut App) {
app
fn build(&self, app: &mut App) {
app
.register_type::<Uuid>()
.register_type::<Saveable>()
.add_event::<SaveRequest>()
@ -54,7 +52,7 @@ impl Plugin for SaveLoadPlugin {
.run_if(should_load) // .run_if(in_state(AppState::AppRunning))
.in_set(LoadingSet::Load)
)
.add_systems(Update,
.add_systems(Update,
(
process_loaded_scene,
apply_deferred,
@ -67,7 +65,6 @@ impl Plugin for SaveLoadPlugin {
)
// .add_systems(Update, bla)
;
}
}
}

View File

@ -1,17 +1,14 @@
use bevy::prelude::*;
use bevy::utils::{Uuid};
use bevy::utils::Uuid;
#[derive(Component, Reflect, Debug, )]
#[derive(Component, Reflect, Debug)]
#[reflect(Component)]
pub struct Saveable{
id: Uuid
pub struct Saveable {
id: Uuid,
}
impl Default for Saveable{
impl Default for Saveable {
fn default() -> Self {
Saveable{
id: Uuid::new_v4()
}
Saveable { id: Uuid::new_v4() }
}
}

View File

@ -1,30 +1,27 @@
use bevy::pbr::{Clusters, VisiblePointLights};
use bevy::render::camera::CameraRenderGraph;
use bevy::render::view::VisibleEntities;
use bevy::{prelude::*, gltf::GltfExtras};
use bevy::tasks::IoTaskPool;
use bevy::{gltf::GltfExtras, prelude::*};
use bevy_rapier3d::prelude::RigidBody;
use std::io::Write;
use std::fs::File;
use std::io::Write;
use crate::core::physics::Collider;
use crate::game::{Pickable, Player};
use super::Saveable;
const NEW_SCENE_FILE_PATH:&str="save.scn.ron";
const NEW_SCENE_FILE_PATH: &str = "save.scn.ron";
#[derive(Component, Event)]
pub struct SaveRequest {
pub path: String,
}
pub fn should_save(
// keycode: Res<Input<KeyCode>>,
save_requested_events: EventReader<SaveRequest>,
) -> bool {
return save_requested_events.len() > 0;
@ -34,7 +31,7 @@ pub fn should_save(
pub fn save_game(
world: &mut World,
// save_requested_events: EventReader<SaveRequest>,
){
) {
info!("saving");
// world.
/*for bli in save_requested_events.iter(){
@ -42,9 +39,9 @@ pub fn save_game(
}*/
let saveable_entities: Vec<Entity> = world
.query_filtered::<Entity, With<Saveable>>()
.iter(world)
.collect();
.query_filtered::<Entity, With<Saveable>>()
.iter(world)
.collect();
/*let static_entities: Vec<Entity> = world
.query_filtered::<Entity, Without<Saveable>>()
@ -60,11 +57,9 @@ pub fn save_game(
.deny::<Visibility>()
.deny::<GltfExtras>()
.deny::<GlobalTransform>()
.deny::<Collider>()
.deny::<RigidBody>()
.deny::<Saveable>()
// camera stuff
.deny::<Camera>()
.deny::<CameraRenderGraph>()
@ -73,22 +68,20 @@ pub fn save_game(
.deny::<VisibleEntities>()
.deny::<VisiblePointLights>()
//.deny::<HasGizmoMarker>()
.extract_entities(saveable_entities.into_iter());
let dyn_scene = scene_builder.build();
let serialized_scene = dyn_scene
.serialize_ron(world.resource::<AppTypeRegistry>())
.unwrap();
let dyn_scene = scene_builder.build();
let serialized_scene = dyn_scene.serialize_ron(world.resource::<AppTypeRegistry>()).unwrap();
#[cfg(not(target_arch = "wasm32"))]
IoTaskPool::get()
.spawn(async move {
// Write the scene RON data to file
File::create(format!("assets/scenes/{NEW_SCENE_FILE_PATH}"))
.and_then(|mut file| file.write(serialized_scene.as_bytes()))
.expect("Error while writing scene to file");
})
.detach();
#[cfg(not(target_arch = "wasm32"))]
IoTaskPool::get()
.spawn(async move {
// Write the scene RON data to file
File::create(format!("assets/scenes/{NEW_SCENE_FILE_PATH}"))
.and_then(|mut file| file.write(serialized_scene.as_bytes()))
.expect("Error while writing scene to file");
})
.detach();
}

View File

@ -1,6 +1,9 @@
use bevy::prelude::*;
use crate::{assets::GameAssets, state::{InAppRunning, GameState}};
use crate::{
assets::GameAssets,
state::{GameState, InAppRunning},
};
use bevy_gltf_blueprints::GameWorldTag;
pub fn setup_game(
@ -8,7 +11,6 @@ pub fn setup_game(
game_assets: Res<GameAssets>,
mut next_game_state: ResMut<NextState<GameState>>,
) {
println!("setting up all stuff");
commands.insert_resource(AmbientLight {
color: Color::WHITE,
@ -23,7 +25,7 @@ pub fn setup_game(
},
bevy::prelude::Name::from("world"),
GameWorldTag,
InAppRunning
InAppRunning,
));
next_game_state.set(GameState::InGame)

View File

@ -1,8 +1,11 @@
use bevy::prelude::*;
use crate::{state::{AppState, GameState, InMainMenu}, core::save_load::{LoadRequest, SaveRequest}};
use crate::{
core::save_load::{LoadRequest, SaveRequest},
state::{AppState, GameState, InMainMenu},
};
pub fn setup_main_menu(mut commands: Commands){
pub fn setup_main_menu(mut commands: Commands) {
commands.spawn((Camera2dBundle::default(), InMainMenu));
commands.spawn((
@ -21,10 +24,9 @@ pub fn setup_main_menu(mut commands: Commands){
left: Val::Px(200.0),
..default()
}),
InMainMenu
InMainMenu,
));
commands.spawn((
TextBundle::from_section(
"New Game (press Enter to start, press T once the game is started for demo spawning)",
@ -41,7 +43,7 @@ pub fn setup_main_menu(mut commands: Commands){
left: Val::Px(200.0),
..default()
}),
InMainMenu
InMainMenu,
));
/*
@ -84,8 +86,8 @@ pub fn setup_main_menu(mut commands: Commands){
));*/
}
pub fn teardown_main_menu(bla: Query<Entity, With<InMainMenu>>, mut commands: Commands){
for bli in bla.iter(){
pub fn teardown_main_menu(bla: Query<Entity, With<InMainMenu>>, mut commands: Commands) {
for bli in bla.iter() {
commands.entity(bli).despawn_recursive();
}
}
@ -95,11 +97,9 @@ pub fn main_menu(
mut next_app_state: ResMut<NextState<AppState>>,
// mut next_game_state: ResMut<NextState<GameState>>,
mut save_requested_events: EventWriter<SaveRequest>,
mut load_requested_events: EventWriter<LoadRequest>,
){
) {
if keycode.just_pressed(KeyCode::Return) {
next_app_state.set(AppState::AppLoading);
// next_game_state.set(GameState::None);

View File

@ -7,52 +7,46 @@ pub use in_main_menu::*;
pub mod picking;
pub use picking::*;
use crate::{
insert_dependant_component,
state::{AppState, GameState},
};
use bevy::prelude::*;
use bevy_rapier3d::prelude::*;
use crate::{insert_dependant_component, state::{AppState, GameState}};
// this file is just for demo purposes, contains various types of components, systems etc
#[derive(Component, Reflect, Default, Debug, )]
#[derive(Component, Reflect, Default, Debug)]
#[reflect(Component)]
pub enum SoundMaterial{
Metal,
Wood,
Rock,
Cloth,
Squishy,
#[default]
None
pub enum SoundMaterial {
Metal,
Wood,
Rock,
Cloth,
Squishy,
#[default]
None,
}
#[derive(Component, Reflect, Default, Debug, )]
#[derive(Component, Reflect, Default, Debug)]
#[reflect(Component)]
/// Demo marker component
pub struct Player;
#[derive(Component, Reflect, Default, Debug, )]
#[derive(Component, Reflect, Default, Debug)]
#[reflect(Component)]
/// Demo component showing auto injection of components
pub struct ShouldBeWithPlayer;
#[derive(Component, Reflect, Default, Debug, )]
#[derive(Component, Reflect, Default, Debug)]
#[reflect(Component)]
/// Demo marker component
pub struct Interactible;
fn player_move_demo(
keycode: Res<Input<KeyCode>>,
mut players: Query<&mut Transform, With<Player>>,
){
) {
let speed = 0.2;
if let Ok(mut player) = players.get_single_mut() {
if keycode.pressed(KeyCode::Left) {
@ -75,17 +69,15 @@ fn player_move_demo(
pub fn test_collision_events(
mut collision_events: EventReader<CollisionEvent>,
mut contact_force_events: EventReader<ContactForceEvent>,
)
{
) {
for collision_event in collision_events.iter() {
println!("collision");
match collision_event {
CollisionEvent::Started(_entity1, _entity2 ,_) => {
CollisionEvent::Started(_entity1, _entity2, _) => {
println!("collision started")
}
CollisionEvent::Stopped(_entity1, _entity2 ,_) => {
CollisionEvent::Stopped(_entity1, _entity2, _) => {
println!("collision ended")
}
}
}
@ -95,42 +87,28 @@ pub fn test_collision_events(
}
}
pub struct GamePlugin;
impl Plugin for GamePlugin {
fn build(&self, app: &mut App) {
app
.add_plugins(PickingPlugin)
.register_type::<Interactible>()
.register_type::<SoundMaterial>()
.register_type::<Player>()
// little helper utility, to automatically inject components that are dependant on an other component
// ie, here an Entity with a Player component should also always have a ShouldBeWithPlayer component
// you get a warning if you use this, as I consider this to be stop-gap solution (usually you should have either a bundle, or directly define all needed components)
.add_systems(Update, (
// insert_dependant_component::<Player, ShouldBeWithPlayer>,
player_move_demo, //.run_if(in_state(AppState::Running)),
// test_collision_events
)
.run_if(in_state(GameState::InGame)))
.add_systems(
OnEnter(AppState::MenuRunning),
setup_main_menu
)
.add_systems(
OnExit(AppState::MenuRunning),
teardown_main_menu
)
.add_systems(Update, (main_menu))
.add_systems(
OnEnter(AppState::AppRunning),
setup_game
)
;
}
fn build(&self, app: &mut App) {
app.add_plugins(PickingPlugin)
.register_type::<Interactible>()
.register_type::<SoundMaterial>()
.register_type::<Player>()
// little helper utility, to automatically inject components that are dependant on an other component
// ie, here an Entity with a Player component should also always have a ShouldBeWithPlayer component
// you get a warning if you use this, as I consider this to be stop-gap solution (usually you should have either a bundle, or directly define all needed components)
.add_systems(
Update,
(
// insert_dependant_component::<Player, ShouldBeWithPlayer>,
player_move_demo, //.run_if(in_state(AppState::Running)),
// test_collision_events
)
.run_if(in_state(GameState::InGame)),
)
.add_systems(OnEnter(AppState::MenuRunning), setup_main_menu)
.add_systems(OnExit(AppState::MenuRunning), teardown_main_menu)
.add_systems(Update, (main_menu))
.add_systems(OnEnter(AppState::AppRunning), setup_game);
}
}

View File

@ -1,8 +1,7 @@
use bevy::prelude::*;
use super::Player;
use bevy::prelude::*;
#[derive(Component, Reflect, Default, Debug, )]
#[derive(Component, Reflect, Default, Debug)]
#[reflect(Component)]
pub struct Pickable;
@ -11,11 +10,13 @@ pub struct Pickable;
pub fn picking(
players: Query<&GlobalTransform, With<Player>>,
pickables: Query<(Entity, &GlobalTransform), With<Pickable>>,
mut commands: Commands
){
for player_transforms in players.iter(){
for (pickable, pickable_transforms) in pickables.iter(){
let distance = player_transforms.translation().distance(pickable_transforms.translation());
mut commands: Commands,
) {
for player_transforms in players.iter() {
for (pickable, pickable_transforms) in pickables.iter() {
let distance = player_transforms
.translation()
.distance(pickable_transforms.translation());
if distance < 2.5 {
commands.entity(pickable).despawn_recursive();
}
@ -25,16 +26,12 @@ pub fn picking(
pub struct PickingPlugin;
impl Plugin for PickingPlugin {
fn build(&self, app: &mut App) {
app
.register_type::<Pickable>()
.add_systems(Update, (
picking, //.run_if(in_state(AppState::Running)),
))
;
}
fn build(&self, app: &mut App) {
app.register_type::<Pickable>().add_systems(
Update,
(
picking, //.run_if(in_state(AppState::Running)),
),
);
}
}

View File

@ -1,8 +1,8 @@
use std::time::Duration;
use bevy::{prelude::*, asset::ChangeWatcher, gltf::Gltf};
use bevy::{asset::ChangeWatcher, gltf::Gltf, prelude::*};
use bevy_editor_pls::prelude::*;
use bevy_rapier3d::prelude::*;
use bevy_gltf_components::ComponentsFromGltfPlugin;
use bevy_rapier3d::prelude::*;
use std::time::Duration;
mod core;
use crate::core::*;
@ -19,33 +19,28 @@ use game::*;
mod test_components;
use test_components::*;
fn main(){
fn main() {
App::new()
.add_plugins((
DefaultPlugins.set(
AssetPlugin {
.add_plugins((
DefaultPlugins.set(AssetPlugin {
// This tells the AssetServer to watch for changes to assets.
// It enables our scenes to automatically reload in game when we modify their files.
// practical in our case to be able to edit the shaders without needing to recompile
// watch_for_changes: ChangeWatcher::with_delay(Duration::from_millis(50)), : FIXME: breaks scene save/loading
..default()
}
),
// editor
EditorPlugin::default(),
// physics
RapierPhysicsPlugin::<NoUserData>::default(),
RapierDebugRenderPlugin::default(),
// our custom plugins
ComponentsFromGltfPlugin,
StatePlugin,
AssetsPlugin,
CorePlugin, // reusable plugins
GamePlugin, // specific to our game
ComponentsTestPlugin // Showcases different type of components /structs
))
.run();
}),
// editor
EditorPlugin::default(),
// physics
RapierPhysicsPlugin::<NoUserData>::default(),
RapierDebugRenderPlugin::default(),
// our custom plugins
ComponentsFromGltfPlugin,
StatePlugin,
AssetsPlugin,
CorePlugin, // reusable plugins
GamePlugin, // specific to our game
ComponentsTestPlugin, // Showcases different type of components /structs
))
.run();
}

View File

@ -1,8 +1,8 @@
use bevy::prelude::*;
use bevy::app::AppExit;
use bevy::prelude::*;
#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash, Default, States)]
pub enum AppState{
pub enum AppState {
#[default]
CoreLoading,
MenuRunning,
@ -11,7 +11,7 @@ pub enum AppState{
AppEnding,
// FIXME: not sure
LoadingGame
LoadingGame,
}
#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash, Default, States)]
@ -25,7 +25,7 @@ pub enum GameState {
InGameOver,
InSaving,
InLoading
InLoading,
}
// tag components for all entities within a certain state (for despawning them if needed) , FIXME: seems kinda hack-ish
@ -46,13 +46,9 @@ pub struct InMenu;
#[derive(Component, Default)]
pub struct InGame;
pub struct StatePlugin;
impl Plugin for StatePlugin {
fn build(&self, app: &mut App) {
app
.add_state::<AppState>()
.add_state::<GameState>()
;
}
fn build(&self, app: &mut App) {
app.add_state::<AppState>().add_state::<GameState>();
}
}

View File

@ -1,7 +1,6 @@
use bevy::prelude::*;
#[derive(Component, Reflect, Default, Debug, )]
#[derive(Component, Reflect, Default, Debug)]
#[reflect(Component)]
struct UnitTest;
@ -17,70 +16,65 @@ struct TuppleTestU64(u64);
#[reflect(Component)]
pub struct TuppleTestStr(String);
#[derive(Component, Reflect, Default, Debug, )]
#[derive(Component, Reflect, Default, Debug)]
#[reflect(Component)]
struct TuppleTest2(f32, u64, String);
#[derive(Component, Reflect, Default, Debug, )]
#[derive(Component, Reflect, Default, Debug)]
#[reflect(Component)]
struct TuppleTestBool(bool);
#[derive(Component, Reflect, Default, Debug, )]
#[derive(Component, Reflect, Default, Debug)]
#[reflect(Component)]
struct TuppleVec2(Vec2);
#[derive(Component, Reflect, Default, Debug, )]
#[derive(Component, Reflect, Default, Debug)]
#[reflect(Component)]
struct TuppleVec3(Vec3);
#[derive(Component, Reflect, Default, Debug, )]
#[derive(Component, Reflect, Default, Debug)]
#[reflect(Component)]
struct TuppleVec(Vec<String>);
#[derive(Component, Reflect, Default, Debug, )]
#[derive(Component, Reflect, Default, Debug)]
#[reflect(Component)]
struct TuppleTestColor(Color);
#[derive(Component, Reflect, Default, Debug, )]
#[derive(Component, Reflect, Default, Debug)]
#[reflect(Component)]
struct BasicTest{
struct BasicTest {
a: f32,
b: u64,
c: String
c: String,
}
#[derive(Component, Reflect, Default, Debug, )]
#[derive(Component, Reflect, Default, Debug)]
#[reflect(Component)]
pub enum EnumTest{
Metal,
Wood,
Rock,
Cloth,
Squishy,
#[default]
None
pub enum EnumTest {
Metal,
Wood,
Rock,
Cloth,
Squishy,
#[default]
None,
}
pub struct ComponentsTestPlugin;
impl Plugin for ComponentsTestPlugin {
fn build(&self, app: &mut App) {
app
.register_type::<BasicTest>()
.register_type::<UnitTest>()
.register_type::<TuppleTestF32>()
.register_type::<TuppleTestU64>()
.register_type::<TuppleTestStr>()
.register_type::<TuppleTestBool>()
.register_type::<TuppleTest2>()
.register_type::<TuppleVec2>()
.register_type::<TuppleVec3>()
.register_type::<EnumTest>()
.register_type::<TuppleTestColor>()
.register_type::<TuppleVec>()
.register_type::<Vec<String>>()
;
}
fn build(&self, app: &mut App) {
app.register_type::<BasicTest>()
.register_type::<UnitTest>()
.register_type::<TuppleTestF32>()
.register_type::<TuppleTestU64>()
.register_type::<TuppleTestStr>()
.register_type::<TuppleTestBool>()
.register_type::<TuppleTest2>()
.register_type::<TuppleVec2>()
.register_type::<TuppleVec3>()
.register_type::<EnumTest>()
.register_type::<TuppleTestColor>()
.register_type::<TuppleVec>()
.register_type::<Vec<String>>();
}
}

View File

@ -1,34 +1,24 @@
use bevy::core_pipeline::bloom::{BloomCompositeMode, BloomSettings};
use bevy::core_pipeline::tonemapping::{DebandDither, Tonemapping};
use bevy::prelude::*;
use bevy::core_pipeline::bloom::{BloomSettings, BloomCompositeMode};
use bevy::core_pipeline::tonemapping::{Tonemapping, DebandDither};
use super::CameraTrackingOffset;
pub fn camera_replace_proxies (
pub fn camera_replace_proxies(
mut commands: Commands,
mut added_cameras: Query<(Entity, &mut Camera), (Added<Camera>, With<CameraTrackingOffset>)>,
) {
for (entity, mut camera) in added_cameras.iter_mut(){
for (entity, mut camera) in added_cameras.iter_mut() {
info!("detected added camera, updating proxy");
camera.hdr = true;
commands.entity(entity)
.insert(
DebandDither::Enabled
)
.insert(
Tonemapping::BlenderFilmic
)
.insert(
BloomSettings{
intensity: 0.01,
composite_mode:BloomCompositeMode::Additive,
..default()
}
)
;
}
commands
.entity(entity)
.insert(DebandDither::Enabled)
.insert(Tonemapping::BlenderFilmic)
.insert(BloomSettings {
intensity: 0.01,
composite_mode: BloomCompositeMode::Additive,
..default()
});
}
}

View File

@ -1,20 +1,20 @@
use bevy::prelude::*;
#[derive(Component, Reflect, Debug)]
#[reflect(Component)]
/// Component for cameras, with an offset from the Trackable target
///
pub struct CameraTracking{
pub offset: Vec3
pub struct CameraTracking {
pub offset: Vec3,
}
impl Default for CameraTracking {
fn default() -> Self {
CameraTracking { offset: Vec3::new(0.0, 6.0, 8.0) }
CameraTracking {
offset: Vec3::new(0.0, 6.0, 8.0),
}
}
}
#[derive(Component, Reflect, Debug, Deref, DerefMut)]
#[reflect(Component)]
/// Component for cameras, with an offset from the Trackable target
@ -26,32 +26,33 @@ impl Default for CameraTrackingOffset {
}
impl CameraTrackingOffset {
fn new (input: Vec3) -> Self {
fn new(input: Vec3) -> Self {
CameraTrackingOffset(input)
}
}
#[derive(Component, Reflect, Default, Debug, )]
#[derive(Component, Reflect, Default, Debug)]
#[reflect(Component)]
/// Add this component to an entity if you want it to be tracked by a Camera
pub struct CameraTrackable;
pub fn camera_track(
mut tracking_cameras: Query<(&mut Transform, &CameraTrackingOffset), (With<Camera>, With<CameraTrackingOffset>, Without<CameraTrackable>)>,
mut tracking_cameras: Query<
(&mut Transform, &CameraTrackingOffset),
(
With<Camera>,
With<CameraTrackingOffset>,
Without<CameraTrackable>,
),
>,
camera_tracked: Query<&Transform, With<CameraTrackable>>,
) {
for (mut camera_transform, tracking_offset) in tracking_cameras.iter_mut() {
for tracked_transform in camera_tracked.iter(){
for tracked_transform in camera_tracked.iter() {
let target_position = tracked_transform.translation + tracking_offset.0;
let eased_position = camera_transform.translation.lerp(target_position, 0.1);
camera_transform.translation = eased_position;// + tracking.offset;// tracked_transform.translation + tracking.offset;
camera_transform.translation = eased_position; // + tracking.offset;// tracked_transform.translation + tracking.offset;
*camera_transform = camera_transform.looking_at(tracked_transform.translation, Vec3::Y);
}
}
}

View File

@ -8,18 +8,10 @@ use bevy::prelude::*;
pub struct CameraPlugin;
impl Plugin for CameraPlugin {
fn build(&self, app: &mut App) {
app
.register_type::<CameraTrackable>()
.register_type::<CameraTracking>()
.register_type::<CameraTrackingOffset>()
.add_systems(Update,
(
camera_replace_proxies,
camera_track,
)
)
;
}
fn build(&self, app: &mut App) {
app.register_type::<CameraTrackable>()
.register_type::<CameraTracking>()
.register_type::<CameraTrackingOffset>()
.add_systems(Update, (camera_replace_proxies, camera_track));
}
}

View File

@ -1,29 +1,25 @@
use bevy::prelude::*;
use bevy::pbr::{CascadeShadowConfigBuilder, CascadeShadowConfig};
use bevy::pbr::{CascadeShadowConfig, CascadeShadowConfigBuilder};
// fixme might be too specific to might needs, should it be moved out ? also these are all for lights, not models
pub fn lighting_replace_proxies(
mut added_dirights: Query<(Entity, &mut DirectionalLight), Added<DirectionalLight>>,
mut added_spotlights: Query<&mut SpotLight, Added<SpotLight>>,
mut commands: Commands,
){
for (entity, mut light) in added_dirights.iter_mut(){
light.illuminance *= 5.0;
light.shadows_enabled = true;
let shadow_config:CascadeShadowConfig = CascadeShadowConfigBuilder {
first_cascade_far_bound: 15.0,
maximum_distance: 135.0,
..default()
}
.into();
commands.entity(entity)
.insert(shadow_config);
}
for mut light in added_spotlights.iter_mut(){
light.shadows_enabled = true;
}
mut added_dirights: Query<(Entity, &mut DirectionalLight), Added<DirectionalLight>>,
mut added_spotlights: Query<&mut SpotLight, Added<SpotLight>>,
mut commands: Commands,
) {
for (entity, mut light) in added_dirights.iter_mut() {
light.illuminance *= 5.0;
light.shadows_enabled = true;
let shadow_config: CascadeShadowConfig = CascadeShadowConfigBuilder {
first_cascade_far_bound: 15.0,
maximum_distance: 135.0,
..default()
}
.into();
commands.entity(entity).insert(shadow_config);
}
for mut light in added_spotlights.iter_mut() {
light.shadows_enabled = true;
}
}

View File

@ -1,18 +1,18 @@
mod lighting_replace_proxies;
use lighting_replace_proxies::*;
use bevy::pbr::{DirectionalLightShadowMap, NotShadowCaster};
use bevy::prelude::*;
use bevy::pbr::{NotShadowCaster, DirectionalLightShadowMap};
pub struct LightingPlugin;
impl Plugin for LightingPlugin {
fn build(&self, app: &mut App) {
app
fn build(&self, app: &mut App) {
app
.insert_resource(DirectionalLightShadowMap { size: 4096 })
// FIXME: adding these since they are missing
.register_type::<NotShadowCaster>()
.add_systems(PreUpdate, lighting_replace_proxies) // FIXME: you should actually run this in a specific state most likely
;
}
}
}

View File

@ -13,12 +13,7 @@ pub use physics::*;
use bevy::prelude::*;
pub struct CorePlugin;
impl Plugin for CorePlugin {
fn build(&self, app: &mut App) {
app
.add_plugins((
LightingPlugin,
CameraPlugin,
PhysicsPlugin
));
}
fn build(&self, app: &mut App) {
app.add_plugins((LightingPlugin, CameraPlugin, PhysicsPlugin));
}
}

View File

@ -1,10 +1,10 @@
use bevy::prelude::ResMut;
use bevy_rapier3d::prelude::RapierConfiguration;
pub fn pause_physics(mut physics_config: ResMut<RapierConfiguration>){
pub fn pause_physics(mut physics_config: ResMut<RapierConfiguration>) {
physics_config.physics_pipeline_active = false;
}
pub fn resume_physics(mut physics_config: ResMut<RapierConfiguration>){
pub fn resume_physics(mut physics_config: ResMut<RapierConfiguration>) {
physics_config.physics_pipeline_active = true;
}

View File

@ -10,8 +10,8 @@ use bevy::prelude::*;
// use crate::Collider;
pub struct PhysicsPlugin;
impl Plugin for PhysicsPlugin {
fn build(&self, app: &mut App) {
app
fn build(&self, app: &mut App) {
app
.register_type::<AutoAABBCollider>()
.register_type::<physics_replace_proxies::Collider>()
@ -23,6 +23,5 @@ impl Plugin for PhysicsPlugin {
//.add_system(pause_physics.in_schedule(OnEnter(GameState::InMenu)))
//.add_system(resume_physics.in_schedule(OnEnter(GameState::InGame)))
;
}
}
}

View File

@ -1,11 +1,11 @@
use bevy::prelude::*;
// use bevy::render::primitives::Aabb;
use bevy_rapier3d::geometry::Collider as RapierCollider;
use bevy_rapier3d::prelude::{ComputedColliderShape, ActiveEvents, ActiveCollisionTypes};
use bevy_rapier3d::prelude::{ActiveCollisionTypes, ActiveEvents, ComputedColliderShape};
use super::utils::*;
#[derive(Component, Reflect, Default, Debug, )]
#[derive(Component, Reflect, Default, Debug)]
#[reflect(Component)]
pub enum Collider {
Ball(f32),
@ -15,20 +15,23 @@ pub enum Collider {
Mesh,
}
#[derive(Component, Reflect, Default, Debug, )]
#[derive(Component, Reflect, Default, Debug)]
#[reflect(Component)]
pub enum AutoAABBCollider {
#[default]
Cuboid,
Ball,
Capsule
Capsule,
}
// replaces all physics stand-ins with the actual rapier types
pub fn physics_replace_proxies (
pub fn physics_replace_proxies(
meshes: Res<Assets<Mesh>>,
mesh_handles: Query<&Handle<Mesh>>,
mut proxy_colliders: Query<(Entity, &Collider, &Name, &mut Visibility), (Without<RapierCollider>, Added<Collider>)>,
mut proxy_colliders: Query<
(Entity, &Collider, &Name, &mut Visibility),
(Without<RapierCollider>, Added<Collider>),
>,
// needed for tri meshes
children: Query<&Children>,
@ -37,12 +40,12 @@ pub fn physics_replace_proxies (
for proxy_colider in proxy_colliders.iter_mut() {
let (entity, collider_proxy, name, mut visibility) = proxy_colider;
// we hide the collider meshes: perhaps they should be removed altogether once processed ?
if name.ends_with( "_collider" ) || name.ends_with( "_sensor" ) {
if name.ends_with("_collider") || name.ends_with("_sensor") {
*visibility = Visibility::Hidden;
}
let mut rapier_collider:RapierCollider;
match collider_proxy{
let mut rapier_collider: RapierCollider;
match collider_proxy {
Collider::Ball(radius) => {
println!("proxy: ball");
rapier_collider = RapierCollider::ball(*radius);
@ -69,15 +72,25 @@ pub fn physics_replace_proxies (
}
Collider::Mesh => {
println!("proxy: mesh");
for (_, collider_mesh) in Mesh::search_in_children(entity, &children, &meshes, &mesh_handles)
for (_, collider_mesh) in
Mesh::search_in_children(entity, &children, &meshes, &mesh_handles)
{
rapier_collider = RapierCollider::from_bevy_mesh(collider_mesh, &ComputedColliderShape::TriMesh).unwrap();
commands.entity(entity)
rapier_collider = RapierCollider::from_bevy_mesh(
collider_mesh,
&ComputedColliderShape::TriMesh,
)
.unwrap();
commands
.entity(entity)
.insert(rapier_collider)
// FIXME: this is just for demo purposes !!!
.insert(ActiveCollisionTypes::default() | ActiveCollisionTypes::KINEMATIC_STATIC | ActiveCollisionTypes::STATIC_STATIC | ActiveCollisionTypes::DYNAMIC_STATIC)
.insert(ActiveEvents::COLLISION_EVENTS)
;
// FIXME: this is just for demo purposes !!!
.insert(
ActiveCollisionTypes::default()
| ActiveCollisionTypes::KINEMATIC_STATIC
| ActiveCollisionTypes::STATIC_STATIC
| ActiveCollisionTypes::DYNAMIC_STATIC,
)
.insert(ActiveEvents::COLLISION_EVENTS);
// .insert(ActiveEvents::COLLISION_EVENTS)
// break;
// RapierCollider::convex_hull(points)

View File

@ -5,7 +5,7 @@ use bevy::prelude::*;
pub struct EcsRelationshipsPlugin;
impl Plugin for EcsRelationshipsPlugin {
fn build(&self, app: &mut App) {
app;
}
fn build(&self, app: &mut App) {
app;
}
}

View File

@ -1,16 +1,15 @@
use bevy::prelude::*;
pub fn insert_dependant_component<Dependant: Component, Dependency: Component+ std::default::Default>(
pub fn insert_dependant_component<
Dependant: Component,
Dependency: Component + std::default::Default,
>(
mut commands: Commands,
entities_without_depency: Query<(Entity, &Name), (With<Dependant>, Without<Dependency>)>,
) {
for (entity, name) in entities_without_depency.iter() {
let name = name.clone().to_string();
commands.entity(entity)
.insert(
Dependency::default()
)
;
commands.entity(entity).insert(Dependency::default());
warn!("found an entity called {} with a {} component but without an {}, please check your assets", name.clone(), std::any::type_name::<Dependant>(), std::any::type_name::<Dependency>());
}
}

View File

@ -1,50 +1,45 @@
use crate::insert_dependant_component;
use bevy::prelude::*;
use bevy_rapier3d::prelude::*;
use crate::insert_dependant_component;
// this file is just for demo purposes, contains various types of components, systems etc
#[derive(Component, Reflect, Default, Debug, )]
#[derive(Component, Reflect, Default, Debug)]
#[reflect(Component)]
pub enum SoundMaterial{
Metal,
Wood,
Rock,
Cloth,
Squishy,
#[default]
None
pub enum SoundMaterial {
Metal,
Wood,
Rock,
Cloth,
Squishy,
#[default]
None,
}
#[derive(Component, Reflect, Default, Debug, )]
#[derive(Component, Reflect, Default, Debug)]
#[reflect(Component)]
/// Demo marker component
pub struct Player;
#[derive(Component, Reflect, Default, Debug, )]
#[derive(Component, Reflect, Default, Debug)]
#[reflect(Component)]
/// Demo component showing auto injection of components
pub struct ShouldBeWithPlayer;
#[derive(Component, Reflect, Default, Debug, )]
#[derive(Component, Reflect, Default, Debug)]
#[reflect(Component)]
/// Demo marker component
pub struct Interactible;
#[derive(Component, Reflect, Default, Debug, )]
#[derive(Component, Reflect, Default, Debug)]
#[reflect(Component)]
/// Demo marker component
pub struct Pickable;
fn player_move_demo(
keycode: Res<Input<KeyCode>>,
mut players: Query<&mut Transform, With<Player>>,
){
) {
let speed = 0.2;
if let Ok(mut player) = players.get_single_mut() {
if keycode.pressed(KeyCode::Left) {
@ -67,17 +62,15 @@ fn player_move_demo(
pub fn test_collision_events(
mut collision_events: EventReader<CollisionEvent>,
mut contact_force_events: EventReader<ContactForceEvent>,
)
{
) {
for collision_event in collision_events.iter() {
println!("collision");
match collision_event {
CollisionEvent::Started(_entity1, _entity2 ,_) => {
CollisionEvent::Started(_entity1, _entity2, _) => {
println!("collision started")
}
CollisionEvent::Stopped(_entity1, _entity2 ,_) => {
CollisionEvent::Stopped(_entity1, _entity2, _) => {
println!("collision ended")
}
}
}
@ -87,23 +80,23 @@ pub fn test_collision_events(
}
}
pub struct DemoPlugin;
impl Plugin for DemoPlugin {
fn build(&self, app: &mut App) {
app
.register_type::<Interactible>()
.register_type::<Pickable>()
.register_type::<SoundMaterial>()
.register_type::<Player>()
// little helper utility, to automatically inject components that are dependant on an other component
// ie, here an Entity with a Player component should also always have a ShouldBeWithPlayer component
// you get a warning if you use this, as I consider this to be stop-gap solution (usually you should have either a bundle, or directly define all needed components)
.add_systems(Update, (
insert_dependant_component::<Player, ShouldBeWithPlayer>,
player_move_demo, //.run_if(in_state(AppState::Running)),
test_collision_events
))
;
}
fn build(&self, app: &mut App) {
app.register_type::<Interactible>()
.register_type::<Pickable>()
.register_type::<SoundMaterial>()
.register_type::<Player>()
// little helper utility, to automatically inject components that are dependant on an other component
// ie, here an Entity with a Player component should also always have a ShouldBeWithPlayer component
// you get a warning if you use this, as I consider this to be stop-gap solution (usually you should have either a bundle, or directly define all needed components)
.add_systems(
Update,
(
insert_dependant_component::<Player, ShouldBeWithPlayer>,
player_move_demo, //.run_if(in_state(AppState::Running)),
test_collision_events,
),
);
}
}

View File

@ -1,8 +1,8 @@
use std::time::Duration;
use bevy::{prelude::*, asset::ChangeWatcher, gltf::Gltf};
use bevy::{asset::ChangeWatcher, gltf::Gltf, prelude::*};
use bevy_editor_pls::prelude::*;
use bevy_rapier3d::prelude::*;
use bevy_gltf_components::ComponentsFromGltfPlugin;
use bevy_rapier3d::prelude::*;
use std::time::Duration;
mod core;
use crate::core::*;
@ -13,12 +13,11 @@ use game::*;
mod test_components;
use test_components::*;
#[derive(Component, Reflect, Default, Debug, )]
#[derive(Component, Reflect, Default, Debug)]
#[reflect(Component)]
/// helper marker component
pub struct LoadedMarker;
#[derive(Debug, Clone, Copy, Default, Eq, PartialEq, Hash, States)]
enum AppState {
#[default]
@ -26,54 +25,41 @@ enum AppState {
Running,
}
fn main(){
fn main() {
App::new()
.add_plugins((
DefaultPlugins.set(
AssetPlugin {
.add_plugins((
DefaultPlugins.set(AssetPlugin {
// This tells the AssetServer to watch for changes to assets.
// It enables our scenes to automatically reload in game when we modify their files.
// practical in our case to be able to edit the shaders without needing to recompile
watch_for_changes: ChangeWatcher::with_delay(Duration::from_millis(50)),
..default()
}
),
// editor
EditorPlugin::default(),
// physics
RapierPhysicsPlugin::<NoUserData>::default(),
RapierDebugRenderPlugin::default(),
// our custom plugins
ComponentsFromGltfPlugin,
CorePlugin, // reusable plugins
DemoPlugin, // specific to our game
ComponentsTestPlugin // Showcases different type of components /structs
))
.add_state::<AppState>()
.add_systems(Startup, setup)
.add_systems(Update, (
spawn_level.run_if(in_state(AppState::Loading)),
))
.run();
}),
// editor
EditorPlugin::default(),
// physics
RapierPhysicsPlugin::<NoUserData>::default(),
RapierDebugRenderPlugin::default(),
// our custom plugins
ComponentsFromGltfPlugin,
CorePlugin, // reusable plugins
DemoPlugin, // specific to our game
ComponentsTestPlugin, // Showcases different type of components /structs
))
.add_state::<AppState>()
.add_systems(Startup, setup)
.add_systems(Update, (spawn_level.run_if(in_state(AppState::Loading)),))
.run();
}
#[derive(Resource)]
struct AssetLoadHelper(Handle<Scene>);
// we preload the data here, but this is for DEMO PURPOSES ONLY !! Please use https://github.com/NiklasEi/bevy_asset_loader or a similar logic to seperate loading / pre processing
// of assets from the spawning
// AssetLoadHelper is also just for the same purpose, you do not need it in a real scenario
// the states here are also for demo purposes only,
fn setup(
mut commands: Commands,
asset_server: Res<AssetServer>,
) {
let tmp: Handle<Scene> = asset_server.load("basic/models/level1.glb#Scene0");
fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
let tmp: Handle<Scene> = asset_server.load("basic/models/level1.glb#Scene0");
commands.insert_resource(AssetLoadHelper(tmp));
}
@ -84,28 +70,25 @@ fn spawn_level(
mut asset_event_reader: EventReader<AssetEvent<Gltf>>,
mut next_state: ResMut<NextState<AppState>>,
){
) {
if let Some(asset_event) = asset_event_reader.iter().next() {
match asset_event {
AssetEvent::Created { handle: _ } => {
info!("GLTF loaded");
if scene_markers.is_empty() {
info!("spawning scene");
commands.spawn(
(
SceneBundle {
scene: preloaded_scene.0.clone(),
..default()
},
LoadedMarker,
Name::new("Level1")
)
);
commands.spawn((
SceneBundle {
scene: preloaded_scene.0.clone(),
..default()
},
LoadedMarker,
Name::new("Level1"),
));
next_state.set(AppState::Running);
}
}
_ => ()
_ => (),
}
}
}

View File

@ -1,7 +1,6 @@
use bevy::prelude::*;
#[derive(Component, Reflect, Default, Debug, )]
#[derive(Component, Reflect, Default, Debug)]
#[reflect(Component)]
struct UnitTest;
@ -17,70 +16,65 @@ struct TuppleTestU64(u64);
#[reflect(Component)]
pub struct TuppleTestStr(String);
#[derive(Component, Reflect, Default, Debug, )]
#[derive(Component, Reflect, Default, Debug)]
#[reflect(Component)]
struct TuppleTest2(f32, u64, String);
#[derive(Component, Reflect, Default, Debug, )]
#[derive(Component, Reflect, Default, Debug)]
#[reflect(Component)]
struct TuppleTestBool(bool);
#[derive(Component, Reflect, Default, Debug, )]
#[derive(Component, Reflect, Default, Debug)]
#[reflect(Component)]
struct TuppleVec2(Vec2);
#[derive(Component, Reflect, Default, Debug, )]
#[derive(Component, Reflect, Default, Debug)]
#[reflect(Component)]
struct TuppleVec3(Vec3);
#[derive(Component, Reflect, Default, Debug, )]
#[derive(Component, Reflect, Default, Debug)]
#[reflect(Component)]
struct TuppleVec(Vec<String>);
#[derive(Component, Reflect, Default, Debug, )]
#[derive(Component, Reflect, Default, Debug)]
#[reflect(Component)]
struct TuppleTestColor(Color);
#[derive(Component, Reflect, Default, Debug, )]
#[derive(Component, Reflect, Default, Debug)]
#[reflect(Component)]
struct BasicTest{
struct BasicTest {
a: f32,
b: u64,
c: String
c: String,
}
#[derive(Component, Reflect, Default, Debug, )]
#[derive(Component, Reflect, Default, Debug)]
#[reflect(Component)]
pub enum EnumTest{
Metal,
Wood,
Rock,
Cloth,
Squishy,
#[default]
None
pub enum EnumTest {
Metal,
Wood,
Rock,
Cloth,
Squishy,
#[default]
None,
}
pub struct ComponentsTestPlugin;
impl Plugin for ComponentsTestPlugin {
fn build(&self, app: &mut App) {
app
.register_type::<BasicTest>()
.register_type::<UnitTest>()
.register_type::<TuppleTestF32>()
.register_type::<TuppleTestU64>()
.register_type::<TuppleTestStr>()
.register_type::<TuppleTestBool>()
.register_type::<TuppleTest2>()
.register_type::<TuppleVec2>()
.register_type::<TuppleVec3>()
.register_type::<EnumTest>()
.register_type::<TuppleTestColor>()
.register_type::<TuppleVec>()
.register_type::<Vec<String>>()
;
}
fn build(&self, app: &mut App) {
app.register_type::<BasicTest>()
.register_type::<UnitTest>()
.register_type::<TuppleTestF32>()
.register_type::<TuppleTestU64>()
.register_type::<TuppleTestStr>()
.register_type::<TuppleTestBool>()
.register_type::<TuppleTest2>()
.register_type::<TuppleVec2>()
.register_type::<TuppleVec3>()
.register_type::<EnumTest>()
.register_type::<TuppleTestColor>()
.register_type::<TuppleVec>()
.register_type::<Vec<String>>();
}
}