#Lookup data in a DynamicBuffer?
1 messages · Page 1 of 1 (latest)
How large is the buffer most likely going to be? Can multiple buffer elements have the same enum value / how do you want to handle multiple of the same value? Is it frequent access on a buffer on a singleton or is the buffer on multiple entities?
The buffer should be pretty much static and hold one value per key. It's for an attribute system. I'm hoping to do something like attributesBuffer[Type.Health].Value
I can't have the attributes in separate components because I need generic logic to write to them through modifiers that point to a specific attribute type
As long as it's not going to be huge, iterating is probably fine
If you have a lot of data though, I wrote dynamic hash maps for this exact problem
Also possible to just initialize it into a nativehashmap before processing it, though probably not ideal for this usecase
Depending on how many attributes there are and how many entities need them, you can also make all entities always have all attributes, and just use the enum as an index. Then you could even make it a component since the enum size is known at compile time
I made a little attribute reader aspect for now:
public readonly partial struct AttributeReader : IAspect
{
public readonly DynamicBuffer<Attribute> _attributes;
public float this[AttributeType type]
{
get
{
foreach (var attribute in this._attributes)
{
if (attribute.Type == type)
{
return attribute.Value;
}
}
return 0f;
}
}
}
Can't say I'm too happy with this, but maybe it's just my OOP brain.
I would love to find a way to make it a component. The only problem is that I need a way to reference a specific attribute both at compile time and runtime.
The main reason is that I have a modifier system that recalculates all attributes depending on modifiers currently applied to the character.
foreach (var (attributes, modifiers) in SystemAPI
.Query<DynamicBuffer<Attribute>, DynamicBuffer<AttributeModifier>>())
{
foreach (var modifier in modifiers)
{
//find correct attribute with modifier.AttributeType
//edit the Attribute value with the modifier
}
}
Probably fine to do it like that for now, and change it later only if you actually experience lag from it
No reason not to do it like you said:
public struct Attributes : IComponentData
{
[GhostField] public float Health;
[GhostField] public float MovementSpeed;
public float this[Type type] =>
type switch
{
Type.Health => this.Health,
Type.MovementSpeed => this.MovementSpeed,
_ => throw new InvalidEnumArgumentException()
};
public enum Type
{
Health,
MovementSpeed
}
}
That works too, though again, that forces all attributes to be on all entities with that component. So really depends on your usecase.
For indexing I usually convert it to a span:
public static unsafe Span<Container> AsSpan(this ref ResourceContainer resourceContainer)
{
var elements = sizeof(ResourceContainer) / sizeof(Container);
var span = new Span<Container>(Unsafe.AsPointer(ref resourceContainer), elements);
return span;
}
Then just index that instead. Haven't checked if it's fast or not though, I think the sizeof might have some issues?
Though then you need the fields to be strictly in the same order as the enum, I don't know how that interacts with networking
just remember 2 things
- aspects always open DynamicBuffers as write so it screws with your dependencies
- aspects are deprecated
i'd suggest just making this an extension method instead
Didn't know that about aspects, thanks!
Now I'm having even more of a headache. I'd like to have attributes as individual components so that I can selectively read what interests my system.
Health component with Value field.
That's easy to do. But I would also like to have generic logic running on all my attributes. That's where I'm lost. There doesn't seem to be a way for me to tell ECS; "gather all modifiers that match that attribute, and change its value field to the computed value".
There are two attribute systems made for ecs already, might be a good idea to look at or use those if they fit your usecase
like mine 🙂 https://gitlab.com/tertle/com.bovinelabs.stats
reaction is a very different way of working with entities though
probably not for everyone
though you can implement a similar stat solution without reaction in a more traditional project
which i did at work
Thanks for the link I'll take a look! My main issue is that my stats need to be flat with the character to be serialized with Netcode. It would be easy to run the generic logic on separate stat entities for example