diff --git a/examples/nodes/component/boundaries.go b/examples/nodes/component/boundaries.go new file mode 100644 index 0000000..35c732b --- /dev/null +++ b/examples/nodes/component/boundaries.go @@ -0,0 +1,12 @@ +package component + +import "image" + +//go:generate golem component Boundaries +type Boundaries struct { + image.Rectangle +} + +func NewBoundaries(x, y, w, h int) *Boundaries { + return &Boundaries{Rectangle: image.Rect(x, y, w+x, h+y)} +} diff --git a/examples/nodes/component/boundaries_golem.go b/examples/nodes/component/boundaries_golem.go new file mode 100644 index 0000000..59ded6f --- /dev/null +++ b/examples/nodes/component/boundaries_golem.go @@ -0,0 +1,21 @@ +// Code generated by Golem. DO NOT EDIT. +// Source: https://github.com/t-geindre/golem + +package component + +import "github.com/t-geindre/golem/pkg/golem" + +type BoundariesGolemI interface { + GetBoundaries() *Boundaries +} + +func (p *Boundaries) GetBoundaries() *Boundaries { + return p +} + +func GetBoundaries(e golem.Entity) *Boundaries { + if p, ok := e.(BoundariesGolemI); ok { + return p.GetBoundaries() + } + return nil +} \ No newline at end of file diff --git a/examples/nodes/component/color.go b/examples/nodes/component/color.go new file mode 100644 index 0000000..9d93b15 --- /dev/null +++ b/examples/nodes/component/color.go @@ -0,0 +1,20 @@ +package component + +import ( + "image/color" + "math/rand" +) + +//go:generate golem component Color +type Color struct { + color.Color +} + +func NewRandomColor() *Color { + return &Color{Color: color.RGBA{ + R: uint8(rand.Intn(155) + 100), + G: uint8(rand.Intn(155) + 100), + B: uint8(rand.Intn(155) + 100), + A: 0xff, + }} +} diff --git a/examples/nodes/component/color_golem.go b/examples/nodes/component/color_golem.go new file mode 100644 index 0000000..b7d8b6d --- /dev/null +++ b/examples/nodes/component/color_golem.go @@ -0,0 +1,21 @@ +// Code generated by Golem. DO NOT EDIT. +// Source: https://github.com/t-geindre/golem + +package component + +import "github.com/t-geindre/golem/pkg/golem" + +type ColorGolemI interface { + GetColor() *Color +} + +func (p *Color) GetColor() *Color { + return p +} + +func GetColor(e golem.Entity) *Color { + if p, ok := e.(ColorGolemI); ok { + return p.GetColor() + } + return nil +} \ No newline at end of file diff --git a/examples/nodes/component/geometry.go b/examples/nodes/component/geometry.go new file mode 100644 index 0000000..2a1788d --- /dev/null +++ b/examples/nodes/component/geometry.go @@ -0,0 +1,12 @@ +package component + +import "github.com/hajimehoshi/ebiten/v2" + +type Geometry struct { + *ebiten.GeoM +} + +//go:generate golem component Geometry +func NewGeometry() *Geometry { + return &Geometry{GeoM: &ebiten.GeoM{}} +} diff --git a/examples/nodes/component/geometry_golem.go b/examples/nodes/component/geometry_golem.go new file mode 100644 index 0000000..2bffab8 --- /dev/null +++ b/examples/nodes/component/geometry_golem.go @@ -0,0 +1,21 @@ +// Code generated by Golem. DO NOT EDIT. +// Source: https://github.com/t-geindre/golem + +package component + +import "github.com/t-geindre/golem/pkg/golem" + +type GeometryGolemI interface { + GetGeometry() *Geometry +} + +func (p *Geometry) GetGeometry() *Geometry { + return p +} + +func GetGeometry(e golem.Entity) *Geometry { + if p, ok := e.(GeometryGolemI); ok { + return p.GetGeometry() + } + return nil +} \ No newline at end of file diff --git a/examples/nodes/component/velocity.go b/examples/nodes/component/velocity.go new file mode 100644 index 0000000..f7f0c8f --- /dev/null +++ b/examples/nodes/component/velocity.go @@ -0,0 +1,12 @@ +package component + +import "image" + +//go:generate golem component Velocity +type Velocity struct { + image.Point +} + +func NewVelocity(x, y int) *Velocity { + return &Velocity{Point: image.Pt(x, y)} +} diff --git a/examples/nodes/component/velocity_golem.go b/examples/nodes/component/velocity_golem.go new file mode 100644 index 0000000..3f0d9d5 --- /dev/null +++ b/examples/nodes/component/velocity_golem.go @@ -0,0 +1,21 @@ +// Code generated by Golem. DO NOT EDIT. +// Source: https://github.com/t-geindre/golem + +package component + +import "github.com/t-geindre/golem/pkg/golem" + +type VelocityGolemI interface { + GetVelocity() *Velocity +} + +func (p *Velocity) GetVelocity() *Velocity { + return p +} + +func GetVelocity(e golem.Entity) *Velocity { + if p, ok := e.(VelocityGolemI); ok { + return p.GetVelocity() + } + return nil +} \ No newline at end of file diff --git a/examples/nodes/entity/node.go b/examples/nodes/entity/node.go new file mode 100644 index 0000000..7400872 --- /dev/null +++ b/examples/nodes/entity/node.go @@ -0,0 +1,30 @@ +package entity + +import ( + "github.com/t-geindre/golem/examples/nodes/component" + "github.com/t-geindre/golem/pkg/golem" +) + +type Node struct { + golem.Entity + golem.World + *component.Boundaries + *component.Velocity + *component.Geometry + *component.Color +} + +func NewNode(l golem.LayerID, vx, vy, x, y, w, h int) *Node { + n := &Node{ + Entity: golem.NewEntity(l), + World: golem.NewWorld(), + Boundaries: component.NewBoundaries(x, y, w, h), + Velocity: component.NewVelocity(vx, vy), + Geometry: component.NewGeometry(), + Color: component.NewRandomColor(), + } + + n.SetParentSharedSystems(true) + + return n +} diff --git a/examples/nodes/go.mod b/examples/nodes/go.mod new file mode 100644 index 0000000..83fe26a --- /dev/null +++ b/examples/nodes/go.mod @@ -0,0 +1,20 @@ +module github.com/t-geindre/golem/examples/nodes + +go 1.23.1 + +require ( + github.com/hajimehoshi/ebiten/v2 v2.7.8 + github.com/t-geindre/golem v0.0.0-20241013093716-b09d869e6333 +) + +require ( + github.com/ebitengine/gomobile v0.0.0-20240518074828-e86332849895 // indirect + github.com/ebitengine/hideconsole v1.0.0 // indirect + github.com/ebitengine/purego v0.7.0 // indirect + github.com/jezek/xgb v1.1.1 // indirect + golang.org/x/image v0.18.0 // indirect + golang.org/x/sync v0.7.0 // indirect + golang.org/x/sys v0.20.0 // indirect +) + +replace github.com/t-geindre/golem => ..\..\ diff --git a/examples/nodes/go.sum b/examples/nodes/go.sum new file mode 100644 index 0000000..8367294 --- /dev/null +++ b/examples/nodes/go.sum @@ -0,0 +1,20 @@ +github.com/ebitengine/gomobile v0.0.0-20240518074828-e86332849895 h1:48bCqKTuD7Z0UovDfvpCn7wZ0GUZ+yosIteNDthn3FU= +github.com/ebitengine/gomobile v0.0.0-20240518074828-e86332849895/go.mod h1:XZdLv05c5hOZm3fM2NlJ92FyEZjnslcMcNRrhxs8+8M= +github.com/ebitengine/hideconsole v1.0.0 h1:5J4U0kXF+pv/DhiXt5/lTz0eO5ogJ1iXb8Yj1yReDqE= +github.com/ebitengine/hideconsole v1.0.0/go.mod h1:hTTBTvVYWKBuxPr7peweneWdkUwEuHuB3C1R/ielR1A= +github.com/ebitengine/purego v0.7.0 h1:HPZpl61edMGCEW6XK2nsR6+7AnJ3unUxpTZBkkIXnMc= +github.com/ebitengine/purego v0.7.0/go.mod h1:ah1In8AOtksoNK6yk5z1HTJeUkC1Ez4Wk2idgGslMwQ= +github.com/hajimehoshi/bitmapfont/v3 v3.0.0 h1:r2+6gYK38nfztS/et50gHAswb9hXgxXECYgE8Nczmi4= +github.com/hajimehoshi/bitmapfont/v3 v3.0.0/go.mod h1:+CxxG+uMmgU4mI2poq944i3uZ6UYFfAkj9V6WqmuvZA= +github.com/hajimehoshi/ebiten/v2 v2.7.8 h1:QrlvF2byCzMuDsbxFReJkOCbM3O2z1H/NKQaGcA8PKk= +github.com/hajimehoshi/ebiten/v2 v2.7.8/go.mod h1:Ulbq5xDmdx47P24EJ+Mb31Zps7vQq+guieG9mghQUaA= +github.com/jezek/xgb v1.1.1 h1:bE/r8ZZtSv7l9gk6nU0mYx51aXrvnyb44892TwSaqS4= +github.com/jezek/xgb v1.1.1/go.mod h1:nrhwO0FX/enq75I7Y7G8iN1ubpSGZEiA3v9e9GyRFlk= +golang.org/x/image v0.18.0 h1:jGzIakQa/ZXI1I0Fxvaa9W7yP25TqT6cHIHn+6CqvSQ= +golang.org/x/image v0.18.0/go.mod h1:4yyo5vMFQjVjUcVk4jEQcU9MGy/rulF5WvUILseCM2E= +golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M= +golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y= +golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4= +golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI= diff --git a/examples/nodes/main.go b/examples/nodes/main.go new file mode 100644 index 0000000..4773184 --- /dev/null +++ b/examples/nodes/main.go @@ -0,0 +1,51 @@ +package main + +import ( + "github.com/hajimehoshi/ebiten/v2" + "github.com/t-geindre/golem/examples/nodes/entity" + "github.com/t-geindre/golem/examples/nodes/system" + "github.com/t-geindre/golem/pkg/golemutils" + "math/rand" + "time" +) + +func main() { + const ( + LayerAll = iota + LayerDebug + ) + + ww, wh := 800, 800 + ebiten.SetWindowSize(ww, wh) + + root := entity.NewNode(LayerAll, 1, 2, 30, 30, ww-60, wh-60) + for i := 0; i < 10; i++ { + cont := entity.NewNode( + LayerAll, + 1+rand.Intn(5), + 1+rand.Intn(5), + 0, + 0, + 150, + 150, + ) + for j := 0; j < 10; j++ { + child := entity.NewNode(LayerAll, rand.Intn(3), rand.Intn(3), 10, 10, 10, 10) + cont.AddEntity(child) + } + root.AddEntity(cont) + } + + game := golemutils.NewGame() + game.AddEntity(root) + + game.AddSystems( + system.NewMove(), + system.NewBounce(), + system.NewGeometry(), + system.NewRenderer(), + golemutils.NewMetrics(LayerDebug, time.Millisecond*100), + ) + + ebiten.RunGame(game) +} diff --git a/examples/nodes/system/bounce.go b/examples/nodes/system/bounce.go new file mode 100644 index 0000000..cbdf2eb --- /dev/null +++ b/examples/nodes/system/bounce.go @@ -0,0 +1,48 @@ +package system + +import ( + "github.com/hajimehoshi/ebiten/v2" + "github.com/t-geindre/golem/examples/nodes/component" + "github.com/t-geindre/golem/pkg/golem" + "image" +) + +type Bounce struct { + bds image.Rectangle +} + +func NewBounce() *Bounce { + return &Bounce{} +} + +func (b *Bounce) UpdateOnce(w golem.World, c golem.Clock) { + ww, wh := ebiten.WindowSize() + b.bds = image.Rect(0, 0, ww, wh) +} + +func (b *Bounce) Update(e golem.Entity, w golem.World, _ golem.Clock) { + eBds := component.GetBoundaries(e) + eVel := component.GetVelocity(e) + + if eBds == nil || eVel == nil { + return + } + + bds := b.bds + rBds := eBds.Rectangle + if p := w.GetParentEntity(); p != nil { + if pb := component.GetBoundaries(p); pb != nil { + bds = pb.Rectangle + rBds = eBds.Add(pb.Rectangle.Min) + } + } + + // This can be fairly improved, if an entity gets out of the boundaries, it will be stuck + if rBds.Min.X < bds.Min.X || rBds.Max.X > bds.Max.X { + eVel.X = -eVel.X + } + + if rBds.Min.Y < bds.Min.Y || rBds.Max.Y > bds.Max.Y { + eVel.Y = -eVel.Y + } +} diff --git a/examples/nodes/system/geometry.go b/examples/nodes/system/geometry.go new file mode 100644 index 0000000..fe125be --- /dev/null +++ b/examples/nodes/system/geometry.go @@ -0,0 +1,36 @@ +package system + +import ( + "github.com/t-geindre/golem/examples/nodes/component" + "github.com/t-geindre/golem/pkg/golem" +) + +type Geometry struct { +} + +func NewGeometry() *Geometry { + return &Geometry{} +} + +func (g *Geometry) Update(e golem.Entity, w golem.World, _ golem.Clock) { + geo := component.GetGeometry(e) + if geo == nil { + return + } + + geo.GeoM.Reset() + + parent := w.GetParentEntity() + if parent != nil { + if parentGeo := component.GetGeometry(parent); parentGeo != nil { + geo.GeoM.Concat(*parentGeo.GeoM) + } + } + + bds := component.GetBoundaries(e) + if bds == nil { + return + } + + geo.Translate(float64(bds.Rectangle.Min.X), float64(bds.Rectangle.Min.Y)) +} diff --git a/examples/nodes/system/move.go b/examples/nodes/system/move.go new file mode 100644 index 0000000..33acffc --- /dev/null +++ b/examples/nodes/system/move.go @@ -0,0 +1,27 @@ +package system + +import ( + "github.com/t-geindre/golem/examples/nodes/component" + "github.com/t-geindre/golem/pkg/golem" +) + +type Move struct { +} + +func NewMove() *Move { + return &Move{} +} + +func (m *Move) Update(e golem.Entity, _ golem.World, _ golem.Clock) { + vel := component.GetVelocity(e) + if vel == nil { + return + } + + bds := component.GetBoundaries(e) + if bds == nil { + return + } + + bds.Rectangle = bds.Add(vel.Point) +} diff --git a/examples/nodes/system/renderer.go b/examples/nodes/system/renderer.go new file mode 100644 index 0000000..5ed2838 --- /dev/null +++ b/examples/nodes/system/renderer.go @@ -0,0 +1,43 @@ +package system + +import ( + "github.com/hajimehoshi/ebiten/v2" + "github.com/hajimehoshi/ebiten/v2/vector" + "github.com/t-geindre/golem/examples/nodes/component" + "github.com/t-geindre/golem/pkg/golem" +) + +type Renderer struct { +} + +func NewRenderer() *Renderer { + return &Renderer{} +} + +func (d *Renderer) Draw(e golem.Entity, screen *ebiten.Image, w golem.World) { + geo := component.GetGeometry(e) + if geo == nil { + return + } + + bds := component.GetBoundaries(e) + if bds == nil { + return + } + + col := component.GetColor(e) + if col == nil { + return + } + + xs, ys := geo.GeoM.Apply(float64(0), float64(0)) + xe, ye := geo.GeoM.Apply(float64(bds.Rectangle.Dx()), float64(bds.Rectangle.Dy())) + + vector.StrokeRect( + screen, + float32(xs), float32(ys), + float32(xe-xs), float32(ye-ys), + 1, col, + false, + ) +}