Implemented dynamic layering. (should think of optimizations because it requires to sort objects every frame.
This commit is contained in:
parent
f081e9e6b4
commit
560bb93644
8 changed files with 138 additions and 108 deletions
1
go.mod
1
go.mod
|
@ -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
2
go.sum
|
@ -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=
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
|
|
|
@ -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
|
||||
*/
|
||||
|
||||
|
|
126
src/gx/engine.go
126
src/gx/engine.go
|
@ -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)
|
||||
}
|
||||
|
||||
drawer, ok := b.(Drawer)
|
||||
if ok {
|
||||
e.addDrawer(l, drawer)
|
||||
}
|
||||
e.objects.Set(object, struct{}{})
|
||||
|
||||
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...)
|
||||
}
|
||||
|
||||
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)
|
||||
deleter.Delete(e)
|
||||
}
|
||||
|
||||
e.objects.Del(b)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
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() {
|
||||
continue
|
||||
}
|
||||
pj.V.Draw(eng, i)
|
||||
layers := sparsex.New[Layer, []Drawer]()
|
||||
for object := range eng.objects.KeyChan() {
|
||||
drawer, ok := object.(Drawer)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
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
12
src/gx/errors.go
Normal 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")
|
||||
)
|
||||
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
Loading…
Reference in a new issue