Implemented dynamic layering. (should think of optimizations because it requires to sort objects every frame.

This commit is contained in:
Andrey Parhomenko 2023-08-31 11:29:37 +03:00
parent f081e9e6b4
commit 560bb93644
8 changed files with 138 additions and 108 deletions

1
go.mod
View file

@ -8,6 +8,7 @@ require (
github.com/hajimehoshi/ebiten/v2 v2.6.0-alpha.3.0.20230521122940-90562ee84b9b // indirect github.com/hajimehoshi/ebiten/v2 v2.6.0-alpha.3.0.20230521122940-90562ee84b9b // indirect
github.com/hajimehoshi/file2byteslice v1.0.0 // indirect github.com/hajimehoshi/file2byteslice v1.0.0 // indirect
github.com/jezek/xgb v1.1.0 // indirect github.com/jezek/xgb v1.1.0 // indirect
github.com/mojosa-software/godat v0.0.0-20230831073655-8009ad0e84fd // indirect
github.com/surdeus/godat v0.0.0-20230428145139-f51a8ab74bc8 // indirect github.com/surdeus/godat v0.0.0-20230428145139-f51a8ab74bc8 // indirect
golang.org/x/exp v0.0.0-20230213192124-5e25df0256eb // indirect golang.org/x/exp v0.0.0-20230213192124-5e25df0256eb // indirect
golang.org/x/exp/shiny v0.0.0-20230522175609-2e198f4a06a1 // indirect golang.org/x/exp/shiny v0.0.0-20230522175609-2e198f4a06a1 // indirect

2
go.sum
View file

@ -24,6 +24,8 @@ github.com/jezek/xgb v1.1.0 h1:wnpxJzP1+rkbGclEkmwpVFQWpuE2PUGNUzP8SbfFobk=
github.com/jezek/xgb v1.1.0/go.mod h1:nrhwO0FX/enq75I7Y7G8iN1ubpSGZEiA3v9e9GyRFlk= github.com/jezek/xgb v1.1.0/go.mod h1:nrhwO0FX/enq75I7Y7G8iN1ubpSGZEiA3v9e9GyRFlk=
github.com/jfreymuth/oggvorbis v1.0.4/go.mod h1:1U4pqWmghcoVsCJJ4fRBKv9peUJMBHixthRlBeD6uII= github.com/jfreymuth/oggvorbis v1.0.4/go.mod h1:1U4pqWmghcoVsCJJ4fRBKv9peUJMBHixthRlBeD6uII=
github.com/jfreymuth/vorbis v1.0.2/go.mod h1:DoftRo4AznKnShRl1GxiTFCseHr4zR9BN3TWXyuzrqQ= github.com/jfreymuth/vorbis v1.0.2/go.mod h1:DoftRo4AznKnShRl1GxiTFCseHr4zR9BN3TWXyuzrqQ=
github.com/mojosa-software/godat v0.0.0-20230831073655-8009ad0e84fd h1:zc6BBPR5U31BMLrjFDfLoZuvRIygaDAWnDbQxEYPxrc=
github.com/mojosa-software/godat v0.0.0-20230831073655-8009ad0e84fd/go.mod h1:E6ohOj8PpUJBQOSRdrLygjoO+Te6yfeox3ZtaItsHUg=
github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8/go.mod h1:HKlIX3XHQyzLZPlr7++PzdhaXEj94dEiJgZDTsxEqUI= github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8/go.mod h1:HKlIX3XHQyzLZPlr7++PzdhaXEj94dEiJgZDTsxEqUI=
github.com/surdeus/godat v0.0.0-20230217130825-eddf2c345cb7 h1:Fmoxhb42mS6BEmLF2spRiYlzes+S1VrEw0PnbR1ktUM= github.com/surdeus/godat v0.0.0-20230217130825-eddf2c345cb7 h1:Fmoxhb42mS6BEmLF2spRiYlzes+S1VrEw0PnbR1ktUM=
github.com/surdeus/godat v0.0.0-20230217130825-eddf2c345cb7/go.mod h1:tQGz8oe6Qig5yjYobaW1O8paXGGhzdukg8nT2bpvfes= github.com/surdeus/godat v0.0.0-20230217130825-eddf2c345cb7/go.mod h1:tQGz8oe6Qig5yjYobaW1O8paXGGhzdukg8nT2bpvfes=

View file

@ -11,20 +11,34 @@ import (
"math/rand" "math/rand"
) )
const (
HighestL gx.Layer = -iota
DebugL
TriangleL
PlayerL
RectL
LowestL
)
type Player struct { type Player struct {
*gx.Sprite *gx.Sprite
MoveSpeed gx.Float MoveSpeed gx.Float
ScaleSpeed gx.Float ScaleSpeed gx.Float
gx.Layer
} }
type Debug struct{} type Debug struct{
gx.Layer
}
type Rect struct { type Rect struct {
*gx.DrawableRectangle *gx.DrawableRectangle
gx.Layer
} }
type Tri struct { type Tri struct {
*gx.DrawablePolygon *gx.DrawablePolygon
gx.Layer
} }
func NewTri() *Tri { func NewTri() *Tri {
@ -46,12 +60,13 @@ func NewTri() *Tri {
} }
ret.Color = gx.Color{gx.MaxColorV, gx.MaxColorV, 0, gx.MaxColorV} ret.Color = gx.Color{gx.MaxColorV, gx.MaxColorV, 0, gx.MaxColorV}
ret.Visible = true ret.Visible = true
ret.Layer = TriangleL
return ret return ret
} }
func NewRect() *Rect { func NewRect() *Rect {
return &Rect{&gx.DrawableRectangle{ ret := &Rect{&gx.DrawableRectangle{
Rectangle: gx.Rectangle{ Rectangle: gx.Rectangle{
Transform: gx.Transform{ Transform: gx.Transform{
S: gx.Vector{ S: gx.Vector{
@ -76,7 +91,11 @@ func NewRect() *Rect {
nil, nil,
}, },
},*/ },*/
}} },
RectL,
}
return ret
} }
func (r *Rect) Update(e *gx.Engine) error { func (r *Rect) Update(e *gx.Engine) error {
@ -110,6 +129,7 @@ func NewPlayer() *Player {
} }
ret.Images[0] = playerImg ret.Images[0] = playerImg
ret.Layer = PlayerL
return ret return ret
} }
@ -206,6 +226,12 @@ func (p *Player) Update(e *gx.Engine) error {
} }
case ebiten.Key0 : case ebiten.Key0 :
e.Del(p) e.Del(p)
case ebiten.KeyB :
if p.Layer != PlayerL {
p.Layer = PlayerL
} else {
p.Layer = HighestL
}
}} }}
return nil return nil
@ -251,10 +277,11 @@ func main() {
rect = NewRect() rect = NewRect()
tri = NewTri() tri = NewTri()
e.Add(1, &Debug{}) e.Add(&Debug{})
e.Add(0, player) e.Add(player)
e.Add(-1, rect) e.Add(rect)
e.Add(100, tri) e.Add(tri)
fmt.Println(rect.GetLayer(), player.GetLayer())
e.Run() e.Run()
} }

