Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How to use both View and ViewMut's Get trait as a generic? #151

Closed
PudgeKim opened this issue Oct 28, 2022 · 8 comments
Closed

How to use both View and ViewMut's Get trait as a generic? #151

PudgeKim opened this issue Oct 28, 2022 · 8 comments

Comments

@PudgeKim
Copy link

PudgeKim commented Oct 28, 2022

#[deirve(Component)]
struct Person();

fn my_func<T>(p: &T, id: EntityId, ...)
where T: Get
{
    if p.get(id).is_ok() { ... }
    ...
}

fn system1(
    people: View<Person> // This will be ViewMut in other system.
)
{
    ...
    my_func(&people, id, ...);
}

I'd like to create some function like this.

p can be View and ViewMut.
Can I get some help?

@toyboot4e
Copy link

toyboot4e commented Oct 28, 2022

Edit: Below I rushed. Get is implemented for &View and &ViewMut, so we can write like:

use shipyard::*;

#[derive(Component)]
struct Person();

fn my_func(p: impl Get, id: EntityId) {
    if p.get(id).is_ok() {}
}

fn system1(people: View<Person>) {
    let id = todo!();
    my_func(&people, id);
}

fn system1_mut(people: ViewMut<Person>) {
    let id = todo!();
    my_func(&people, id);
}

Previous comment:

I like this question ❤️ Maybe Get is not the way to go (?).

Both View<T> and ViewMut<T> implement AsRef<SparseSet<T>>, and we can write like:

use shipyard::*;

#[derive(Component)]
pub struct C(u32);

fn f_view(c: View<C>) {
    f(&c);
}

fn f_view_mut(c: ViewMut<C>) {
    f(&c);
}

fn f(_: &SparseSet<C>) { }

fn main() {
    let world = World::new();
    world.run(f_view);
    world.run(f_view_mut);
}

It's showing one of my favorite aspect of the beautiful shipyard; it's a simple, solid ECS library based on sparse sets :)

@toyboot4e
Copy link

toyboot4e commented Oct 28, 2022

..and SparseSet can be casted to the fancy &[T] with as_slice method!

Edit: Getting &T from SparseSet<T> with EntityId is a bit verbose (index_of + raw slice access). If SparseSet<T> is intended for such use cases, it might need more accessors (get or Index trait implementation)).

@PudgeKim
Copy link
Author

Thank you SparseSet<T> is also interesting.

@leudz
Copy link
Owner

leudz commented Oct 28, 2022

Thanks @toyboot4e!

I can give a bit of context to explain why &SparseSet isn't so good anymore.
When the library was created there was only SparseSet, even Unique was implemented inside a SparseSet.
As time went on I had to choose between simplicity and features.

The feature in this case is the "new" tracking, inspired by Bevy.
One of the con is that views have more information than SparseSet. Views know when systems run and can give an accurate list of entities added/modified/... since the last time the system ran.
With the previous implementation you had to be careful when resetting tracking to not clear the information too soon or you'd might miss some of the tracking information.

infex_of and as_slice are also at risk of disappearing when I add groups again. The current plan is not to remove them but limit them to non grouped SparseSet.

@PudgeKim
Copy link
Author

PudgeKim commented Oct 31, 2022

#[derive(Component)
struct Monster{ .. }

impl Monster {
    ...
    fn get_monster_id(&self) -> u64 { .. }
}

// If I only use impl Get, all components can be used as a parameter in this function.
// I want to limit the parameter just for &View<Monster> or &ViewMut<Monster>
// And this function should take both &View<Monster> and &ViewMut<Monster>
fn some_func(monsters: ...) {
    ...
    if let Ok(monster) = monsters.get(some_entity_id) {
        let monster_id = monster.get_monster_id();
        ...
    }
}

Can I do this?

@PudgeKim PudgeKim reopened this Oct 31, 2022
@leudz
Copy link
Owner

leudz commented Oct 31, 2022

You can restrict the component type by specifying Get's associated type.

fn some_func<'a>(monsters: impl Get<Out = &'a Monster>) {
    if let Ok(monster) = monsters.get(some_entity_id) {
        let monster_id = monster.get_monster_id();
    }
}

fn main() {
    let world = World::new();

    world.run(|vm_monster: ViewMut<Monster>| {
        some_func(&vm_monster);
    });

    world.run(|v_monster: View<Monster>| {
        some_func(&v_monster);
    });
}

@PudgeKim
Copy link
Author

Thank you for answering! But I have some minor problem..
The function works when I use View or ViewMut as a parameter. But after I changed it to impl Get<Out = &'a Monster>, this error occurred.

monsters: impl Get<Out=&'a Monster>
               --- captured outer variable

...entities.iter().all(|entity_id| {
   |                       -------- captured by this `FnMut` closure
match monsters.get(*entity_id) {
   |             ^^^^ ------------ `monsters` moved due to this method call
   |             |
   |             move occurs because `monsters` has type `impl Get<Out = &'a Monster>`, which does not implement the `Copy` trait

@leudz
Copy link
Owner

leudz commented Oct 31, 2022

You can add a Copy bound since all &T are Copy.

fn some_func<'a>(monsters: impl Get<Out = &'a Monster> + Copy) {

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants