chore(): cargo fmt (#21)
This commit is contained in:
parent
a1c32ae1d6
commit
f41a315563
|
@ -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)
|
||||
}
|
||||
}
|
|
@ -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),
|
||||
);
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
pub fn capitalize_first_letter(s: &str) -> String {
|
||||
s[0..1].to_uppercase() + &s[1..]
|
||||
}
|
||||
|
|
@ -2,6 +2,4 @@ use bevy::prelude::*;
|
|||
use bevy_asset_loader::prelude::*;
|
||||
|
||||
#[derive(AssetCollection, Resource)]
|
||||
pub struct CoreAssets {
|
||||
|
||||
}
|
||||
pub struct CoreAssets {}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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()
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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>());
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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)
|
||||
|
||||
;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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() }
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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();
|
||||
}
|
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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>();
|
||||
}
|
||||
}
|
|
@ -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>>();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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()
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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)))
|
||||
;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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>());
|
||||
}
|
||||
}
|
|
@ -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,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
_ => ()
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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>>();
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue