2023-10-23 15:45:18 +03:00
|
|
|
package gg
|
2023-02-17 07:04:29 +03:00
|
|
|
|
|
|
|
import (
|
|
|
|
"github.com/hajimehoshi/ebiten/v2"
|
2023-02-18 07:46:33 +03:00
|
|
|
"github.com/hajimehoshi/ebiten/v2/inpututil"
|
2023-08-31 11:29:37 +03:00
|
|
|
"github.com/mojosa-software/godat/sparsex"
|
|
|
|
"github.com/mojosa-software/godat/mapx"
|
2023-05-30 22:35:49 +03:00
|
|
|
"fmt"
|
2023-02-17 23:51:40 +03:00
|
|
|
"time"
|
2023-02-17 07:04:29 +03:00
|
|
|
)
|
|
|
|
|
2023-02-17 16:40:46 +03:00
|
|
|
// The type represents order of drawing.
|
2023-08-31 11:29:37 +03:00
|
|
|
// Higher values are drawn later.
|
|
|
|
type Layer float64
|
|
|
|
|
|
|
|
func (l Layer) GetLayer() Layer {
|
|
|
|
return l
|
|
|
|
}
|
2023-02-17 16:40:46 +03:00
|
|
|
|
2023-05-02 17:38:34 +03:00
|
|
|
// Window configuration type.
|
2023-02-17 07:04:29 +03:00
|
|
|
type WindowConfig struct {
|
|
|
|
Title string
|
2023-05-26 18:31:04 +03:00
|
|
|
|
|
|
|
Width,
|
|
|
|
Height int
|
|
|
|
|
|
|
|
FixedSize,
|
|
|
|
Fullscreen,
|
2023-05-04 19:31:33 +03:00
|
|
|
VSync bool
|
2023-02-17 07:04:29 +03:00
|
|
|
}
|
|
|
|
|
2023-05-02 17:38:34 +03:00
|
|
|
// The main structure that represents current state of [game] engine.
|
2023-02-17 07:04:29 +03:00
|
|
|
type Engine struct {
|
|
|
|
wcfg *WindowConfig
|
2023-08-31 11:29:37 +03:00
|
|
|
objects *mapx.OrderedMap[Object, struct{}]
|
2023-02-17 23:51:40 +03:00
|
|
|
lastTime time.Time
|
|
|
|
dt Float
|
2023-02-18 03:51:43 +03:00
|
|
|
camera *Camera
|
2023-02-18 07:46:33 +03:00
|
|
|
keys []Key
|
2023-02-17 07:04:29 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
type engine Engine
|
|
|
|
|
2023-05-02 17:38:34 +03:00
|
|
|
// Return current camera.
|
2023-02-18 03:51:43 +03:00
|
|
|
func (e *Engine) Camera() *Camera {
|
|
|
|
return e.camera
|
|
|
|
}
|
|
|
|
|
2023-05-02 17:38:34 +03:00
|
|
|
// Set new current camera.
|
2023-02-18 15:38:28 +03:00
|
|
|
func (e *Engine) SetCamera(c *Camera) {
|
|
|
|
e.camera = c
|
|
|
|
}
|
|
|
|
|
2023-05-02 17:38:34 +03:00
|
|
|
// Get currently pressed keys.
|
2023-02-18 07:46:33 +03:00
|
|
|
func (e *Engine) Keys() []Key {
|
|
|
|
return e.keys
|
|
|
|
}
|
|
|
|
|
2023-05-02 17:38:34 +03:00
|
|
|
// Returns new empty Engine.
|
2023-06-03 10:39:15 +03:00
|
|
|
func NewEngine(
|
2023-02-17 07:04:29 +03:00
|
|
|
cfg *WindowConfig,
|
|
|
|
) *Engine {
|
2023-05-30 22:35:49 +03:00
|
|
|
w := Float(cfg.Width)
|
|
|
|
h := Float(cfg.Height)
|
2023-02-17 07:04:29 +03:00
|
|
|
return &Engine{
|
|
|
|
wcfg: cfg,
|
2023-02-18 03:51:43 +03:00
|
|
|
camera: &Camera{
|
2023-05-30 13:24:14 +03:00
|
|
|
Transform: Transform{
|
2023-08-31 11:29:37 +03:00
|
|
|
// Normal, no distortion.
|
2023-02-18 07:46:33 +03:00
|
|
|
S: Vector{1, 1},
|
2023-08-31 11:29:37 +03:00
|
|
|
// Center.
|
2023-05-30 22:35:49 +03:00
|
|
|
RA: V(w/2, h/2),
|
2023-02-18 03:51:43 +03:00
|
|
|
},
|
|
|
|
},
|
2023-08-31 11:29:37 +03:00
|
|
|
objects: mapx.NewOrdered[Object, struct{}](),
|
2023-02-17 16:40:46 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-02-17 23:51:40 +03:00
|
|
|
// Add new object considering what
|
|
|
|
// interfaces it implements.
|
2023-08-31 11:29:37 +03:00
|
|
|
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
|
|
|
|
}*/
|
|
|
|
|
2023-05-26 18:31:04 +03:00
|
|
|
starter, ok := b.(Starter)
|
|
|
|
if ok {
|
|
|
|
starter.Start(e)
|
|
|
|
}
|
2023-02-17 23:51:40 +03:00
|
|
|
|
2023-08-31 11:29:37 +03:00
|
|
|
e.objects.Set(object, struct{}{})
|
|
|
|
|
|
|
|
return nil
|
2023-02-17 23:51:40 +03:00
|
|
|
}
|
|
|
|
|
2023-05-26 18:31:04 +03:00
|
|
|
// Delete object from Engine.
|
2023-08-31 11:29:37 +03:00
|
|
|
func (e *Engine) Del(b any) error {
|
|
|
|
object, _ := b.(Object)
|
|
|
|
if !e.objects.Has(object) {
|
|
|
|
return ObjectNotExistErr
|
2023-04-28 18:21:34 +03:00
|
|
|
}
|
2023-08-31 11:29:37 +03:00
|
|
|
|
|
|
|
deleter, ok := b.(Deleter)
|
2023-04-28 18:21:34 +03:00
|
|
|
if ok {
|
2023-08-31 11:29:37 +03:00
|
|
|
deleter.Delete(e)
|
2023-04-28 18:21:34 +03:00
|
|
|
}
|
|
|
|
|
2023-08-31 11:29:37 +03:00
|
|
|
e.objects.Del(b)
|
2023-02-17 16:40:46 +03:00
|
|
|
|
2023-08-31 11:29:37 +03:00
|
|
|
return nil
|
2023-02-17 23:51:40 +03:00
|
|
|
}
|
|
|
|
|
2023-02-17 07:04:29 +03:00
|
|
|
|
|
|
|
func (e *engine) Update() error {
|
2023-02-18 09:11:09 +03:00
|
|
|
var err error
|
2023-02-17 16:40:46 +03:00
|
|
|
eng := (*Engine)(e)
|
2023-02-17 23:51:40 +03:00
|
|
|
|
2023-02-18 07:46:33 +03:00
|
|
|
e.keys = inpututil.
|
|
|
|
AppendPressedKeys(e.keys[:0])
|
|
|
|
|
2023-02-18 02:03:28 +03:00
|
|
|
e.dt = time.Since(e.lastTime).Seconds()
|
2023-08-31 11:29:37 +03:00
|
|
|
for object := range e.objects.KeyChan() {
|
|
|
|
updater, ok := object.(Updater)
|
|
|
|
if !ok {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
err = updater.Update(eng)
|
2023-02-18 09:11:09 +03:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2023-02-17 16:40:46 +03:00
|
|
|
}
|
2023-02-18 02:03:28 +03:00
|
|
|
e.lastTime = time.Now()
|
2023-02-17 16:40:46 +03:00
|
|
|
|
2023-02-17 07:04:29 +03:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2023-02-17 23:51:40 +03:00
|
|
|
func (e *engine) Draw(i *ebiten.Image) {
|
|
|
|
eng := (*Engine)(e)
|
2023-08-31 11:29:37 +03:00
|
|
|
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)
|
2023-02-17 23:51:40 +03:00
|
|
|
}
|
|
|
|
}
|
2023-02-17 07:04:29 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
func (e *engine) Layout(ow, oh int) (int, int) {
|
2023-02-18 09:11:09 +03:00
|
|
|
if e.wcfg.FixedSize {
|
|
|
|
return e.wcfg.Width, e.wcfg.Height
|
|
|
|
}
|
2023-02-18 15:38:28 +03:00
|
|
|
|
2023-02-18 09:11:09 +03:00
|
|
|
return ow, oh
|
2023-02-17 07:04:29 +03:00
|
|
|
}
|
|
|
|
|
2023-02-17 23:51:40 +03:00
|
|
|
// Return the delta time duration value.
|
|
|
|
func (e *Engine) DT() Float {
|
|
|
|
return e.dt
|
|
|
|
}
|
|
|
|
|
2023-02-17 07:04:29 +03:00
|
|
|
func (e *Engine) Run() error {
|
|
|
|
ebiten.SetWindowTitle(e.wcfg.Title)
|
|
|
|
ebiten.SetWindowSize(e.wcfg.Width, e.wcfg.Height)
|
2023-02-18 19:35:38 +03:00
|
|
|
ebiten.SetWindowSizeLimits(1, 1, e.wcfg.Width, e.wcfg.Height)
|
2023-05-04 19:31:33 +03:00
|
|
|
|
|
|
|
ebiten.SetVsyncEnabled(e.wcfg.VSync)
|
2023-02-17 07:04:29 +03:00
|
|
|
|
2023-02-18 00:17:51 +03:00
|
|
|
e.lastTime = time.Now()
|
2023-08-31 11:29:37 +03:00
|
|
|
fmt.Println(e.objects)
|
2023-02-17 07:04:29 +03:00
|
|
|
return ebiten.RunGame((*engine)(e))
|
|
|
|
}
|
|
|
|
|