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/file2byteslice v1.0.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
golang.org/x/exp v0.0.0-20230213192124-5e25df0256eb // 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/jfreymuth/oggvorbis v1.0.4/go.mod h1:1U4pqWmghcoVsCJJ4fRBKv9peUJMBHixthRlBeD6uII=
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/surdeus/godat v0.0.0-20230217130825-eddf2c345cb7 h1:Fmoxhb42mS6BEmLF2spRiYlzes+S1VrEw0PnbR1ktUM=
github.com/surdeus/godat v0.0.0-20230217130825-eddf2c345cb7/go.mod h1:tQGz8oe6Qig5yjYobaW1O8paXGGhzdukg8nT2bpvfes=

View file

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

View file

@ -20,26 +20,10 @@ type Collision struct {
// to determine if the object collides with anything.
// Mostly will use the Collide function with some
// inner structure field as first argument.
// The Collide method will be called on collisions.
type Collider interface {
Collides(Collider) *Collision
}
// happening collision getting the Collision as
// argument.
type CollideEventer interface {
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 (
"github.com/hajimehoshi/ebiten/v2"
"github.com/hajimehoshi/ebiten/v2/inpututil"
"github.com/surdeus/godat/src/sparsex"
"github.com/surdeus/godat/src/poolx"
"github.com/mojosa-software/godat/sparsex"
"github.com/mojosa-software/godat/mapx"
"fmt"
"time"
)
// 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.
type WindowConfig struct {
@ -27,8 +32,7 @@ type WindowConfig struct {
// The main structure that represents current state of [game] engine.
type Engine struct {
wcfg *WindowConfig
layers *sparsex.Sparse[Layer, *poolx.Pool[Drawer]]
updaters *poolx.Pool[Updater]
objects *mapx.OrderedMap[Object, struct{}]
lastTime time.Time
dt Float
camera *Camera
@ -58,81 +62,59 @@ func NewEngine(
) *Engine {
w := Float(cfg.Width)
h := Float(cfg.Height)
fmt.Println("res:", w, h)
return &Engine{
wcfg: cfg,
layers: sparsex.New[
Layer,
*poolx.Pool[Drawer],
](true),
camera: &Camera{
Transform: Transform{
// Normal, no distortion.
S: Vector{1, 1},
// Center.
RA: V(w/2, h/2),
},
},
updaters: poolx.New[Updater](),
objects: mapx.NewOrdered[Object, struct{}](),
}
}
// Add new object considering what
// 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)
if ok {
starter.Start(e)
}
updater, ok := b.(Updater)
if ok {
e.addUpdater(updater)
}
e.objects.Set(object, struct{}{})
drawer, ok := b.(Drawer)
if ok {
e.addDrawer(l, drawer)
}
return nil
}
// 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)
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)
}
e.objects.Del(b)
return nil
}
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)
}
}
func (e *Engine) addUpdater(b Updater) {
e.updaters.Append(b)
}
func (e *engine) Update() error {
var err error
@ -142,8 +124,12 @@ func (e *engine) Update() error {
AppendPressedKeys(e.keys[:0])
e.dt = time.Since(e.lastTime).Seconds()
for p := range eng.updaters.Range() {
err = p.V.Update(eng)
for object := range e.objects.KeyChan() {
updater, ok := object.(Updater)
if !ok {
continue
}
err = updater.Update(eng)
if err != nil {
return err
}
@ -155,13 +141,28 @@ func (e *engine) Update() error {
func (e *engine) Draw(i *ebiten.Image) {
eng := (*Engine)(e)
for p := range e.layers.Vals() {
for pj := range p.V.Range() {
visibler, ok := pj.V.(Visibler)
if ok && !visibler.IsVisible() {
layers := sparsex.New[Layer, []Drawer]()
for object := range eng.objects.KeyChan() {
drawer, ok := object.(Drawer)
if !ok {
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)
e.lastTime = time.Now()
fmt.Println(e.objects)
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
}
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 (
MaxColorV = math.MaxUint32
)
func (v Visibility) IsVisible() bool {
return v.Visible
}
func LoadImage(input io.Reader) (*Image, error) {
img, _, err := image.Decode(input)

View file

@ -6,7 +6,7 @@ package gx
// the OnUpdate.
// The v value will be get from Add function.
type Starter interface {
Start(*Engine, ...any)
Start(*Engine)
}
// Implementing the interface type
@ -23,3 +23,26 @@ type Deleter interface {
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