gg/engine.go

490 lines
9 KiB
Go
Raw Normal View History

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"
2024-05-28 11:24:12 +03:00
"surdeus.su/core/gods/maps"
2023-02-17 23:51:40 +03:00
"time"
2023-12-24 15:05:34 +03:00
"slices"
2023-02-17 07:04:29 +03:00
)
2024-05-28 11:24:12 +03:00
import "surdeus.su/core/gg/mx"
2024-01-08 12:25:18 +03:00
const (
MaxVertices = 1 << 16
2024-01-08 12:25:18 +03:00
)
2023-12-25 23:41:12 +03:00
type GraphicsLibrary = ebiten.GraphicsLibrary
type RunOptions = ebiten.RunGameOptions
2023-05-02 17:38:34 +03:00
// Window configuration type.
2023-02-17 07:04:29 +03:00
type WindowConfig struct {
2023-12-25 23:41:12 +03:00
DebugInfo ebiten.DebugInfo
Options *RunOptions
2023-12-23 00:09:07 +03:00
// The title of the window.
2023-02-17 07:04:29 +03:00
Title string
2023-05-26 18:31:04 +03:00
2023-12-23 00:09:07 +03:00
// Width and height of the window
// in pixels.
2023-05-26 18:31:04 +03:00
Width,
Height int
2023-12-23 00:09:07 +03:00
// Optional settings with
// self describing names.
2023-05-26 18:31:04 +03:00
FixedSize,
Fullscreen,
VSync bool
2023-02-17 07:04:29 +03:00
}
2024-05-28 11:24:12 +03:00
// The main structure that represents
// current state of [game] engine.
2023-02-17 07:04:29 +03:00
type Engine struct {
2024-05-28 11:24:12 +03:00
wcfg WindowConfig
2023-12-23 00:09:07 +03:00
// The main holder for objects.
// Uses the map structure to quickly
// delete and create new objects.
objects *Objects
2023-12-23 00:09:07 +03:00
// The main camera to display in window.
// If is set to nil then the engine will panic.
2024-06-01 16:07:28 +03:00
camera Camera
2023-12-23 00:09:07 +03:00
drawLastTime time.Time
drawdt Duration
// Frame delta time.
dt Duration
lastTime time.Time
2023-12-23 00:09:07 +03:00
// Temporary stuff
2023-11-23 22:05:22 +03:00
keys, prevKeys []Key
2023-12-24 15:05:34 +03:00
buttons MouseButtonMap
2024-05-28 11:24:12 +03:00
wheel mx.Vector
cursorPos mx.Vector
2023-11-23 22:05:22 +03:00
outerEvents, handleEvents EventChan
2024-01-08 12:25:18 +03:00
//bufs [LayerBufSize]*Image
vertices map[Layer] []ebiten.Vertex
//vindices []uint16
// Draw frame.
dframe uint
// Frame.
frame uint
runes []rune
2023-02-17 07:04:29 +03:00
}
type engine Engine
2023-05-02 17:38:34 +03:00
// Get currently pressed keys.
2024-06-01 16:07:28 +03:00
func (e *Engine) GetKeyboardKeys() []Key {
2023-02-18 07:46:33 +03:00
return e.keys
}
2023-12-25 23:41:12 +03:00
func (e *Engine) GraphicsLibrary() GraphicsLibrary {
return e.wcfg.DebugInfo.GraphicsLibrary
}
2023-12-24 15:05:34 +03:00
// Returns currently pressed buttons.
func (e *Engine) GetMouseButtons() []MouseButton {
2023-12-24 15:05:34 +03:00
ret := make([]MouseButton, len(e.buttons))
i := 0
for v := range e.buttons {
ret[i] = v
i++
}
slices.Sort(ret)
return ret
}
2023-05-02 17:38:34 +03:00
// Returns new empty Engine.
func NewEngine(
2024-05-28 11:24:12 +03:00
cfg WindowConfig,
2023-02-17 07:04:29 +03:00
) *Engine {
2023-12-23 00:09:07 +03:00
ret := &Engine{}
ret.wcfg = cfg
ret.outerEvents = make(EventChan)
ret.handleEvents = make(EventChan)
ret.objects = NewObjects()
2023-12-24 15:05:34 +03:00
ret.buttons = MouseButtonMap{}
2023-12-23 00:09:07 +03:00
return ret
}
2024-06-01 16:07:28 +03:00
func (e *Engine) Camera() Camera {
return e.camera
2023-12-23 00:09:07 +03:00
}
2024-06-01 16:07:28 +03:00
func (e *Engine) SetCamera(c Camera) *Engine {
e.camera = c
return e
}
2024-06-01 16:07:28 +03:00
2023-11-23 22:05:22 +03:00
func (e *Engine) EventInput() EventChan {
return e.outerEvents
}
func (e *Engine) Exist(object Object) bool {
return e.objects.has(object)
}
// Add new objects to the Engine's view.
func (e *Engine) Spawn(object Object) bool {
2023-02-17 23:51:40 +03:00
ctx := Context{
2024-06-01 16:07:28 +03:00
engine: e,
}
object.OnStart(ctx)
2024-06-02 22:27:11 +03:00
ok := e.objects.add(object)
return ok
2023-02-17 23:51:40 +03:00
}
2023-05-26 18:31:04 +03:00
// Delete object from Engine.
func (e *Engine) Delete(object Object) bool {
ctx := Context{
engine: e,
}
object.OnDelete(ctx)
2024-06-02 22:27:11 +03:00
ok := e.objects.remove(object)
return ok
2023-02-17 23:51:40 +03:00
}
2023-12-24 15:05:34 +03:00
var (
allButtons = []MouseButton{
MouseButton0,
MouseButton1,
MouseButton2,
MouseButton3,
MouseButton4,
}
)
func (e *Engine) IsPressed(k Key) bool {
2024-06-01 16:07:28 +03:00
keys := e.GetKeyboardKeys()
2023-12-24 15:05:34 +03:00
for _, v := range keys {
if v == k {
return true
}
}
return false
}
func (e *Engine) IsButtoned(b MouseButton) bool {
_, ok := e.buttons[b]
return ok
}
func (e *Engine) GetMouseWheel() mx.Vector {
2023-12-24 15:05:34 +03:00
return e.wheel
}
2024-05-28 11:24:12 +03:00
func (e *Engine) cursorPosition() mx.Vector {
2023-12-24 15:05:34 +03:00
x, y := ebiten.CursorPosition()
2024-05-28 11:24:12 +03:00
return mx.Vector{mx.Float(x), mx.Float(y)}
2023-12-24 15:05:34 +03:00
}
2024-06-01 16:07:28 +03:00
// Get the real cursor position.
func (e *Engine) GetRealCursorPosition() mx.Vector {
2023-12-24 15:05:34 +03:00
return e.cursorPos
}
2024-06-01 16:07:28 +03:00
// Get the absolute cursor position in the world
// of the engine.
func (e *Engine) GetAbsCursorPosition(
) mx.Vector {
return e.GetRealCursorPosition().
Apply(e.camera.GetAbsMatrice(Context{
engine: e,
}))
}
// Get the real window size in the current context.
func (e *Engine) GetRealWinSize() mx.Vector {
var w, h int
if e.wcfg.Fullscreen {
w, h = ebiten.ScreenSizeInFullscreen()
} else {
w, h = e.wcfg.Width, e.wcfg.Height
}
return mx.Vector{
mx.Float(w),
mx.Float(h),
}
2023-12-24 15:05:34 +03:00
}
2023-02-17 07:04:29 +03:00
2024-06-01 16:07:28 +03:00
func (e *Engine) GetAbsWinSize() mx.Vector {
return e.camera.GetAbsWinSize(Context{
engine: e,
})
}
func (e *Engine) Runes() []rune {
return e.runes
}
2024-06-01 16:07:28 +03:00
func (e *engine) updateEvents() Events {
eng := (*Engine)(e)
2023-11-23 22:05:22 +03:00
e.prevKeys = e.keys
2023-02-18 07:46:33 +03:00
e.keys = inpututil.
AppendPressedKeys(e.keys[:0])
2024-06-01 16:07:28 +03:00
events := Events{}
// Mouse buttons.
2023-12-24 15:05:34 +03:00
btns := e.buttons
for _, btn := range allButtons {
if inpututil.IsMouseButtonJustPressed(btn) {
btns[btn] = struct{}{}
2024-06-01 16:07:28 +03:00
events.Mouse.ButtonDowns = append(
events.Mouse.ButtonDowns,
MouseButtonDown{
MouseButton: btn,
},
)
2023-12-24 15:05:34 +03:00
} else if inpututil.IsMouseButtonJustReleased(btn) {
delete(btns, btn)
2024-06-01 16:07:28 +03:00
events.Mouse.ButtonUps = append(
events.Mouse.ButtonUps,
MouseButtonUp{
MouseButton: btn,
},
)
2023-12-24 15:05:34 +03:00
}
}
2024-06-01 16:07:28 +03:00
// Mouse wheel.
2023-12-24 15:05:34 +03:00
x, y := ebiten.Wheel()
2024-05-28 11:24:12 +03:00
e.wheel = mx.Vector{x, y}
if !(e.wheel.Eq(mx.ZV)) {
2024-06-01 16:07:28 +03:00
events.Mouse.WheelChange = &WheelChange{
Offset: e.wheel,
2023-11-23 22:05:22 +03:00
}
}
2024-06-01 16:07:28 +03:00
// Cursor position.
2023-12-24 15:05:34 +03:00
realPos := eng.cursorPosition()
if !realPos.Eq(e.cursorPos) {
2024-06-01 16:07:28 +03:00
absM := eng.camera.GetAbsMatrice(Context{
engine: eng,
})
2023-12-24 15:05:34 +03:00
absPrevPos := e.cursorPos.Apply(absM)
absPos := realPos.Apply(absM)
2023-12-24 15:05:34 +03:00
2024-06-01 16:07:28 +03:00
events.Mouse.Move = &MouseMove{
2024-05-28 11:24:12 +03:00
RealDelta: realPos.Sub(e.cursorPos),
AbsDelta: absPos.Sub(absPrevPos),
2024-06-01 16:07:28 +03:00
}
e.cursorPos = realPos
2023-12-24 15:05:34 +03:00
}
2024-06-01 16:07:28 +03:00
// Keyboard.
keyDiff := diffEm(e.prevKeys, e.keys)
for _, key := range keyDiff {
if eng.IsPressed(key) {
events.Keyboard.KeyDowns = append(
events.Keyboard.KeyDowns,
KeyDown{
Key: key,
},
)
} else {
events.Keyboard.KeyUps = append(
events.Keyboard.KeyUps,
KeyUp{
Key: key,
},
)
}
}
return events
}
func (e *engine) Update() error {
eng := (*Engine)(e)
e.dt = time.Since(e.lastTime)
e.runes = ebiten.AppendInputChars(e.runes[:0])
//fmt.Println("runes:", e.runes)
// Buffering the context for faster.
// Providing the events to the objects.
// Maybe should think of the better way,
// but for it is simple enough.
2024-05-28 11:24:12 +03:00
events := e.updateEvents()
2024-06-02 23:53:43 +03:00
2024-05-28 11:24:12 +03:00
c := Context{
2024-06-01 16:07:28 +03:00
engine: eng,
events: events,
2024-01-08 07:12:35 +03:00
}
2024-06-02 23:53:43 +03:00
// Should think of the order?
e.objects.updateTags(c)
e.objects.updateObjects(c)
e.lastTime = time.Now()
e.frame++
2023-02-17 07:04:29 +03:00
return nil
}
2024-06-02 23:53:43 +03:00
func (e *Engine) Objects() *Objects {
return e.objects
}
var (
fullPageIndexes = func() [MaxVertices]uint16 {
ret := [MaxVertices]uint16{}
for i:=0 ; i<len(ret) ; i++ {
ret[i] = uint16(i)
}
return ret
}()
defaultPageImg = func() *Image {
img := NewImage(1, 1)
2024-05-28 11:24:12 +03:00
img.Set(0, 0, RGBA(1, 1, 1, 1))
return img
}()
defaultTriOptions = &ebiten.DrawTrianglesOptions{}
)
func (e *engine) Draw(img *ebiten.Image) {
e.drawdt = time.Since(e.drawLastTime)
2023-02-17 23:51:40 +03:00
eng := (*Engine)(e)
2023-11-12 13:29:38 +03:00
m := map[Layer][]Drawer{}
for _, object := range eng.objects.store {
// Skipping the ones we do not need to draw.
2024-06-02 22:27:11 +03:00
if object == nil || !object.IsVisible() {
continue
}
l := object.GetLayer()
2023-11-12 13:29:38 +03:00
layer, ok := m[l]
// Create new if has no the layer
if !ok {
m[l] = []Drawer{object}
continue
}
m[l] = append(layer, object)
}
// Drawing layers via the sparse array.
// First drawing via the inside function
// and then the returned []EVertex.
2023-11-12 13:29:38 +03:00
layers := maps.NewSparse[Layer, []Drawer](nil, m)
2024-06-01 16:07:28 +03:00
c := Context{engine: eng, image: img}
for layer := range layers.Chan() {
2024-05-28 11:24:12 +03:00
2024-06-01 16:07:28 +03:00
vertices := Vertices{}
for _, drawer := range layer {
2024-05-28 11:24:12 +03:00
drawing := drawer.Draw(c)
2024-06-01 16:07:28 +03:00
if drawing != nil {
2024-05-28 11:24:12 +03:00
vertices = append(
vertices,
2024-06-01 16:07:28 +03:00
drawing.Vertices...,
2024-05-28 11:24:12 +03:00
)
}
2023-02-17 23:51:40 +03:00
}
2024-05-28 11:24:12 +03:00
pn := len(vertices) / MaxVertices
mod := len(vertices) % MaxVertices
for i := 0 ; i<pn ; i++ {
cur := i*MaxVertices
img.DrawTriangles(
2024-06-01 16:07:28 +03:00
vertices[cur:cur+MaxVertices].ToAPI(),
fullPageIndexes[:],
defaultPageImg,
defaultTriOptions,
)
}
2024-05-28 11:24:12 +03:00
st := pn*MaxVertices
img.DrawTriangles(
2024-06-01 16:07:28 +03:00
vertices[st:].ToAPI(),
fullPageIndexes[:mod],
defaultPageImg,
defaultTriOptions,
)
2023-02-17 23:51:40 +03:00
}
2023-12-23 00:09:07 +03:00
// Empty the buff to generate it again.
e.drawLastTime = time.Now()
e.dframe++
2023-02-17 07:04:29 +03:00
}
func (e *engine) Layout(ow, oh int) (int, int) {
if e.wcfg.FixedSize {
return e.wcfg.Width, e.wcfg.Height
}
return ow, oh
2023-02-17 07:04:29 +03:00
}
2024-05-28 11:24:12 +03:00
// Return the delta time between Draw calls.
func (e *Engine) DrawDT() Duration {
return e.drawdt
2023-02-17 23:51:40 +03:00
}
2024-05-28 11:24:12 +03:00
// Current Drawing frame.
func (e *Engine) Dframe() uint {
return e.dframe
}
2024-05-28 11:24:12 +03:00
// Return the real delta time.
// Please, prefer the DT().
func (e *Engine) RealDT() Duration {
return e.dt
}
2024-05-28 11:24:12 +03:00
// Returns the current delta time. (btw frames)
// (By the Ebiten community convention
// currently it is a fixed value)
func (e *Engine) DT() Duration {
return time.Second/60
}
2024-05-28 11:24:12 +03:00
// Current frame
func (e *Engine) Frame() uint {
return e.frame
}
2024-05-28 11:24:12 +03:00
// Current FPS.
func (e *Engine) FPS() float64 {
return ebiten.ActualFPS()
}
2024-05-28 11:24:12 +03:00
// Current TPS.
func (e *Engine) TPS() float64 {
return ebiten.ActualTPS()
}
2023-02-17 23:51:40 +03:00
2024-05-28 11:24:12 +03:00
// Run the engine.
2023-02-17 07:04:29 +03:00
func (e *Engine) Run() error {
2023-12-25 23:41:12 +03:00
ebiten.ReadDebugInfo(&e.wcfg.DebugInfo)
2023-02-17 07:04:29 +03:00
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)
ebiten.SetFullscreen(e.wcfg.Fullscreen)
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-12-23 00:09:07 +03:00
//fmt.Println(e.Objects)
2023-12-25 23:41:12 +03:00
return ebiten.RunGameWithOptions((*engine)(e), e.wcfg.Options)
2023-02-17 07:04:29 +03:00
}
2024-06-01 16:07:28 +03:00
func (e *Engine) GetWindowConfig() WindowConfig {
return e.wcfg
}