From 3f0ecd6093234c6e3a3941524e04f7abd28970ff Mon Sep 17 00:00:00 2001 From: gin Date: Wed, 29 May 2024 15:49:22 +0200 Subject: [PATCH] Miscellaneous improvements/changes (#138) * Add the ability to get all component types registered * Tags: changed to a single string: can name them as you create them * Update tag.go * Fix test --- README.md | 27 +++++++++++++++++++++++---- component.go | 15 +++++++++++++-- ecs/layer.go | 2 +- entry_test.go | 2 +- tag.go | 16 ++++++++++++++-- 5 files changed, 52 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index 0adbff7..ee23b8d 100644 --- a/README.md +++ b/README.md @@ -230,25 +230,44 @@ query.Each(world, func(entry *donburi.Entry) { } }) +``` + +## Ordered Queries +Sometimes you may need to iterate a query in a specific order. Donburi supports this through the `OrderedQuery[T]` type. +In order to use this, the component must implement the IOrderable interface: +```go +type IOrderable interface { + Order() int +} +``` + +Example: +Here we assume the `spatial.TransformComponent` implements `Order()`. +```go +q := donburi.NewOrderedQuery[spatial.Transform]( +filter.Contains(sprite.Component, spatial.TransformComponent)) +q.EachOrdered(w, spatial.TransformComponent, func(entry *donburi.Entry) { + // This will be iterated according to the spatial.TransformComponent's Order() function. +}) ``` ### Tags -One or multiple "Tag" components can be attached to an entity. "Tag"s are just components with no data. +One or multiple "Tag" components can be attached to an entity. "Tag"s are just components with a single name string as data. Here is the utility function to create a tag component. ```go // This is the utility function to make tag component -func NewTag() *ComponentType { - return NewComponentType(struct{}{}) +func NewTag(name string) *ComponentType { + return NewComponentType(Tag(name)) } ``` Since "Tags" are components, they can be used in queries in the same way as components as follows: ```go -var EnemyTag = donburi.NewTag() +var EnemyTag = donburi.NewTag("Enemy") world.CreateMany(100, EnemyTag, Position, Velocity) // Search entities with EnemyTag diff --git a/component.go b/component.go index 317c9a1..86cfafd 100644 --- a/component.go +++ b/component.go @@ -139,14 +139,16 @@ func (c *ComponentType[T]) validateDefaultVal() { } var nextComponentTypeId component.ComponentTypeId = 1 +var globalComponentTypes []component.IComponentType // NewComponentType creates a new component type. // The argument is a struct that represents a data of the component. func newComponentType[T any](s T, defaultVal interface{}) *ComponentType[T] { + typ := reflect.TypeOf(s) componentType := &ComponentType[T]{ id: nextComponentTypeId, - typ: reflect.TypeOf(s), - name: reflect.TypeOf(s).Name(), + typ: typ, + name: typ.Name(), defaultVal: defaultVal, } componentType.query = NewQuery(filter.Contains(componentType)) @@ -154,5 +156,14 @@ func newComponentType[T any](s T, defaultVal interface{}) *ComponentType[T] { componentType.validateDefaultVal() } nextComponentTypeId++ + globalComponentTypes = append(globalComponentTypes, componentType) return componentType } + +// AllComponentTypes returns all IComponentTypes created at that point in time. +// All types created using `donburi.NewComponentType()` will be in this list. +// This is useful if you want to use introspection on the ECS, such as when doing (de)serialization. +// This includes components which are not in any archetypes or on any entity. +func AllComponentTypes() []IComponentType { + return globalComponentTypes +} diff --git a/ecs/layer.go b/ecs/layer.go index 97f0833..8d12f52 100644 --- a/ecs/layer.go +++ b/ecs/layer.go @@ -68,7 +68,7 @@ func getLayer(layerID LayerID) *layer { if layers[layerID] == nil { layers[layerID] = &layer{ id: layerID, - tag: donburi.NewTag().SetName(fmt.Sprintf("Layer%d", layerID)), + tag: donburi.NewTag(fmt.Sprintf("Layer%d", layerID)), } } return layers[layerID] diff --git a/entry_test.go b/entry_test.go index 8ce8a11..bb291ac 100644 --- a/entry_test.go +++ b/entry_test.go @@ -47,7 +47,7 @@ func TestGetComponents(t *testing.T) { donburi.SetValue(entryA, velocity, veData) gots := donburi.GetComponents(entryA) - wants := []interface{}{trData, veData, struct{}{}} + wants := []interface{}{trData, veData, donburi.Tag("")} if len(gots) != len(wants) { t.Fatalf("got: %v, want: %v", gots, wants) diff --git a/tag.go b/tag.go index e78832e..f64a36c 100644 --- a/tag.go +++ b/tag.go @@ -1,7 +1,19 @@ package donburi +type Tag string + // NewTag is an utility to create a tag component. // Which is just an component that contains no data. -func NewTag() *ComponentType[struct{}] { - return NewComponentType[struct{}]() +// Specify a string as the first and only parameter if you wish to name the component. +func NewTag(opts ...any) *ComponentType[Tag] { + if len(opts) == 0 { + return NewComponentType[Tag]() + } + first, ok := opts[0].(string) + if !ok { + return NewComponentType[Tag]() + } + c := NewComponentType[Tag](Tag(first)) + c.SetName(first) + return c }