Fix Changed<T> by rewriting ecs `Filter`s, position `TileMapPos` entities with ChildOf relations

This commit is contained in:
SeanOMik 2024-11-30 12:33:23 -05:00 committed by SeanOMik
parent 418765d595
commit 5a0e06f94d
20 changed files with 279 additions and 201 deletions

View File

@ -56,12 +56,15 @@ impl ComponentColumn {
pub unsafe fn new(info: ComponentInfo, capacity: usize) -> Self {
let data = ComponentColumn::alloc(info.layout(), capacity);
let mut ticks = Vec::with_capacity(capacity);
ticks.resize(capacity, Tick::from(0));
Self {
data: RefCell::new(data),
capacity,
info,
len: 0,
entity_ticks: Vec::new(),
entity_ticks: ticks,
}
}
@ -99,9 +102,7 @@ impl ComponentColumn {
let dest = NonNull::new_unchecked(data.as_ptr().add(entity_index * size));
ptr::copy_nonoverlapping(comp_src.as_ptr(), dest.as_ptr(), size);
// check if a component spot is being set twice and that the entity's tick is
// already stored
self.entity_ticks.push(tick);
self.entity_ticks[entity_index] = tick;
self.len += 1;
}
@ -175,6 +176,7 @@ impl ComponentColumn {
*data = new_ptr;
}
self.entity_ticks.resize(new_capacity, Tick::from(0));
self.capacity = new_capacity;
}

View File

@ -272,11 +272,11 @@ mod tests {
let mut world = World::new();
let e = world.spawn(b);
let pos = world.view_one::<&Vec2>(e).get()
let pos = world.view_one::<&Vec2>(e)
.expect("failed to find spawned Vec2 from Bundle on Entity");
assert!(pos.x == b_pos.x && pos.y == b_pos.y, "Spawned Vec2 values were not correct, got: {:?}, expected: {:?}", *pos, b_pos);
let flag = world.view_one::<&SomeFlag>(e).get()
let flag = world.view_one::<&SomeFlag>(e)
.expect("failed to find spawned SomeFlag from Bundle on Entity");
assert_eq!(*flag, b_flag);
}

View File

