Skip to content

Commit

Permalink
Miscellaneous improvements/changes (#138)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
imthatgin committed May 29, 2024
1 parent 680901d commit 3f0ecd6
Show file tree
Hide file tree
Showing 5 changed files with 52 additions and 10 deletions.
27 changes: 23 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
15 changes: 13 additions & 2 deletions component.go
Original file line number Diff line number Diff line change
Expand Up @@ -139,20 +139,31 @@ 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))
if defaultVal != nil {
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
}
2 changes: 1 addition & 1 deletion ecs/layer.go
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Expand Down
2 changes: 1 addition & 1 deletion entry_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
16 changes: 14 additions & 2 deletions tag.go
Original file line number Diff line number Diff line change
@@ -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
}

0 comments on commit 3f0ecd6

Please sign in to comment.