Skip to content

Commit

Permalink
Merge pull request #49 from yohamta/develop
Browse files Browse the repository at this point in the history
add hierarchy package
  • Loading branch information
yohamta authored Oct 21, 2022
2 parents 3bcdae4 + 8ffe82d commit 88cdc2a
Show file tree
Hide file tree
Showing 5 changed files with 170 additions and 0 deletions.
9 changes: 9 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ It aims to be a feature rich and high performance [ECS Library](https://en.wikip
- [Queries](#queries)
- [Tags](#tags)
- [Systems (Experimental)](#systems-experimental)
- [Hierarchy (Experimental)](#hierarchy-experimental)
- [Internal Design for `World`](#internal-design-for-world)
- [How to contribute?](#how-to-contribute)
- [Contributors](#contributors)
Expand Down Expand Up @@ -236,6 +237,14 @@ See [GoDoc](https://pkg.go.dev/github.com/yohamta/donburi/ecs) and [Example](htt

*Note: ECS package is experimental and API is unstable*

### Hierarchy (Experimental)

The [Hierarchy package](https://github.com/yohamta/donburi/tree/hierarchy/ecs) provides Parent-Children relationship function.

See [GoDoc](https://pkg.go.dev/github.com/yohamta/donburi/hierarchy) and [Example](https://github.com/yohamta/donburi/tree/main/hierarchy/example_test.go).

*Note: Hierarchy package is experimental and API is unstable*

## Internal Design for `World`

It is a bit complex, so please refer to the following diagram to understand it.
Expand Down
20 changes: 20 additions & 0 deletions hierarchy/children.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package hierarchy

import (
"github.com/yohamta/donburi"
)

type childrenData struct {
Children []donburi.Entity
}

var childrenComponent = donburi.NewComponentType[childrenData]()

// GetChildren returns children of the entry.
func GetChildren(entry *donburi.Entry) ([]donburi.Entity, bool) {
if entry.HasComponent(childrenComponent) {
c := donburi.Get[childrenData](entry, childrenComponent).Children
return c, true
}
return nil, false
}
66 changes: 66 additions & 0 deletions hierarchy/example_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
package hierarchy

import (
"testing"

"github.com/yohamta/donburi"
ecslib "github.com/yohamta/donburi/ecs"
)

func TestParent(t *testing.T) {
w := donburi.NewWorld()
ecs := ecslib.NewECS(w)

ecs.AddSystem(ecslib.System{
Update: HierarchySystem.RemoveChildren,
})

parent := donburi.NewTag().SetName("parent")
child := donburi.NewTag().SetName("child")
grandChild := donburi.NewTag().SetName("grandChild")

pe := w.Entry(w.Create(parent))
ce := w.Entry(w.Create(child))
ge := w.Entry(w.Create(grandChild))

SetParent(pe, ce)
SetParent(ce, ge)

if p, ok := GetParent(ce); p != pe.Entity() || !ok {
t.Errorf("expected parent entity %d, got %d", pe.Entity(), p)
}

if p, ok := GetParent(ge); p != ce.Entity() || !ok {
t.Errorf("expected parent entity %d, got %d", ce.Entity(), p)
}

if _, ok := GetParent(pe); ok {
t.Errorf("expected parent entity %d, got %d", donburi.Null, pe.Entity())
}

children, ok := GetChildren(pe)
if !ok {
t.Errorf("expected children, got nil")
}
if children[0] != ce.Entity() {
t.Errorf("expected child entity %d, got %d", ce.Entity(), children[0])
}

children, ok = GetChildren(ce)
if children[0] != ge.Entity() {
t.Errorf("expected child entity %d, got %d", ge.Entity(), children[0])
}

pe.Remove()
ecs.Update()

if w.Valid(ce.Entity()) {
t.Errorf("expected child entity %d to be removed", ce.Entity())
}
if w.Valid(ge.Entity()) {
t.Errorf("expected grand child entity %d to be removed", ge.Entity())
}
if w.Len() != 0 {
t.Errorf("expected world to be empty")
}
}
30 changes: 30 additions & 0 deletions hierarchy/hierarchy.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package hierarchy

import (
"github.com/yohamta/donburi"
"github.com/yohamta/donburi/ecs"
"github.com/yohamta/donburi/filter"
"github.com/yohamta/donburi/query"
)

// HierarchySystem is a system that removes children of invalid parents.
var HierarchySystem = &parent{
query: query.NewQuery(filter.Contains(parentComponent)),
}

func (ps *parent) RemoveChildren(ecs *ecs.ECS) {
ps.query.EachEntity(ecs.World, func(entry *donburi.Entry) {
if p, ok := GetParent(entry); ok {
if ecs.World.Valid(p) {
return
}
c, ok := GetChildren(entry)
if ok {
for _, e := range c {
ecs.World.Remove(e)
}
}
entry.Remove()
}
})
}
45 changes: 45 additions & 0 deletions hierarchy/parent.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package hierarchy

import (
"github.com/yohamta/donburi"
"github.com/yohamta/donburi/query"
)

type parentData struct {
Parent donburi.Entity
}

var parentComponent = donburi.NewComponentType[parentData]()

// GetParent returns a parent of the entry.
func GetParent(entry *donburi.Entry) (donburi.Entity, bool) {
if entry.HasComponent(parentComponent) {
p := donburi.Get[parentData](entry, parentComponent).Parent
return p, true
}
return donburi.Null, false
}

// SetParent sets a parent of the entry.
func SetParent(parent *donburi.Entry, child *donburi.Entry) {
if !parent.Valid() {
panic("parent is not valid")
}
if !child.Valid() {
panic("child is not valid")
}
if child.HasComponent(parentComponent) {
panic("child already has a parent")
}
if !parent.HasComponent(childrenComponent) {
parent.AddComponent(childrenComponent)
}
child.AddComponent(parentComponent)
donburi.SetValue(child, parentComponent, parentData{Parent: parent.Entity()})
children := donburi.Get[childrenData](parent, childrenComponent)
children.Children = append(children.Children, child.Entity())
}

type parent struct {
query *query.Query
}

0 comments on commit 88cdc2a

Please sign in to comment.