@ -1,6 +1,6 @@
use std::{any::Any, cell::RefMut, mem::{self, MaybeUninit}, ptr::{self, NonNull}};
use crate::{system::FnArgFetcher, Access, Bundle, Entities, Entity, World};
use crate::{system::FnArgFetcher, Access, Bundle, Entities, Entity, Relation, World};
/// A Command be used to delay mutation of the world until after this system is ran.
pub trait Command: Any {
@ -175,6 +175,12 @@ impl<'a, 'b> Commands<'a, 'b> {
});
}
pub fn add_relation<R: Relation>(&mut self, origin: Entity, relation: R, target: Entity){
self.add(move | world: &mut World| {
world.add_relation(origin, relation, target);
});
}
/// Execute all commands in the queue, in order of insertion
pub fn execute(&mut self, world: &mut World) {
self.queue.execute(Some(world));

View File

@ -1,6 +1,6 @@
use std::marker::PhantomData;
use crate::{query::{AsFilter, AsQuery, Fetch, Filter, Query}, Component, ComponentColumn, DynTypeId, Tick, World};
use crate::{query::{AsQuery, Fetch, Query}, Component, ComponentColumn, DynTypeId, Tick, World};
pub struct ChangedFetcher<'a, T> {
col: &'a ComponentColumn,
@ -20,7 +20,10 @@ where
unsafe fn get_item(&mut self, entity: crate::world::ArchetypeEntityId) -> Self::Item {
let tick = self.col.entity_ticks[entity.0 as usize];
*tick >= (*self.tick) - 1
if *tick > 50 {
//debug!("tick: {}, world tick: {}", *tick, *self.tick);
}
*tick >= *self.tick
}
}
@ -77,10 +80,10 @@ where
archetype.has_column(self.type_id)
}
unsafe fn fetch<'a>(&self, w: &'a World, a: &'a crate::archetype::Archetype, _: crate::Tick) -> Self::Fetch<'a> {
unsafe fn fetch<'a>(&self, _: &'a World, a: &'a crate::archetype::Archetype, tick: crate::Tick) -> Self::Fetch<'a> {
ChangedFetcher {
col: a.get_column(self.type_id).unwrap(),
tick: w.current_tick(),
tick,
_phantom: PhantomData::<&T>,
}
}
@ -89,9 +92,3 @@ where
impl<T: Component> AsQuery for Changed<T> {
type Query = Self;
}
impl<T: Component> Filter for Changed<T> { }
impl<T: Component> AsFilter for Changed<T> {
type Filter = Self;
}

View File

@ -1,6 +1,6 @@
use std::marker::PhantomData;
use crate::{query::{AsFilter, AsQuery, Filter, Query}, Archetype, Component, DynTypeId, World};
use crate::{query::{AsQuery, Query}, Archetype, Component, DynTypeId, World};
use super::StaticFetcher;
@ -45,8 +45,8 @@ impl<C: Component> AsQuery for Has<C> {
type Query = Self;
}
impl<C: Component> Filter for Has<C> { }
//impl<C: Component> Filter for Has<C> { }
impl<C: Component> AsFilter for Has<C> {
/* impl<C: Component> AsFilter for Has<C> {
type Filter = Self;
}
} */

View File

@ -10,7 +10,73 @@ pub use not::*;
mod changed;
pub use changed::*;
use super::Fetch;
use crate::{Archetype, ArchetypeEntityId, Tick, World};
use super::{Fetch, Query};
pub trait FilterFetch<'a> {
/// Returns true if the entity should be visited or skipped.
fn can_visit(&mut self, entity: ArchetypeEntityId) -> bool;
}
pub trait Filter: Copy {
/// The fetcher used for the filter
type Fetch<'a>: FilterFetch<'a>;
fn new() -> Self;
/// Returns true if the archetype should be visited or skipped.
fn can_visit_archetype(&self, archetype: &Archetype) -> bool;
unsafe fn fetch<'a>(&self, world: &'a World, archetype: &'a Archetype, tick: Tick) -> Self::Fetch<'a>;
/// Returns a fetcher that doesn't fetch from an archetype.
unsafe fn fetch_world<'a>(&self, world: &'a World) -> Option<Self::Fetch<'a>> {
let _ = world;
None
}
}
/// A trait for getting the filter of a type.
pub trait AsFilter {
/// The query for this type
type Filter: Filter;
}
impl<Q> Filter for Q
where
Q: for <'a> Query<Item<'a> = bool>,
{
type Fetch<'a> = Q::Fetch<'a>;
fn new() -> Self {
Query::new()
}
fn can_visit_archetype(&self, archetype: &Archetype) -> bool {
Query::can_visit_archetype(self, archetype)
}
unsafe fn fetch<'a>(&self, world: &'a World, archetype: &'a Archetype, tick: Tick) -> Self::Fetch<'a> {
Query::fetch(self, world, archetype, tick)
}
}
impl<'a, F> FilterFetch<'a> for F
where
F: Fetch<'a, Item = bool>
{
fn can_visit(&mut self, entity: ArchetypeEntityId) -> bool {
Fetch::can_visit_item(self, entity) && unsafe { Fetch::get_item(self, entity) }
}
}
impl<Q> AsFilter for Q
where
Q: for <'a> Query<Item<'a> = bool>
{
type Filter = Q;
}
/// A fetcher that just returns a provided value
pub struct StaticFetcher<T: Clone> {

View File

@ -1,4 +1,4 @@
use crate::{query::{AsFilter, AsQuery, Filter, Query}, Archetype, World};
use crate::{query::{AsQuery, Query}, Archetype, World};
use super::StaticFetcher;
@ -44,9 +44,3 @@ impl<Q: Query> Query for Not<Q> {
impl<Q: Query> AsQuery for Not<Q> {
type Query = Self;
}
impl<Q: Query> Filter for Not<Q> { }
impl<Q: Query> AsFilter for Not<Q> {
type Filter = Self;
}

View File

@ -38,6 +38,7 @@ pub use world_tick::*;
pub mod dynamic;
pub mod filter;
pub use filter::{Filter, AsFilter};
/// A [`Fetch`]er implementation gets data out of an archetype.
pub trait Fetch<'a> {
@ -93,30 +94,24 @@ pub trait AsQuery {
type Query: Query;
}
/// A trait for getting the filter of a type.
pub trait AsFilter {
/// The query for this type
type Filter: Filter;
}
pub trait IntoQuery {
fn into_query(self) -> Self;
}
impl<'a> Fetch<'a> for () {
type Item = ();
type Item = bool;
fn dangling() -> Self {
unreachable!()
}
unsafe fn get_item(&mut self, _: ArchetypeEntityId) -> Self::Item {
()
true
}
}
impl Query for () {
type Item<'a> = ();
type Item<'a> = bool;
type Fetch<'a> = ();
@ -135,22 +130,10 @@ impl Query for () {
}
}
impl Filter for () {
type Item<'a> = bool;
}
impl AsQuery for () {
type Query = ();
}
pub trait Filter: Query {
type Item<'a> = bool;
}
impl AsFilter for () {
type Filter = ();
}
#[cfg(test)]
mod tests {
use crate::{World, archetype::Archetype, tests::Vec2};

View File

@ -1,6 +1,6 @@
use crate::World;
use super::{Query, Fetch, AsQuery, Filter, AsFilter};
use super::{Query, Fetch, AsQuery, Filter, AsFilter, filter::FilterFetch, ArchetypeEntityId};
/* impl<'a, F1> Fetch<'a> for (F1,)
where
@ -139,10 +139,7 @@ macro_rules! impl_bundle_tuple {
fn can_visit_archetype(&self, archetype: &crate::archetype::Archetype) -> bool {
let ( $($name,)+ ) = self;
// this is the only way I could figure out how to do an 'and'
let bools = vec![$($name.can_visit_archetype(archetype),)+];
bools.iter().all(|b| *b)
$($name.can_visit_archetype(archetype)) &&+
}
unsafe fn fetch<'a>(&self, world: &'a World, archetype: &'a crate::archetype::Archetype, tick: crate::Tick) -> Self::Fetch<'a> {
@ -156,26 +153,30 @@ macro_rules! impl_bundle_tuple {
}
#[allow(non_snake_case)]
impl<$($name: Filter),+> Filter for ($($name,)+) {
//type Item<'a> = ($(<$name as Query>::Item<'a>,)+);
//type Fetch<'a> = ($(<$name as Query>::Fetch<'a>,)+);
impl<'a, $($name: FilterFetch<'a>),+> FilterFetch<'a> for ($($name,)+) {
fn can_visit(&mut self, entity: ArchetypeEntityId) -> bool {
let ( $($name,)+ ) = self;
$($name.can_visit(entity)) &&+
}
}
/* fn new() -> Self {
#[allow(non_snake_case)]
impl<$($name: Filter),+> Filter for ($($name,)+) {
type Fetch<'a> = ($(<$name as Filter>::Fetch<'a>,)+);
fn new() -> Self {
( $($name::new(),)+ )
}
fn can_visit_archetype(&self, archetype: &crate::archetype::Archetype) -> bool {
let ( $($name,)+ ) = self;
// this is the only way I could figure out how to do an 'and'
let bools = vec![$($name.can_visit_archetype(archetype),)+];
bools.iter().all(|b| *b)
$($name.can_visit_archetype(archetype)) &&+
}
unsafe fn fetch<'a>(&self, world: &'a World, archetype: &'a crate::archetype::Archetype, tick: crate::Tick) -> Self::Fetch<'a> {
let ( $($name,)+ ) = self;
( $($name.fetch(world, archetype, tick),)+ )
} */
}
}
impl<$($name: AsFilter),+> AsFilter for ($($name,)+) {

View File

@ -1,8 +1,8 @@
use std::ops::Range;
use std::{ops::Range, ptr::NonNull};
use crate::{archetype::Archetype, world::{ArchetypeEntityId, World}, EntityId, Tick};
use crate::{archetype::Archetype, system::FnArgFetcher, world::{ArchetypeEntityId, World}, Entity, Tick};
use super::{AsFilter, AsQuery, Fetch, Filter, Query};
use super::{filter::{AsFilter, Filter, FilterFetch}, AsQuery, Fetch, Query};
pub type View<'a, Q, F = ()> = ViewState<'a, <Q as AsQuery>::Query, <F as AsQuery>::Query>;
@ -109,7 +109,7 @@ where
let filter_fetcher = self.filter_fetcher.as_mut().unwrap();
let entity_index = ArchetypeEntityId(entity_index);
if fetcher.can_visit_item(entity_index) && filter_fetcher.can_visit_item(entity_index) {
if fetcher.can_visit_item(entity_index) && filter_fetcher.can_visit(entity_index) {
let i = unsafe { fetcher.get_item(entity_index) };
return Some(i);
}
@ -138,27 +138,27 @@ where
}
}
pub struct ViewOne<'a, Q: Query> {
pub type ViewOne<'a, Q> = ViewOneState<'a, <Q as AsQuery>::Query>;
pub struct ViewOneState<'a, Q: Query> {
world: &'a World,
tick: Tick,
entity: EntityId,
query: Q,
}
impl<'a, Q: Query> ViewOne<'a, Q> {
pub fn new(world: &'a World, entity: EntityId, query: Q) -> Self {
impl<'a, Q: Query> ViewOneState<'a, Q> {
pub fn new(world: &'a World, query: Q) -> Self {
//let tick = world.tick_tracker().tick_when(Q::MUTATES);
Self {
world,
tick: world.current_tick(),
entity,
query
}
}
pub fn get(self) -> Option<Q::Item<'a>> {
if let Some(record) = self.world.entities.arch_index.get(&self.entity) {
pub fn get(&self, entity: Entity) -> Option<Q::Item<'a>> {
if let Some(record) = self.world.entities.arch_index.get(&entity.id()) {
let arch = self.world.archetypes.get(&record.id)
.expect("An invalid record was specified for an entity");
@ -173,3 +173,17 @@ impl<'a, Q: Query> ViewOne<'a, Q> {
None
}
}
impl<Q: Query> FnArgFetcher for ViewOneState<'_, Q> {
type State = ();
type Arg<'a, 'state> = ViewOneState<'a, Q>;
unsafe fn get<'a, 'state>(_: &'state mut Self::State, world: NonNull<World>) -> Self::Arg<'a, 'state> {
let world = world.as_ref();
ViewOneState::<Q>::new(world, Q::new())
}
fn apply_deferred(_: Self::State, _: NonNull<World>) { }
fn create_state(_: NonNull<World>) -> Self::State { () }
}

View File

@ -2,7 +2,7 @@ use std::{any::TypeId, collections::HashMap, ops::Deref, ptr::NonNull};
use atomic_refcell::{AtomicRef, AtomicRefMut};
use crate::{archetype::{Archetype, ArchetypeId}, bundle::Bundle, query::{dynamic::DynamicView, AsFilter, AsQuery, Query, Res, ResMut, ViewIter, ViewOne, ViewState}, resource::ResourceData, ComponentInfo, DynTypeId, DynamicBundle, Entities, Entity, ResourceObject, Tick, TickTracker, TrackedResource};
use crate::{archetype::{Archetype, ArchetypeId}, bundle::Bundle, query::{dynamic::DynamicView, Filter, AsFilter, AsQuery, Query, Res, ResMut, ViewIter, ViewOneState, ViewState}, resource::ResourceData, ComponentInfo, DynTypeId, DynamicBundle, Entities, Entity, ResourceObject, Tick, TickTracker, TrackedResource};
/// The id of the entity for the Archetype.
///
@ -397,8 +397,8 @@ impl World {
DynamicView::new(self)
}
pub fn view_one<T: AsQuery>(&self, entity: Entity) -> ViewOne<T::Query> {
ViewOne::new(self, entity.id, T::Query::new())
pub fn view_one<T: AsQuery>(&self, entity: Entity) -> Option<<T::Query as Query>::Item<'_>> {
ViewOneState::<T::Query>::new(self, T::Query::new()).get(entity)
}
/// Add a resource to the world.
@ -688,7 +688,7 @@ mod tests {
world.insert(e, (Vec3::rand(),));
assert!(world.view_one::<&Vec3>(e).get().is_some())
assert!(world.view_one::<&Vec3>(e).is_some())
}
#[test]
@ -698,7 +698,7 @@ mod tests {
world.insert(e, (Vec3::rand(),));
assert!(world.view_one::<&Vec3>(e).get().is_some())
assert!(world.view_one::<&Vec3>(e).is_some())
}
#[test]
@ -712,7 +712,7 @@ mod tests {
let e3 = world.spawn(v2s[2]);
println!("Spawned entities");
let ev2 = world.view_one::<&Vec2>(e2).get()
let ev2 = world.view_one::<&Vec2>(e2)
.expect("Failed to find Vec2 and Vec3 on inserted entity!");
assert_eq!(*ev2, v2s[1]);
drop(ev2);
@ -722,7 +722,7 @@ mod tests {
world.insert(e, (v3,));
println!("inserted entity");
let (ev2, ev3) = world.view_one::<(&Vec2, &Vec3)>(e).get()
let (ev2, ev3) = world.view_one::<(&Vec2, &Vec3)>(e)
.expect("Failed to find Vec2 and Vec3 on inserted entity!");
assert_eq!(*ev2, v2);
assert_eq!(*ev3, v3);
@ -747,7 +747,7 @@ mod tests {
let e = world.spawn((v,));
let view = world.view_one::<&Vec2>(e);
assert_eq!(*view.get().unwrap(), v);
assert_eq!(*view.unwrap(), v);
}
#[test]
@ -782,11 +782,11 @@ mod tests {
world.insert(first, Vec2::new(50.0, 50.0));
let pos = world.view_one::<&mut Vec2>(first).get().unwrap();
let pos = world.view_one::<&mut Vec2>(first).unwrap();
assert_eq!(*pos, Vec2::new(50.0, 50.0));
drop(pos);
let pos = world.view_one::<&mut Vec2>(second).get().unwrap();
let pos = world.view_one::<&mut Vec2>(second).unwrap();
assert_eq!(*pos, Vec2::new(5.0, 5.0));
}

View File

@ -43,8 +43,6 @@ impl TransformsNode {
fn process_component_queue(world: &mut lyra_ecs::World, component_queue: Vec<(Entity, Option<InterpTransform>, Option<TransformIndex>)>) {
for (en, interp, index) in component_queue {
println!("writing index {:?} for entity {}", index, en.id().0);
match (interp, index) {
(None, None) => unreachable!(),
(None, Some(index)) => world.insert(en, index),
@ -94,7 +92,7 @@ fn update_transforms(
// Interpolate the transform for this entity using a component.
// If the entity does not have the component then it will be queued to be added
// to it after all the entities are prepared for rendering.
let transform = match interp_tran {
/* let transform = match interp_tran {
Some(mut interp_transform) => {
// found in https://youtu.be/YJB1QnEmlTs?t=472
interp_transform.alpha = 1.0 - interp_transform.alpha.powf(*delta_time);
@ -112,6 +110,21 @@ fn update_transforms(
component_queue.push((entity, Some(interp), None));
transform
}
}; */
let transform = match interp_tran {
Some(mut interp_transform) => {
interp_transform.last_transform = transform;
transform
}
None => {
let interp = InterpTransform {
last_transform: transform,
alpha: 1.0,
};
component_queue.push((entity, Some(interp), None));
transform
}
};
// Get the TransformIndex from the entity, or reserve a new one if the entity doesn't have
@ -120,11 +133,6 @@ fn update_transforms(
Some(i) => *i,
None => {
let i = buffers.reserve_transform(&device);
debug!(
"Reserved transform index {:?} for entity {}",
i,
entity.id().0
);
component_queue.push((entity, None, Some(i)));
i

View File

@ -1,24 +1,25 @@
use std::collections::VecDeque;
use glam::{UVec2, UVec3, Vec2, Vec3};
use lyra_ecs::{
query::{Entities, View},
relation::ChildOf,
Commands, Component, Entity, World,
query::{
filter::{Changed, Has, Not},
Entities, View, ViewOne,
},
relation::{ChildOf, RelationOriginComponent},
Commands, Component, Entity,
};
use lyra_math::Transform;
use lyra_reflect::Reflect;
use lyra_resource::ResHandle;
use lyra_scene::WorldTransform;
use tracing::{debug, error};
use tracing::error;
use crate::{game::GameStages, plugin::Plugin};
use super::{AtlasSprite, TextureAtlas};
/// A position relative to a tile on a tilemap
/// A position on a tile map
#[derive(Clone, Copy, Component, Reflect)]
pub struct RelativeToTile {
pub struct TileMapPos {
#[reflect(skip)] // TODO: impl reflect for Entity
pub tilemap_entity: Entity,
/// The position of the tile to spawn at.
@ -107,9 +108,9 @@ impl TileMap {
/// A system to update the tilemap when tiles are inserted/removed.
pub fn system_tilemap_update(
mut commands: Commands,
view: View<(&mut TileMap, &Transform)>,
view: View<(Entities, &mut TileMap)>,
) -> anyhow::Result<()> {
for (mut map, pos) in view.into_iter() {
for (map_en, mut map) in view.into_iter() {
let tile_size = map.tile_size;
let atlas_handle = map.atlas.clone();
let atlas = match atlas_handle.data_ref() {
@ -128,15 +129,32 @@ pub fn system_tilemap_update(
};
let grid = tile.tile.position * tile_size;
let sprite_pos = *pos
+ Transform::from_xyz(
grid.x as _,
grid.y as _,
tile.tile.z_level as _,
);
let sprite_pos = Transform::from_xyz(
grid.x as _,
// expand the grid downwards so 0,0 is top left of the tilemap
-(grid.y as f32),
tile.tile.z_level as _,
);
tile.entity = Some(commands.spawn((sprite, sprite_pos, WorldTransform::default())));
debug!("Spawned tile at ({}, {})", grid.x, grid.y);
let tile_en = commands.spawn((
sprite,
TileMapPos {
tilemap_entity: map_en,
position: tile.tile.position,
z_level: tile.tile.z_level,
},
tile.tile,
sprite_pos,
WorldTransform::default(),
));
commands.add_relation(tile_en, ChildOf, map_en);
tile.entity = Some(tile_en);
//debug!("Spawned tile at ({}, {})", grid.x, grid.y);
/* let tile_en = commands.spawn((sprite, sprite_pos, WorldTransform::default()));
commands.add_relation(tile_en, ChildOf, map_en);
tile.entity = Some(tile_en);
debug!("Spawned tile at ({}, {})", grid.x, grid.y); */
} else {
error!(
"Invalid atlas index '{}' for tile at pos '{:?}'",
@ -152,17 +170,22 @@ pub fn system_tilemap_update(
}
fn system_relative_tile_position_update(
world: &mut World,
view: View<(Entities, &RelativeToTile)>,
mut commands: Commands,
view: View<
(
Entities,
&TileMapPos,
Option<&mut Transform>,
Option<&WorldTransform>,
),
(Changed<TileMapPos>, Not<Has<Tile>>),
>,
tile_map_view: ViewOne<&TileMap>,
child_of_rel_view: ViewOne<&RelationOriginComponent<ChildOf>>,
) -> anyhow::Result<()> {
let mut to_relate = VecDeque::new();
for (en, rel) in view.into_iter() {
match world
.view_one::<(&TileMap, &Transform)>(rel.tilemap_entity)
.get()
{
Some((map, pos)) => {
for (en, rel, pos, wpos) in view.into_iter() {
match tile_map_view.get(rel.tilemap_entity) {
Some(map) => {
let layer = map.layers.last().unwrap();
if let Some(tile_en) = layer
@ -171,7 +194,27 @@ fn system_relative_tile_position_update(
.find(|t| t.tile.position == rel.position)
.and_then(|t| t.entity)
{
to_relate.push_back((en, tile_en, Transform::from_translation(Vec3::new(0.0, 0.0, rel.z_level as _))));
if child_of_rel_view
.get(en)
.map(|rel| rel.target() != tile_en)
.unwrap_or(true)
{
commands.add_relation(en, ChildOf, tile_en);
}
if let Some(mut pos) = pos {
*pos = Transform::from_translation(Vec3::new(0.0, 0.0, rel.z_level as _));
if wpos.is_none() {
commands.insert(en, WorldTransform::from(*pos));
}
} else {
let pos =
Transform::from_translation(Vec3::new(0.0, 0.0, rel.z_level as _));
commands.insert(en, (pos, WorldTransform::from(pos)));
}
} else {
error!("Unknown tile in map at {:?}", rel.position);
}
}
None => {
@ -183,13 +226,6 @@ fn system_relative_tile_position_update(
}
}
while let Some((from, to, pos)) = to_relate.pop_front() {
if world.view_one::<&WorldTransform>(from).get().is_none() {
world.add_relation(from, ChildOf, to);
world.insert(from, (pos, WorldTransform::default()));
}
}
Ok(())
}

View File

@ -226,7 +226,7 @@ impl ApplicationHandler for WinitRunner {
.expect("missing window entity");
// update the window and its cache so the sync system doesn't try to update the window
let (mut en_window, mut en_last_win) = self.app.world.view_one::<(&mut WindowOptions, &mut LastWindow)>(*en).get().unwrap();
let (mut en_window, mut en_last_win) = self.app.world.view_one::<(&mut WindowOptions, &mut LastWindow)>(*en).unwrap();
let pos = Some(DVec2::new(position.x, position.y));
en_window.set_physical_cursor_position(pos);
en_last_win.set_physical_cursor_position(pos);
@ -242,7 +242,7 @@ impl ApplicationHandler for WinitRunner {
.expect("world missing WinitWindows resource")
.window_to_entity
.get(&window_id)
.and_then(|e| self.app.world.view_one::<(&mut WindowOptions, &mut LastWindow)>(*e).get())
.and_then(|e| self.app.world.view_one::<(&mut WindowOptions, &mut LastWindow)>(*e))
.unwrap();
// update the window and its cache so the sync system doesn't try to update the window
@ -259,7 +259,7 @@ impl ApplicationHandler for WinitRunner {
.expect("world missing WinitWindows resource")
.window_to_entity
.get(&window_id)
.and_then(|e| self.app.world.view_one::<(&mut WindowOptions, &mut LastWindow)>(*e).get())
.and_then(|e| self.app.world.view_one::<(&mut WindowOptions, &mut LastWindow)>(*e))
.unwrap();
window.set_physical_cursor_position(None);
last_window.set_physical_cursor_position(None);
@ -284,7 +284,7 @@ impl ApplicationHandler for WinitRunner {
.expect("world missing WinitWindows resource")
.window_to_entity
.get(&window_id)
.and_then(|e| self.app.world.view_one::<&mut WindowOptions>(*e).get())
.and_then(|e| self.app.world.view_one::<&mut WindowOptions>(*e))
.unwrap();
window_opts.focused = focused;
},
@ -299,7 +299,7 @@ impl ApplicationHandler for WinitRunner {
.expect("world missing WinitWindows resource")
.window_to_entity
.get(&window_id)
.and_then(|e| self.app.world.view_one::<&mut WindowOptions>(*e).get())
.and_then(|e| self.app.world.view_one::<&mut WindowOptions>(*e))
.unwrap();
window_opts.scale_factor = scale_factor;
},
@ -311,7 +311,7 @@ impl ApplicationHandler for WinitRunner {
.expect("world missing WinitWindows resource")
.window_to_entity
.get(&window_id)
.and_then(|e| self.app.world.view_one::<&mut WindowOptions>(*e).get())
.and_then(|e| self.app.world.view_one::<&mut WindowOptions>(*e))
.unwrap();
window_opts.theme = Some(theme);
},
@ -323,7 +323,7 @@ impl ApplicationHandler for WinitRunner {
.expect("world missing WinitWindows resource")
.window_to_entity
.get(&window_id)
.and_then(|e| self.app.world.view_one::<&mut WindowOptions>(*e).get())
.and_then(|e| self.app.world.view_one::<&mut WindowOptions>(*e))
.unwrap();
window_opts.occluded = occ;
},

View File

@ -224,7 +224,8 @@ impl ResourceLoader for GltfLoader {
}
for en in graph.world().view_iter::<query::Entities>() {
graph.world().view_one::<(&WorldTransform, &Transform)>(en).get().expect("Scene node is missing world and local transform bundle!");
graph.world().view_one::<(&WorldTransform, &Transform)>(en)
.expect("Scene node is missing world and local transform bundle!");
}
let graph = ResHandle::new_ready(Some(path.as_str()), graph);
@ -300,14 +301,14 @@ mod tests {
let mut node = None;
//scene.world().view::<SceneNodeFlag()
scene.traverse_down::<_, &WorldTransform>(|_, no, tran| {
tran.get().expect("scene node is missing a WorldTransform");
tran.expect("scene node is missing a WorldTransform");
node = Some(no.clone());
});
let world = scene.world();
let node = node.unwrap();
let data = world.view_one::<(&ResHandle<Mesh>, &Transform)>(node.entity()).get();
let data = world.view_one::<(&ResHandle<Mesh>, &Transform)>(node.entity());
debug_assert!(data.is_some(), "The mesh was not loaded"); // transform will always be there
let data = data.unwrap();

View File

@ -73,17 +73,17 @@ impl<C: Component + Reflect> FromType<C> for ReflectedComponent {
},
fn_reflect: |world: &World, entity: Entity| {
world.view_one::<&C>(entity)
.get().map(|c| c as Ref<dyn Reflect>)
.map(|c| c as Ref<dyn Reflect>)
},
fn_reflect_mut: |world: &mut World, entity: Entity| {
world.view_one::<&mut C>(entity)
.get().map(|c| c as RefMut<dyn Reflect>)
.map(|c| c as RefMut<dyn Reflect>)
},
fn_reflect_tick: |world: &World, entity: Entity| {
world.view_one::<TickOf<C>>(entity).get()
world.view_one::<TickOf<C>>(entity)
},
fn_reflect_is_changed: |world: &World, entity: Entity| {
world.view_one::<Changed<C>>(entity).get()
world.view_one::<Changed<C>>(entity)
}
}
}

View File

@ -6,7 +6,7 @@ pub use node::*;
mod world_transform;
pub use world_transform::*;
use lyra_ecs::{query::{Entities, ViewOne}, relation::ChildOf, Bundle, Component, World};
use lyra_ecs::{query::{Entities, Query}, relation::ChildOf, Bundle, Component, World};
use lyra_math::Transform;
// So we can use lyra_ecs::Component derive macro
@ -95,7 +95,7 @@ impl SceneGraph {
/// The traversal does not include the root scene node.
pub fn traverse_down<F, Q>(&self, mut callback: F)
where
F: FnMut(&World, &SceneNode, ViewOne<Q::Query>),
F: FnMut(&World, &SceneNode, Option<<Q::Query as Query>::Item<'_>>),
Q: lyra_ecs::query::AsQuery,
{
self.traverse_down_from::<F, Q>(self.root_node.clone(), &mut callback);
@ -105,7 +105,7 @@ impl SceneGraph {
/// SceneNode and its world transform.
fn traverse_down_from<F, Q>(&self, start: SceneNode, callback: &mut F)
where
F: FnMut(&World, &SceneNode, ViewOne<Q::Query>),
F: FnMut(&World, &SceneNode, Option<<Q::Query as Query>::Item<'_>>),
Q: lyra_ecs::query::AsQuery,
{
let v = self.world
@ -211,7 +211,7 @@ pub mod tests {
let mut idx = 0;
scene.traverse_down::<_, &WorldTransform>(|_, _, v| {
let pos = v.get().unwrap();
let pos = v.unwrap();
if idx == 0 {
assert_eq!(**pos, Transform::from_translation(v2s[idx]));
} else if idx == 1 {

View File

@ -25,7 +25,7 @@ impl SceneNode {
if let Some(parent) = self.parent {
let v = graph.world.view_one::<&RelationOriginComponent<ChildOf>>(parent);
let p_parent = if let Some(pp) = v.get() {
let p_parent = if let Some(pp) = v {
Some(pp.target())
} else { None };
@ -81,7 +81,7 @@ mod tests {
let view = scene.world.filtered_view::<(Entities, &mut WorldTransform, &Transform, Option<&ResHandle<SceneGraph>>), Not<Has<RelationOriginComponent<ChildOf>>>>();
crate::system_update_world_transforms(&scene.world, view).unwrap();
let tran = scene.world.view_one::<&WorldTransform>(b.entity).get().unwrap();
let tran = scene.world.view_one::<&WorldTransform>(b.entity).unwrap();
assert_eq!(**tran, Transform::from_translation(Vec3::new(60.0, 60.0, 60.0)));
}
}

View File

@ -104,7 +104,7 @@ mod tests {
let view = world.filtered_view::<(Entities, &mut WorldTransform, &Transform, Option<&ResHandle<SceneGraph>>), Not<Has<RelationOriginComponent<ChildOf>>>>();
system_update_world_transforms(&world, view).unwrap();
let g = world.view_one::<&WorldTransform>(child).get().unwrap();
let g = world.view_one::<&WorldTransform>(child).unwrap();
assert_eq!(**g, Transform::from_xyz(25.0, 25.0, 25.0));
}
@ -131,7 +131,7 @@ mod tests {
let mut base_offset = 25.0;
for child in children.into_iter() {
let g = world.view_one::<&WorldTransform>(child).get().unwrap();
let g = world.view_one::<&WorldTransform>(child).unwrap();
println!("Child {:?} at {:?}", child, g.translation);
assert_eq!(**g, Transform::from_xyz(base_offset, base_offset, base_offset));
@ -162,7 +162,7 @@ mod tests {
let view = world.filtered_view::<(Entities, &mut WorldTransform, &Transform, Option<&ResHandle<SceneGraph>>), Not<Has<RelationOriginComponent<ChildOf>>>>();
system_update_world_transforms(&world, view).unwrap();
let g = world.view_one::<&WorldTransform>(five_child).get().unwrap();
let g = world.view_one::<&WorldTransform>(five_child).unwrap();
assert_eq!(**g, Transform::from_xyz(611.0, 248.0, 899.0));
}
@ -189,13 +189,13 @@ mod tests {
let view = world.filtered_view::<(Entities, &mut WorldTransform, &Transform, Option<&ResHandle<SceneGraph>>), Not<Has<RelationOriginComponent<ChildOf>>>>();
system_update_world_transforms(&world, view).unwrap();
let g = world.view_one::<&WorldTransform>(five_child).get().unwrap();
let g = world.view_one::<&WorldTransform>(five_child).unwrap();
assert_eq!(**g, Transform::from_xyz(536.0, 102.0, 817.0));
let g = world.view_one::<&WorldTransform>(thir_chi).get().unwrap();
let g = world.view_one::<&WorldTransform>(thir_chi).unwrap();
assert_eq!(**g, Transform::from_xyz(76.0, 110.0, 42.0));
let g = world.view_one::<&WorldTransform>(four_child).get().unwrap();
let g = world.view_one::<&WorldTransform>(four_child).unwrap();
assert_eq!(**g, Transform::from_xyz(100.0, 171.0, 107.0));
}
}

View File

@ -1,9 +1,11 @@
use lyra_engine::{
assets::{Image, ResourceManager},
ecs::{
query::{Res, ResMut, View}, Commands, Component, Entity, World
query::{Res, View, WorldTick},
relation::{ChildOf, RelationOriginComponent},
Commands, Component, Entity, World,
},
game::App,
game::{App, GameStages},
gltf::Gltf,
input::{
Action, ActionHandler, ActionKind, ActionMapping, ActionMappingId, ActionSource,
@ -18,8 +20,8 @@ use lyra_engine::{
ACTLBL_MOVE_LEFT_RIGHT, ACTLBL_MOVE_UP_DOWN,
},
sprite::{
self, AtlasAnimations, AtlasSprite, Pivot, RelativeToTile, Sprite, SpriteAnimation,
TextureAtlas, TileMap, TileMapPlugin,
self, AtlasAnimations, AtlasSprite, Pivot, Sprite, SpriteAnimation, TextureAtlas, Tile,
TileMap, TileMapPlugin, TileMapPos,
},
DeltaTime,
};
@ -27,7 +29,6 @@ use rand::{
distributions::{Distribution, WeightedIndex},
Rng,
};
use tracing::debug;
#[async_std::main]
async fn main() {
@ -105,41 +106,21 @@ async fn main() {
.with_plugin(setup_scene_plugin)
.with_plugin(action_handler_plugin)
.with_plugin(TileMapPlugin)
//.with_plugin(camera_debug_plugin)
.with_plugin(TopDown2dCameraPlugin)
.with_system(
.add_system_to_stage(
GameStages::Last,
"update_world_transforms",
system_update_world_transforms,
&[],
).with_system(
"spawn_egg",
system_spawn_egg,
&[],
).with_system(
"egg_location",
system_egg_location,
&[],
);
)
.with_system("spawn_egg", system_spawn_egg, &[]);
a.run();
}
fn setup_scene_plugin(app: &mut App) {
/* app.with_system(
"sprite_atlas_animation",
sprite::system_sprite_atlas_animation,
&[],
); */
let world = &mut app.world;
let resman = world.get_resource_mut::<ResourceManager>().unwrap();
let cube_gltf = resman
.request::<Gltf>("../assets/cube-texture-embedded.gltf")
.unwrap();
cube_gltf.wait_recurse_dependencies_load().unwrap();
let cube_mesh = &cube_gltf.data_ref().unwrap().scenes[0];
let grass_tileset = resman
.request::<Image>("../assets/sprout_lands/Tilesets/Grass.png")
.unwrap();
@ -209,7 +190,7 @@ fn setup_scene_plugin(app: &mut App) {
},
Transform::from_xyz(
(map_size.x * tile_size.x) as f32 * 0.5,
(map_size.y * tile_size.y) as f32 * 0.5,
(map_size.y * tile_size.y) as f32 * -0.5,
0.0,
),
TopDown2dCamera {
@ -223,25 +204,13 @@ fn setup_scene_plugin(app: &mut App) {
struct GroundTileMap(Entity);
#[derive(Component)]
struct EggEntity;
fn system_egg_location(view: View<(&WorldTransform, &EggEntity)>) -> anyhow::Result<()> {
for (pos, _) in view.into_iter() {
println!("Found egg at world pos {:?}", **pos);
}
Ok(())
}
fn system_spawn_egg(
mut commands: Commands,
inputs: Res<ActionHandler>,
tile_map: Res<GroundTileMap>,
resman: Res<ResourceManager>,
wtick: WorldTick,
) -> anyhow::Result<()> {
let debug_state = inputs.get_action_state("Debug").unwrap();
if inputs.was_action_just_pressed("Debug").unwrap() {
let egg = resman
.request::<Image>("../assets/sprout_lands/Objects/Egg_item.png")
@ -250,12 +219,14 @@ fn system_spawn_egg(
let x = rand::thread_rng().gen_range(0..32);
let y = rand::thread_rng().gen_range(0..16);
let rtt = RelativeToTile {
let rtt = TileMapPos {
tilemap_entity: tile_map.0,
position: UVec2::new(x, y),
z_level: -9,
z_level: 2,
};
//println!("Spawned egg at ({}, {}) on tick {}", x, y, **wtick);
commands.spawn((
Sprite {
texture: egg,
@ -263,7 +234,6 @@ fn system_spawn_egg(
pivot: Pivot::TopLeft,
},
rtt,
EggEntity
));
}