View file

@ -20,26 +20,10 @@ type Collision struct {
// to determine if the object collides with anything. // to determine if the object collides with anything.
// Mostly will use the Collide function with some // Mostly will use the Collide function with some
// inner structure field as first argument. // inner structure field as first argument.
// The Collide method will be called on collisions.
type Collider interface { type Collider interface {
Collides(Collider) *Collision Collides(Collider) *Collision
}
// happening collision getting the Collision as
// argument.
type CollideEventer interface {
Collide(*Collision) Collide(*Collision)
} }
// Single function for all collision to remove
// functionality duplicating from the archtecture.
// Returns the collision if there is and nil if there
// is no collision.
/*func Collide(c1, c2 any) bool {
}
func triangleCollidesPoint(t Triangle, p Point) *Collision {
}
func triangleCollidesTriangle(t1, t2 Triangle) *Collision
*/

View file

@ -3,14 +3,19 @@ package gx
import ( import (
"github.com/hajimehoshi/ebiten/v2" "github.com/hajimehoshi/ebiten/v2"
"github.com/hajimehoshi/ebiten/v2/inpututil" "github.com/hajimehoshi/ebiten/v2/inpututil"
"github.com/surdeus/godat/src/sparsex" "github.com/mojosa-software/godat/sparsex"
"github.com/surdeus/godat/src/poolx" "github.com/mojosa-software/godat/mapx"
"fmt" "fmt"
"time" "time"
) )
// The type represents order of drawing. // The type represents order of drawing.
type Layer int // Higher values are drawn later.
type Layer float64
func (l Layer) GetLayer() Layer {
return l
}
// Window configuration type. // Window configuration type.
type WindowConfig struct { type WindowConfig struct {
@ -27,8 +32,7 @@ type WindowConfig struct {
// The main structure that represents current state of [game] engine. // The main structure that represents current state of [game] engine.
type Engine struct { type Engine struct {
wcfg *WindowConfig wcfg *WindowConfig
layers *sparsex.Sparse[Layer, *poolx.Pool[Drawer]] objects *mapx.OrderedMap[Object, struct{}]
updaters *poolx.Pool[Updater]
lastTime time.Time lastTime time.Time
dt Float dt Float
camera *Camera camera *Camera
@ -58,81 +62,59 @@ func NewEngine(
) *Engine { ) *Engine {
w := Float(cfg.Width) w := Float(cfg.Width)
h := Float(cfg.Height) h := Float(cfg.Height)
fmt.Println("res:", w, h)
return &Engine{ return &Engine{
wcfg: cfg, wcfg: cfg,
layers: sparsex.New[
Layer,
*poolx.Pool[Drawer],
](true),
camera: &Camera{ camera: &Camera{
Transform: Transform{ Transform: Transform{
// Normal, no distortion.
S: Vector{1, 1}, S: Vector{1, 1},
// Center.
RA: V(w/2, h/2), RA: V(w/2, h/2),
}, },
}, },
updaters: poolx.New[Updater](), objects: mapx.NewOrdered[Object, struct{}](),
} }
} }
// Add new object considering what // Add new object considering what
// interfaces it implements. // interfaces it implements.
func (e *Engine) Add(l Layer, b any) { func (e *Engine) Add(b any) error {
object, _ := b.(Object)
if e.objects.Has(object) {
return ObjectExistErr
}
/*o, ok := e.makeObject(b)
if !ok {
return ObjectNotImplementedErr
}*/
starter, ok := b.(Starter) starter, ok := b.(Starter)
if ok { if ok {
starter.Start(e) starter.Start(e)
} }
updater, ok := b.(Updater)
if ok {
e.addUpdater(updater)
}
drawer, ok := b.(Drawer) e.objects.Set(object, struct{}{})
if ok {
e.addDrawer(l, drawer) return nil
}
} }
// Delete object from Engine. // Delete object from Engine.
func (e *Engine) Del(b any, v ...any) { func (e *Engine) Del(b any) error {
object, _ := b.(Object)
if !e.objects.Has(object) {
return ObjectNotExistErr
}
deleter, ok := b.(Deleter) deleter, ok := b.(Deleter)
if ok { if ok {
deleter.Delete(e, v...) deleter.Delete(e)
}
drawer, ok := b.(Drawer)
if ok {
for layer := range e.layers.Vals() {
layer.V.Del(drawer)
}
}
updater, ok := b.(Updater)
if ok {
e.updaters.Del(updater)
}
}
func (e *Engine) addDrawer(l Layer, d Drawer) {
g, ok := e.layers.Get(l)
if !ok {
layer := poolx.New[Drawer]()
e.layers.Set(
l,
layer,
)
layer.Append(d)
} else {
g.Append(d)
} }
e.objects.Del(b)
return nil
} }
func (e *Engine) addUpdater(b Updater) {
e.updaters.Append(b)
}
func (e *engine) Update() error { func (e *engine) Update() error {
var err error var err error
@ -142,8 +124,12 @@ func (e *engine) Update() error {
AppendPressedKeys(e.keys[:0]) AppendPressedKeys(e.keys[:0])
e.dt = time.Since(e.lastTime).Seconds() e.dt = time.Since(e.lastTime).Seconds()
for p := range eng.updaters.Range() { for object := range e.objects.KeyChan() {
err = p.V.Update(eng) updater, ok := object.(Updater)
if !ok {
continue
}
err = updater.Update(eng)
if err != nil { if err != nil {
return err return err
} }
@ -155,13 +141,28 @@ func (e *engine) Update() error {
func (e *engine) Draw(i *ebiten.Image) { func (e *engine) Draw(i *ebiten.Image) {
eng := (*Engine)(e) eng := (*Engine)(e)
for p := range e.layers.Vals() { layers := sparsex.New[Layer, []Drawer]()
for pj := range p.V.Range() { for object := range eng.objects.KeyChan() {
visibler, ok := pj.V.(Visibler) drawer, ok := object.(Drawer)
if ok && !visibler.IsVisible() { if !ok {
continue continue
} }
pj.V.Draw(eng, i)
l := drawer.GetLayer()
layer, ok := layers.Get(l)
if !ok {
layers.Set(l, []Drawer{drawer})
continue
}
layers.Set(l, append(layer, drawer))
}
// Drawing sorted layers.
layers.Sort()
for layer := range layers.Chan() {
for _, drawer := range layer {
drawer.Draw(eng, i)
} }
} }
} }
@ -187,6 +188,7 @@ func (e *Engine) Run() error {
ebiten.SetVsyncEnabled(e.wcfg.VSync) ebiten.SetVsyncEnabled(e.wcfg.VSync)
e.lastTime = time.Now() e.lastTime = time.Now()
fmt.Println(e.objects)
return ebiten.RunGame((*engine)(e)) return ebiten.RunGame((*engine)(e))
} }

12
src/gx/errors.go Normal file
View file

@ -0,0 +1,12 @@
package gx
import (
"errors"
)
var (
ObjectExistErr = errors.New("the object already exists")
ObjectNotExistErr = errors.New("the object does not exist")
ObjectNotImplementedErr = errors.New("none of object methods are implemented")
)

View file

@ -16,32 +16,11 @@ type Color struct {
R, G, B, A ColorV R, G, B, A ColorV
} }
type Colority struct {
Color Color
}
type Visibility struct {
Visible bool
}
// The interface describes anything that can be
// drawn. It will be drew corresponding to
// the layers order.
type Drawer interface {
Draw(*Engine, *Image)
}
type Visibler interface {
IsVisible() bool
}
const ( const (
MaxColorV = math.MaxUint32 MaxColorV = math.MaxUint32
) )
func (v Visibility) IsVisible() bool {
return v.Visible
}
func LoadImage(input io.Reader) (*Image, error) { func LoadImage(input io.Reader) (*Image, error) {
img, _, err := image.Decode(input) img, _, err := image.Decode(input)

View file

@ -6,7 +6,7 @@ package gx
// the OnUpdate. // the OnUpdate.
// The v value will be get from Add function. // The v value will be get from Add function.
type Starter interface { type Starter interface {
Start(*Engine, ...any) Start(*Engine)
} }
// Implementing the interface type // Implementing the interface type
@ -23,3 +23,26 @@ type Deleter interface {
Delete(*Engine, ...any) Delete(*Engine, ...any)
} }
type Visibility struct {
Visible bool
}
func (v Visibility) IsVisible() bool {
return v.Visible
}
type Colority struct {
Color Color
}
// The interface describes anything that can be
// drawn. It will be drew corresponding to
// the layers order.
type Drawer interface {
Draw(*Engine, *Image)
GetLayer() Layer
IsVisible() bool
}
// The type represents everything that can work inside engine.
type Object any