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

Question: Lifetime for components #717

Open
Kaiser1989 opened this issue Dec 12, 2020 · 10 comments
Open

Question: Lifetime for components #717

Kaiser1989 opened this issue Dec 12, 2020 · 10 comments

Comments

@Kaiser1989
Copy link

I use a "lifetime" component to handle entity's removing. This is pretty easy, as I can iterate all entities with "Lifetime" component and check if lifetime is still valid, otherwise delete entity.

I want to have the same for components. Some components should only live for "x" seconds.

Do you have any idea how to implement this?

I first thought of a lazy_exec with time delay (just removing comp from entity). The solution should be similar to lazy Update, some "Read" resource which can be written in parallel.

I do not want to iterate over all components.
I do not want a lifetime field in every component.

Thanks

@zesterer
Copy link
Contributor

zesterer commented Dec 12, 2020

If you want to avoid per-tick iteration, I'd recommend something like an event system that sits outside the ECS (or in an ECS resource) that uses a binary heap.

Entity IDs get inserted in an ordering consistent with the time at which they should be deleted. Every tick, you'd just have a system that pulls items out of the binary heap while they refer to entities that need destroying on that tick. When the next item in the heap does not need destroying on that tick, you know that no others in the heap do either (because they have a total ordering). This significantly reduces the overhead of checking for this sort of deletion.

The disadvantage is that it's quite hard to modify the lifetime of an entity on the fly. If you want that, I recommend using something like an LRU-style heap or one that allows reprioritising elements.

@Kaiser1989
Copy link
Author

Kaiser1989 commented Dec 12, 2020

If you want to avoid per-tick iteration, I'd recommend something like an event system that sits outside the ECS (or in an ECS resource) that uses a binary heap.

Entity IDs get inserted in an ordering consistent with the time at which they should be deleted. Every tick, you'd just have a system that pulls items out of the binary heap while they refer to entities that need destroying on that tick. When the next item in the heap does not need destroying on that tick, you know that no others in the heap do either (because they have a total ordering). This significantly reduces the overhead of checking for this sort of deletion.

The disadvantage is that it's quite hard to modify the lifetime of an entity on the fly. If you want that, I recommend using something like an LRU-style heap or one that allows reprioritising elements.

It's fine for me to iterate all lifetime entities, there aren't so many. My problem comes with lifetime for components.
I want to add any component, and remove hat after "x" seconds.

@zesterer
Copy link
Contributor

It's fine for me to iterate all lifetime entities, there aren't so many. My problem comes with lifetime for components.
I want to remove to add any component, and remove hat after "x" seconds.

In that case, just iterate over the join of entities and lifetimes and decrement some counter in the component each tick, until it hits 0

@Kaiser1989
Copy link
Author

We are talking about different things. I do not want to remove an entity with a lifetime component. It's absolutely clear to me how to do that.

I want to remove a component:

  • create Entity A
  • add Component Position to A
  • add Component Slow to A
  • remove Component Slow from A after x seconds

In this case, not the entity gets a lifetime component, but the component has a lifetime itself.

I want to add lifetimes to any component.

@zesterer
Copy link
Contributor

Then perhaps a component like struct Lifetime<C: Component>(u32, PhantomData<C>);? Then you can create a system that does this that is generic over C and insert it into the system dispatcher for every component you want to do this for.

@Kaiser1989
Copy link
Author

Kaiser1989 commented Dec 12, 2020

Now you got me right :)

Yes, I already thought about this. Although this would duplicate all components (which have a lifetime).
I'm currently doing my animations this way:

struct Animation<C: Component>{
    start: C, 
    end: C, 
    duration: f32, 
    current: f32, 
}

Adding a single new Component A, adds overhead for Animation<A> & Lifetime<A>, and even worse: Lifetime<Animation<A>>...

Is this the way to go?

I hoped for a more generic way:

  • Lazy Update with delay (allows add & remove)
    • would not allow interruption/extension
  • lifetime should be extended somehow

@zesterer
Copy link
Contributor

I don't think you can avoid that overhead unless you build an event system outside the ECS.

@minecrawler
Copy link
Contributor

What about adding the end-time to the component as a field, so that it's "slow until". You can check that field when you iterate over the slow entities and remove the component if the contained time passed.

@Kaiser1989
Copy link
Author

That's exactly the question. Should one add this per component as a field, or is there a generic solution to this "lifetime for components" problem.

(Of course it's always better to not decrease or increase per tick, but only check if "dead time" is reached)

@LoganDark
Copy link

LoganDark commented Feb 21, 2021

Adding a single new Component A, adds overhead for Animation<A> & Lifetime<A>, and even worse: Lifetime<Animation<A>>...

I think it's worth mentioning that Lifetime<A> will only be added overhead if you actually use it. If you never mention that type then Specs will never know or care about it because of monomorphization - Lifetime<A> is a completely different, unknown type, even if you use Lifetime<Slow>.

Making Lifetime generic is honestly the best solution I can see to this problem. It acts as the field that you would otherwise have to add to every component individually, and so fits the intended use of an ECS.

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

4 participants