-
Notifications
You must be signed in to change notification settings - Fork 19
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
Support for Tags / empty components #118
Comments
I had a similar idea a couple of months ago and just recently I tried to add it together with the new entity version solution that we now have to safely reference entities in components (2.5-SNAPSHOT). It might be easier now to implement tags because entities are now a enum class MyTags {
PLAYER_CONTROLLED,
HAS_COLLISION,
}
entity.tag[PLAYER_CONTROLLED] = true
entity.tag[HAS_COLLISION] = false
// and in Fleks we'd need something like
operator fun set(tag : Enum, value: Boolean) = tagBitArray[tag.ordinal()] = value However, I did a test locally and tried to integrate the component mask directly into the entity which had a big negative impact on the performance. I didn't understand why but it could be similar for tags. If so then an approach similar to the component mask of an entity is better. As you mentioned: I see a benefit for it because creating components without data is a little bit tidious and "overwhelming" for a simple "tagging" functionality. It works but I think tags would be easier. I also have use-cases in my examples projects where I "tag" e.g. entities that are player controlled or which have a collision. With that being said, I, unfortunately don't have a lot of time at the moment to work on features that require little bit of brain power and time ;) But as always, everyone is welcome to create a PR. I definitely have the time to review it! |
Thanks for the consideration, that's what I suspected. Would that solution with a bit field in the entity class still allow for fast iteration by using tags as filters? That'd be my main usecase for them. |
Ah you mean you still want to use it to define a family of an IteratingSystem? |
Ideally yes. E.g. you could put a "player" tag on one entity and then use a family of all entities that have a mesh component and the player tag. In this scenario the family would only contain a single entry. Either way, I see that it's complicated to incorporate into the current API. The easiest way would be to put a flag into ComponentType and use that to decide what storage medium (full fletched array of components vs bit field) to use in ComponentService. But I'd consider this a hacky approach given that it'd rely on a flag to select an implementation. |
Ah okay, if we still want to create families then it is of course trickier but it makes sense to do that Not sure how easy that is. I personally would like to have tags as an enum because it is easy to use and extend and in the end it shouldn't be more than a unique id. But not sure if we can get an easy and nice kotlin syntax going with that. Definitely needs some time to prepare a draft for that and try it out. Or do you want to go for a class approach meaning that a tag is a class that most likely needs to extend something? What would be your wish scenario and syntax for the different use cases? |
Speaking of drafting it out, I came up with a solution that works the following way: I replaced ComponentType with an interface TrackableType. That interface only defines that there's an id and provides a factory method for a so called Tracker. Trackers replace the raw components array used in ComponentsHolder and provide either the previous implemention of a components array, or an implementation using a bit field for a single value in case of tags. By using a TrackableType interface it is possible to define tags using an enum: private object TagTestComponent : TagComponent<TagTestComponent>()
enum class TagTestEnum : TrackableComponentType<TagTestEnum> by TagComponent() {
A,
B,
C
}
private class RegularTestComponent : Component<RegularTestComponent> {
override fun type() = RegularTestComponent
companion object : ComponentType<RegularTestComponent>()
} TagComponent provides the default implementation that is trivial for tags which makes them easier to define. The call site of tags in world, entities and families looks exactly the same as for Components, they are of type Component after all. The changes can be seen in this fork |
You are very quick 😅 I have an idea in my head as well that I want to try today in the evening when wife and son are sleeping. I will then also check your fork and will give some feedback by tomorrow morning. But from a quick glimpse that looks like a clean solution 👍 |
Thank you! I think my solution involves a bit too much abstraction over ComponentType which makes things more complicated. On the other hand I like that it is usable with enum classes, too. Sorry, I somehow failed to answer your questions above. Or do you want to go for a class approach meaning that a tag is a class that most likely needs to extend something?I prefer to be able to do it both, as a class approach similar to usual components or with enums. Not necessarily with the ability of extending other classes. It's probably a good idea to support the use of multiple, independent enum tag classes to help decouple functionality (we want to keep "engine" tags separate from game logic related ones). What would be your wish scenario and syntax for the different use cases?For most cases I want them to be used in the same way as components. Basically for ease of use. The code presented above does this by handling tags as singleton component objects for which only one instance has to be stored in the Components holder. Consequentially, entity[TagType] returns the TagType object itself for all entities that have the tag set and null otherwise. So contains can be used to query it and entity += and -= for setting / clearing. The most important thing to me is to be able to use them in families like regular Components. That way I can use tags to partition my entities into groups that are faster to iterate through with less overhead than regular Components. |
I had a look at your fork and it gave me the idea to also add support for enums in the PR draft - thank you for that! Let me know what you think about the draft, if something needs to be added/adapted. Fyi: For me it is sufficient to know if an entity has the tag or not. Or what would be the purpose to get the tag instance itself? |
I am glad that it was helpful to you. For me the draft looks ready to go, actually (plus minor details like doc and maybe special functions for tags). Also, I think the naming of the new classes and functions involved nails it quite well. |
Hi, I am not sure in which regard these fit into a light weight ECS after all, but they are a great feature to "tag" entities without using the memory a traditional component incurrs.
What are tags?
Basically empty components that could be defined using Kotlin's objects. There can only be one instance per Tag type and all the ECS has to store about it is whether a given entity has said tag. Implementation wise this would work like the ComponentMapper but with an internal bit array instead of a full blown component array (comparable to how ComponentMappers worked before the recent change).
Motivation
The advantage over using components for tagging (we could reuse a single object to share at least that part): Right now each component mapper will hold on array of up to size N for N being the max ever used enttiy id. Let's say that takes 8 bytes per array entry leading to a size of 8N bytes. A tag mapper implemented by a bit array would only use 0.125N bytes (that's a factor of 64).
Let's assume N = 1024 and 10 components, of which 5 could be replaced by simple tags.
I omitted some details like that regular components will use extra memory for each instance since that's not part of the consideration here.
How it could be done
It could be implemented along side component mappers. I am not sure if it would make sense to use a common interface for component and tag types to allow to use them interchangeably in family definitions.
The text was updated successfully, but these errors were encountered: