Skip to content

Commit

Permalink
Merge pull request #31 from yohamta/develop
Browse files Browse the repository at this point in the history
Update ECS API
  • Loading branch information
yohamta authored Oct 15, 2022
2 parents d0d3c2e + bf9ddef commit 6386393
Show file tree
Hide file tree
Showing 13 changed files with 102 additions and 90 deletions.
51 changes: 31 additions & 20 deletions ecs/ecs.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,9 @@ type ECS struct {
// Time manages the time of the world.
Time *Time

layers []*layer
systems []UpdateSystem
layers []*layer
scriptSystem *scriptSystem
}

// NewECS creates a new ECS with the specified world.
Expand All @@ -24,46 +26,55 @@ func NewECS(w donburi.World) *ECS {
World: w,
Time: NewTime(),

layers: []*layer{},
systems: []UpdateSystem{},
layers: []*layer{},
scriptSystem: newScriptSystem(),
}

return ecs
}

// AddSystem adds new system
func (ecs *ECS) AddSystem(l Layer, s System) {
ecs.getLayer(l).addSystem(&system{System: s})
// AddUpdateSystems adds new update systems
func (ecs *ECS) AddUpdateSystems(systems ...UpdateSystem) {
ecs.systems = append(ecs.systems, systems...)
}

// AddUpdateSystem adds new update system
func (ecs *ECS) AddUpdateSystem(s UpdateSystem) {
ecs.addSystem(s)
}

// AddScript adds a script to the entities matched by the query.
func (ecs *ECS) AddScript(l Layer, s Script, q *query.Query) {
ecs.addScript(l, newScript(s, q))
// AddDrawSystem adds new draw system
func (ecs *ECS) AddDrawSystem(l Layer, s DrawSystem) {
ecs.getLayer(l).addSystem(&system{System: s})
}

// AddUpdateScript adds a script to the entities matched by the query.
func (ecs *ECS) AddUpdateScript(s UpdateScript, q *query.Query) {
ecs.scriptSystem.AddUpdateScript(s, q)
}

// ConfigLayer sets the layer configuration.
func (ecs *ECS) ConfigLayer(l Layer, cfg *LayerConfig) {
ecs.getLayer(l).Config(cfg)
// AddDrawScript adds a script to the entities matched by the query.
func (ecs *ECS) AddDrawScript(l Layer, s DrawScript, q *query.Query) {
ecs.getLayer(l).scriptSystem.AddDrawScript(s, q)
}

// Update calls Updater's Update() methods.
func (ecs *ECS) Update() {
ecs.Time.Update()
for _, l := range ecs.layers {
l.Update(ecs)
for _, s := range ecs.systems {
s.Update(ecs)
}
ecs.scriptSystem.Update(ecs)
}

// Draw calls Drawer's Draw() methods.
func (ecs *ECS) Draw(screen *ebiten.Image) {
for _, l := range ecs.layers {
l.Draw(ecs, screen)
}
func (ecs *ECS) Draw(l Layer, screen *ebiten.Image) {
ecs.getLayer(l).Draw(ecs, screen)
}

// AddScript adds a script to the entities matched by the query.
func (ecs *ECS) addScript(l Layer, s *script) {
ecs.getLayer(l).addScript(s)
func (ecs *ECS) addSystem(s UpdateSystem) {
ecs.systems = append(ecs.systems, s)
}

func (ecs *ECS) getLayer(l Layer) *layer {
Expand Down
12 changes: 7 additions & 5 deletions ecs/ecs_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@ func TestECS(t *testing.T) {
}

for _, sys := range systems {
ecs.AddSystem(sys.layer, sys.system)
ecs.AddUpdateSystem(sys.system)
ecs.AddDrawSystem(sys.layer, sys.system)
}

ecs.Update()
Expand All @@ -31,9 +32,9 @@ func TestECS(t *testing.T) {
ExpectedUpdateCount int
ExpectedUpdatedIndex int
}{
{systems[0].system, 1, 1},
{systems[1].system, 1, 2},
{systems[2].system, 1, 0},
{systems[0].system, 1, 0},
{systems[1].system, 1, 1},
{systems[2].system, 1, 2},
}

for idx, test := range updateTests {
Expand All @@ -46,7 +47,8 @@ func TestECS(t *testing.T) {
}
}

ecs.Draw(ebiten.NewImage(1, 1))
ecs.Draw(0, ebiten.NewImage(1, 1))
ecs.Draw(1, ebiten.NewImage(1, 1))

drawTests := []struct {
system *testSystem
Expand Down
19 changes: 1 addition & 18 deletions ecs/layer.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,8 @@ import (
"github.com/hajimehoshi/ebiten/v2"
)

// LayerConfig represents options for layers.
type LayerConfig struct {
Image *ebiten.Image
}

type system struct {
System System
System DrawSystem
}

type layer struct {
Expand All @@ -27,13 +22,9 @@ func newLayer() *layer {
}

func (l *layer) Update(e *ECS) {
for _, u := range l.systems {
u.System.Update(e)
}
l.scriptSystem.Update(e)
}

// Draw calls Drawer's Draw() methods.
func (l *layer) Draw(e *ECS, i *ebiten.Image) {
screen := i
if l.image != nil {
Expand All @@ -45,14 +36,6 @@ func (l *layer) Draw(e *ECS, i *ebiten.Image) {
l.scriptSystem.Draw(e, screen)
}

func (l *layer) Config(c *LayerConfig) {
l.image = c.Image
}

func (l *layer) addScript(s *script) {
l.scriptSystem.AddScript(s)
}

func (l *layer) addSystem(s *system) {
l.systems = append(l.systems, s)
}
53 changes: 34 additions & 19 deletions ecs/script.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,52 +6,67 @@ import (
"github.com/yohamta/donburi/query"
)

type script struct {
query *query.Query
callback Script
// UpdateScript is a script that updates the entity.
type UpdateScript interface {
Update(entry *donburi.Entry)
}

func newScript(scr Script, q *query.Query) *script {
return &script{
query: q,
callback: scr,
}
// DrawScript is a script that draws the entity.
type DrawScript interface {
Draw(entry *donburi.Entry, screen *ebiten.Image)
}

// Script is a function that runs on the entities matched by the query.
type Script interface {
Update(entry *donburi.Entry)
Draw(entry *donburi.Entry, screen *ebiten.Image)
type updateScript struct {
query *query.Query
callback UpdateScript
}

type drawScript struct {
query *query.Query
callback DrawScript
}

type scriptSystem struct {
scripts []*script
updateScripts []*updateScript
drawScripts []*drawScript
}

func newScriptSystem() *scriptSystem {
return &scriptSystem{
scripts: []*script{},
updateScripts: []*updateScript{},
drawScripts: []*drawScript{},
}
}

func (ss *scriptSystem) AddScript(scr *script) {
ss.scripts = append(ss.scripts, scr)
func (ss *scriptSystem) AddUpdateScript(s UpdateScript, q *query.Query) {
ss.updateScripts = append(ss.updateScripts, &updateScript{
query: q,
callback: s,
})
}

func (ss *scriptSystem) AddDrawScript(s DrawScript, q *query.Query) {
ss.drawScripts = append(ss.drawScripts, &drawScript{
query: q,
callback: s,
})
}

func (ss *scriptSystem) Update(ecs *ECS) {
for _, script := range ss.scripts {
for _, script := range ss.updateScripts {
script.query.EachEntity(ecs.World, script.callback.Update)
}
}

func (ss *scriptSystem) Draw(ecs *ECS, screen *ebiten.Image) {
for _, script := range ss.scripts {
for _, script := range ss.drawScripts {
script.query.EachEntity(ecs.World, func(entry *donburi.Entry) {
script.callback.Draw(entry, screen)
})
}
}

var (
_ = (System)((*scriptSystem)(nil))
_ = (UpdateSystem)((*scriptSystem)(nil))
_ = (DrawSystem)((*scriptSystem)(nil))
)
9 changes: 6 additions & 3 deletions ecs/script_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,15 @@ func TestScriptSystem(t *testing.T) {
scriptA := &testScript{}
scriptB := &testScript{}

ecs.AddScript(0, scriptA, queryA)
ecs.AddScript(0, scriptB, queryB)
ecs.AddUpdateScript(scriptA, queryA)
ecs.AddUpdateScript(scriptB, queryB)

ecs.AddDrawScript(0, scriptA, queryA)
ecs.AddDrawScript(0, scriptB, queryB)

ecs.Update()

ecs.Draw(ebiten.NewImage(1, 1))
ecs.Draw(0, ebiten.NewImage(1, 1))

tests := []struct {
script *testScript
Expand Down
8 changes: 8 additions & 0 deletions ecs/system.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,14 @@ type System interface {
Draw(ecs *ECS, screen *ebiten.Image)
}

type UpdateSystem interface {
Update(ecs *ECS)
}

type DrawSystem interface {
Draw(ecs *ECS, screen *ebiten.Image)
}

// SystemOpts represents options for systems.
type SystemOpts struct {
// Image is the image to draw the system.
Expand Down
25 changes: 15 additions & 10 deletions examples/bunnymark_ecs/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,26 +46,29 @@ func NewGame() *Game {
ecs: createECS(),
}

// System is a logic that updates the world.
g.ecs.AddSystem(LayerBackground, system.NewSpawn())
g.ecs.AddSystem(LayerBackground, &system.Background{})
g.ecs.AddSystem(LayerMetrics, system.NewMetrics(&g.bounds))
metrics := system.NewMetrics(&g.bounds)

// Script is a logic that updates the entries that match the query.
g.ecs.AddScript(LayerBunnies, scripts.NewBounce(&g.bounds),
g.ecs.AddUpdateSystems(
system.NewSpawn(),
metrics,
)
g.ecs.AddDrawSystem(LayerBackground, &system.Background{})
g.ecs.AddDrawSystem(LayerMetrics, metrics)

g.ecs.AddUpdateScript(scripts.NewBounce(&g.bounds),
query.NewQuery(filter.Contains(
component.Position,
component.Velocity,
component.Sprite,
)))

g.ecs.AddScript(LayerBunnies, &scripts.Velocity{},
g.ecs.AddUpdateScript(&scripts.Velocity{},
query.NewQuery(filter.Contains(component.Position, component.Velocity)))

g.ecs.AddScript(LayerBunnies, scripts.Gravity,
g.ecs.AddUpdateScript(scripts.Gravity,
query.NewQuery(filter.Contains(component.Velocity, component.Gravity)))

g.ecs.AddScript(LayerBunnies, scripts.Render,
g.ecs.AddDrawScript(LayerBunnies, scripts.Render,
query.NewQuery(filter.Contains(
component.Position,
component.Hue,
Expand Down Expand Up @@ -105,7 +108,9 @@ func (g *Game) Update() error {

func (g *Game) Draw(screen *ebiten.Image) {
screen.Clear()
g.ecs.Draw(screen)
g.ecs.Draw(LayerBackground, screen)
g.ecs.Draw(LayerBunnies, screen)
g.ecs.Draw(LayerMetrics, screen)
}

func (g *Game) Layout(width, height int) (int, int) {
Expand Down
3 changes: 0 additions & 3 deletions examples/bunnymark_ecs/scripts/bounce.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ package scripts
import (
"image"

"github.com/hajimehoshi/ebiten/v2"
"github.com/yohamta/donburi"
"github.com/yohamta/donburi/examples/bunnymark_ecs/component"
"github.com/yohamta/donburi/examples/bunnymark_ecs/helper"
Expand Down Expand Up @@ -45,5 +44,3 @@ func (b *bounce) Update(entry *donburi.Entry) {
position.Y = 0
}
}

func (b *bounce) Draw(entry *donburi.Entry, screen *ebiten.Image) {}
3 changes: 0 additions & 3 deletions examples/bunnymark_ecs/scripts/gravity.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package scripts

import (
"github.com/hajimehoshi/ebiten/v2"
"github.com/yohamta/donburi"
"github.com/yohamta/donburi/examples/bunnymark_ecs/component"
)
Expand All @@ -16,5 +15,3 @@ func (g *gravity) Update(entry *donburi.Entry) {

velocity.Y += gravity.Value
}

func (g *gravity) Draw(entry *donburi.Entry, screen *ebiten.Image) {}
2 changes: 0 additions & 2 deletions examples/bunnymark_ecs/scripts/render.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,6 @@ type render struct{}

var Render = &render{}

func (r *render) Update(entry *donburi.Entry) {}

func (r *render) Draw(entry *donburi.Entry, screen *ebiten.Image) {
position := component.GetPosition(entry)
hue := component.GetHue(entry)
Expand Down
3 changes: 0 additions & 3 deletions examples/bunnymark_ecs/scripts/velocity.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package scripts

import (
"github.com/hajimehoshi/ebiten/v2"
"github.com/yohamta/donburi"
"github.com/yohamta/donburi/examples/bunnymark_ecs/component"
)
Expand All @@ -15,5 +14,3 @@ func (v *Velocity) Update(entry *donburi.Entry) {
position.X += velocity.X
position.Y += velocity.Y
}

func (v *Velocity) Draw(entry *donburi.Entry, image *ebiten.Image) {}
2 changes: 0 additions & 2 deletions examples/bunnymark_ecs/system/background.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,6 @@ import (

type Background struct{}

func (b *Background) Update(ecs *ecs.ECS) {}

func (b *Background) Draw(ecs *ecs.ECS, screen *ebiten.Image) {
screen.Fill(color.RGBA{R: 41, G: 44, B: 45, A: 255})
}
Loading

0 comments on commit 6386393

Please sign in to comment.