diff --git a/camera.go b/camera.go index 18d2c80..ed17a44 100644 --- a/camera.go +++ b/camera.go @@ -3,8 +3,19 @@ package gg // Implements the camera component // for the main window. type Camera struct { + // The shaders that will be applied to everything + // that the camera shows. + ShaderOptions Transform buf *Matrix + engine *Engine +} + +func (e *Engine) NewCamera() *Camera { + ret := &Camera{} + ret.Transform = T() + ret.engine = e + return ret } // Returns the matrix satysfying camera @@ -14,11 +25,16 @@ type Camera struct { // (Should implement buffering it so we do not // need to calculate it each time for each object. ) func (c *Camera)RealMatrix() Matrix { + /*if c.buf != nil { + return *(c.buf) + }*/ g := &Matrix{} - g.Translate(-c.P.X, -c.P.Y) - g.Rotate(c.R) - g.Scale(c.S.X, c.S.Y) - g.Translate(c.RA.X, c.RA.Y) + g.Translate(-c.Position.X, -c.Position.Y) + g.Rotate(c.Rotation) + siz := c.engine.AbsWinSize() + g.Translate(c.Around.X * siz.X, c.Around.Y * siz.Y) + g.Scale(c.Scale.X, c.Scale.Y) + c.buf = g diff --git a/cmd/test/debug.go b/cmd/test/debug.go new file mode 100644 index 0000000..ab4e27e --- /dev/null +++ b/cmd/test/debug.go @@ -0,0 +1,40 @@ +package main + +import "github.com/di4f/gg" + +import ( + "strings" + "fmt" +) + +type Debug struct { + gg.Visibility + gg.Layer +} + +func (d *Debug) Draw(c *Context) { + e := c.Engine + keyStrs := []string{} + for _, k := range e.Keys() { + keyStrs = append(keyStrs, k.String()) + } + + if rectMove.ContainsPoint(e.AbsCursorPosition()) { + keyStrs = append(keyStrs, "contains cursor") + } + + if rectMove.Vertices().Contained(rect).Len() > 0 || + rect.Vertices().Contained(rectMove).Len() > 0 { + keyStrs = append(keyStrs, "rectangles intersect") + } + + keyStrs = append(keyStrs, fmt.Sprintf("%v", e.CursorPosition())) + keyStrs = append(keyStrs, fmt.Sprintf("%v", e.AbsCursorPosition())) + keyStrs = append(keyStrs, fmt.Sprintf("absWinSize: %v", c.AbsWinSize())) + + e.DebugPrint(c.Image, + strings.Join(keyStrs, "\n")) + +} + +func (d *Debug) IsVisible() bool { return true } diff --git a/cmd/test/main.go b/cmd/test/main.go index d155262..7eb5413 100644 --- a/cmd/test/main.go +++ b/cmd/test/main.go @@ -3,12 +3,10 @@ package main import ( "github.com/di4f/gg" "github.com/hajimehoshi/ebiten/v2/examples/resources/images" - "github.com/hajimehoshi/ebiten/v2" "bytes" "log" - "strings" + //"strings" "fmt" - "math/rand" ) type Context = gg.Context @@ -22,94 +20,6 @@ const ( LowestL ) -type Player struct { - *gg.Sprite - MoveSpeed gg.Float - ScaleSpeed gg.Float - gg.Layer -} - -type Debug struct { - gg.Visibility - gg.Layer -} - -type Rect struct { - *gg.DrawableRectangle - gg.Layer -} - -type Tri struct { - *gg.DrawablePolygon - gg.Layer -} - -func NewTri() *Tri { - ret := &Tri{} - ret.DrawablePolygon = &gg.DrawablePolygon{} - ret.Transform.S = gg.V(1, 1) - - ret.Triangles = gg.Triangles{ - gg.Triangle{ - gg.V(0, 0), - gg.V(100, 100), - gg.V(0, -50), - }, - gg.Triangle{ - gg.V(0, 0), - gg.V(-100, -100), - gg.V(0, 50), - }, - } - ret.Color = gg.Color{gg.MaxColorV, gg.MaxColorV, 0, gg.MaxColorV} - ret.Visible = true - ret.Layer = TriangleL - - return ret -} - -func NewRect() *Rect { - ret := &Rect{&gg.DrawableRectangle{ - Rectangle: gg.Rectangle{ - Transform: gg.Transform{ - S: gg.Vector{ - X: 200, - Y: 400, - }, - }, - }, - Color: gg.Color{ - gg.MaxColorV, - 0, - 0, - gg.MaxColorV, - }, - Visible: true, - /*Shader: gg.SolidWhiteColorShader, - Options: gg.ShaderOptions{ - Images: [4]*gg.Image{ - playerImg, - nil, - nil, - nil, - }, - },*/ - }, - RectL, - } - - return ret -} - -func (r *Rect) Update(c *Context) error { - //r.R += 0.3 * e.DT() - r.P = c.AbsCursorPosition() - return nil -} - -func (r *Rect) Event(c *Context) { -} - var ( playerImg *gg.Image player *Player @@ -118,170 +28,6 @@ var ( tri *Tri ) -func NewPlayer() *Player { - ret := &Player{ - Sprite: &gg.Sprite{ - Transform: gg.Transform{ - S: gg.Vector{5, 5}, - RA: gg.Vector{.5, .5}, - }, - Visible: true, - ShaderOptions: gg.ShaderOptions{ - Shader: gg.SolidWhiteColorShader, - Uniforms: make(map[string]any), - }, - }, - MoveSpeed: 90., - ScaleSpeed: .2, - } - - ret.Images[0] = playerImg - ret.Layer = PlayerL - - return ret -} - -func (p *Player) Draw(c *Context) { - p.Sprite.Draw(c) - t := p.Transform - t.S.X *= 4. - t.S.Y *= 4. - rectMove = gg.Rectangle{ - Transform: t, - } - r := &gg.DrawableRectangle{ - Rectangle: rectMove, - Color: gg.Color{0, 0, gg.MaxColorV, gg.MaxColorV}, - } - r.Draw(c) -} - -func (p *Player) Start(c *Context) { - fmt.Println("starting") - cam := c.Camera() - cam.RA = gg.V(360, 240) -} - -func (p *Player) Update(c *Context) error { - dt := c.DT() - cam := c.Camera() - keys := c.Keys() - - p.Uniforms["Random"] = any(rand.Float32()) - for _, v := range keys { - switch v { - case ebiten.KeyArrowUp: - cam.P.Y += p.MoveSpeed * dt - case ebiten.KeyArrowLeft: - cam.P.X -= p.MoveSpeed * dt - case ebiten.KeyArrowDown: - cam.P.Y -= p.MoveSpeed * dt - case ebiten.KeyArrowRight: - cam.P.X += p.MoveSpeed * dt - case ebiten.KeyW: - p.P.Y += p.MoveSpeed * dt - case ebiten.KeyA: - p.P.X -= p.MoveSpeed * dt - case ebiten.KeyS: - p.P.Y -= p.MoveSpeed * dt - case ebiten.KeyD: - p.P.X += p.MoveSpeed * dt - case ebiten.KeyR: - cam.R += gg.Pi * p.ScaleSpeed * dt - case ebiten.KeyT: - cam.R -= gg.Pi * p.ScaleSpeed * dt - case ebiten.KeyRightBracket: - if c.IsPressed(gg.KeyShift) { - p.R -= gg.Pi * 0.3 * dt - } else { - p.R += gg.Pi * 0.3 * dt - } - case gg.KeyF: - if c.IsPressed(ebiten.KeyShift) { - cam.S.X -= gg.Pi * p.ScaleSpeed * dt - } else { - cam.S.X += gg.Pi * p.ScaleSpeed * dt - } - case gg.KeyG: - if c.IsPressed(ebiten.KeyShift) { - cam.S.Y -= gg.Pi * p.ScaleSpeed * dt - } else { - cam.S.Y += gg.Pi * p.ScaleSpeed * dt - } - case gg.KeyZ: - if c.IsPressed(ebiten.KeyShift) { - cam.RA.X -= gg.Pi * p.MoveSpeed * dt - } else { - cam.RA.X += gg.Pi * p.MoveSpeed * dt - } - case gg.KeyX: - if c.IsPressed(ebiten.KeyShift) { - cam.RA.Y -= gg.Pi * p.MoveSpeed * dt - } else { - cam.RA.Y += gg.Pi * p.MoveSpeed * dt - } - case gg.KeyV: - if c.IsPressed(ebiten.KeyShift) { - tri.R -= gg.Pi * 0.3 * dt - } else { - tri.R += gg.Pi * 0.3 * dt - } - case gg.KeyLeftBracket: - if c.IsPressed(ebiten.KeyShift) { - rect.R -= gg.Pi * 0.3 * dt - } else { - rect.R += gg.Pi * 0.3 * dt - } - case gg.Key0: - c.Del(p) - case gg.KeyB: - } - } - - return nil -} - -func (p *Player) Signal(e *gg.Engine, ev any) { - fmt.Println("event:", ev) - switch ec := ev.(type) { - case *gg.KeyDown: - switch { - case ec.Key == gg.KeyB: - if p.Layer != PlayerL { - p.Layer = PlayerL - } else { - p.Layer = HighestL - } - } - } -} - -func (d *Debug) Draw(c *Context) { - e := c.Engine - keyStrs := []string{} - for _, k := range e.Keys() { - keyStrs = append(keyStrs, k.String()) - } - - if rectMove.ContainsPoint(e.AbsCursorPosition()) { - keyStrs = append(keyStrs, "contains cursor") - } - - if rectMove.Vertices().Contained(rect).Len() > 0 || - rect.Vertices().Contained(rectMove).Len() > 0 { - keyStrs = append(keyStrs, "THIS IS SHIT") - } - - keyStrs = append(keyStrs, fmt.Sprintf("%v", e.CursorPosition())) - keyStrs = append(keyStrs, fmt.Sprintf("%v", e.AbsCursorPosition())) - - e.DebugPrint(c.Image, - strings.Join(keyStrs, ", ")) - -} - -func (d *Debug) IsVisible() bool { return true } - func main() { e := gg.NewEngine(&gg.WindowConfig{ Title: "Test title", diff --git a/cmd/test/player.go b/cmd/test/player.go new file mode 100644 index 0000000..182d430 --- /dev/null +++ b/cmd/test/player.go @@ -0,0 +1,129 @@ +package main + +import ( + //"math/rand" + "fmt" +) + +import "github.com/di4f/gg" + +type Player struct { + gg.Sprite + MoveSpeed gg.Float + ScaleSpeed gg.Float + gg.Layer +} + +func NewPlayer() *Player { + ret := &Player{} + ret.Transform = gg.T() + ret.Scale = gg.V(5, 5) + // Around center. + ret.Around = gg.V(.5, .5) + ret.MoveSpeed = 90. + ret.ScaleSpeed = .2 + + ret.Visible = true + + ret.Images[0] = playerImg + ret.Layer = PlayerL + + return ret +} + +// Custom drawing function. +func (p *Player) Draw(c *Context) { + p.Sprite.Draw(c) + t := p.Transform + t.Scale.X *= 4. + t.Scale.Y *= 4. + + r := &gg.DrawableRectangle{} + r.Color = gg.Rgba(0, 0, 1, 1) + r.Rectangle = gg.Rectangle{ + Transform: t, + } + r.Draw(c) +} + +func (p *Player) Update(c *Context) { + dt := c.DT() + cam := c.Camera + keys := c.Keys() + + //p.Uniforms["Random"] = any(rand.Float32()) + for _, v := range keys { + switch v { + case gg.KeyArrowUp: + cam.Position.Y += p.MoveSpeed * dt + case gg.KeyArrowLeft: + cam.Position.X -= p.MoveSpeed * dt + case gg.KeyArrowDown: + cam.Position.Y -= p.MoveSpeed * dt + case gg.KeyArrowRight: + cam.Position.X += p.MoveSpeed * dt + case gg.KeyW: + p.Position.Y += p.MoveSpeed * dt + case gg.KeyA: + p.Position.X -= p.MoveSpeed * dt + case gg.KeyS: + p.Position.Y -= p.MoveSpeed * dt + case gg.KeyD: + p.Position.X += p.MoveSpeed * dt + case gg.KeyR: + cam.Rotation += gg.Pi * p.ScaleSpeed * dt + case gg.KeyT: + cam.Rotation -= gg.Pi * p.ScaleSpeed * dt + case gg.KeyRightBracket: + if c.IsPressed(gg.KeyShift) { + p.Rotation -= gg.Pi * 0.3 * dt + } else { + p.Rotation += gg.Pi * 0.3 * dt + } + case gg.KeyF: + if c.IsPressed(gg.KeyShift) { + cam.Scale = cam.Scale.Add(gg.V1(p.ScaleSpeed * dt)) + } else { + cam.Scale = cam.Scale.Add(gg.V1(-p.ScaleSpeed * dt)) + } + case gg.KeyG: + if c.IsPressed(gg.KeyShift) { + cam.Scale.Y -= gg.Pi * p.ScaleSpeed * dt + } else { + cam.Scale.Y += gg.Pi * p.ScaleSpeed * dt + } + case gg.KeyV: + if c.IsPressed(gg.KeyShift) { + tri.Rotation -= gg.Pi * 0.3 * dt + } else { + tri.Rotation += gg.Pi * 0.3 * dt + } + case gg.KeyLeftBracket: + if c.IsPressed(gg.KeyShift) { + rect.Rotation -= gg.Pi * 0.3 * dt + } else { + rect.Rotation += gg.Pi * 0.3 * dt + } + case gg.Key0: + c.Del(p) + case gg.KeyB: + } + } + +} + +func (p *Player) Event(c *gg.Context) { + fmt.Println("event:", c.Event) + switch ec := c.Event.(type) { + case *gg.KeyDown: + switch { + case ec.Key == gg.KeyB: + if p.Layer != PlayerL { + p.Layer = PlayerL + } else { + p.Layer = HighestL + } + } + } +} + diff --git a/cmd/test/rect.go b/cmd/test/rect.go new file mode 100644 index 0000000..fad9a5d --- /dev/null +++ b/cmd/test/rect.go @@ -0,0 +1,30 @@ +package main + +import "github.com/di4f/gg" + +type Rect struct { + gg.DrawableRectangle + gg.Layer +} + +func NewRect() *Rect { + ret := &Rect{} + ret.Scale = gg.V(200, 400) + ret.Color = gg.Color{ + gg.MaxColorV, + 0, + 0, + gg.MaxColorV, + } + ret.Layer = RectL + + return ret +} + +func (r *Rect) Update(c *Context) { + //r.R += 0.3 * e.DT() + //r.Position = c.AbsCursorPosition() +} + +func (r *Rect) Event(c *Context) { +} diff --git a/cmd/test/trianlge.go b/cmd/test/trianlge.go new file mode 100644 index 0000000..acdd065 --- /dev/null +++ b/cmd/test/trianlge.go @@ -0,0 +1,41 @@ +package main + +import "github.com/di4f/gg" +//import "fmt" + +type Tri struct { + *gg.DrawablePolygon + gg.Layer +} + +func NewTri() *Tri { + ret := &Tri{} + ret.DrawablePolygon = &gg.DrawablePolygon{} + ret.Transform.Scale = gg.V(1, 1) + + ret.Triangles = gg.Triangles{ + gg.Triangle{ + gg.V(0, 0), + gg.V(100, 100), + gg.V(0, -50), + }, + gg.Triangle{ + gg.V(0, 0), + gg.V(-100, -100), + gg.V(0, 50), + }, + } + ret.Color = gg.Color{gg.MaxColorV, gg.MaxColorV, 0, gg.MaxColorV} + ret.Visible = true + ret.Layer = TriangleL + + return ret +} + +func (t *Tri) Update(c *Context) { + if t.ContainsPoint(c.AbsCursorPosition()) { + t.Color = gg.Rgba(0, 1, 0, 1) + } else { + t.Color = gg.Rgba(1, 0, 1, 1) + } +} diff --git a/engine.go b/engine.go index 3bfdf78..c4d7349 100644 --- a/engine.go +++ b/engine.go @@ -3,8 +3,8 @@ package gg import ( "github.com/hajimehoshi/ebiten/v2" "github.com/hajimehoshi/ebiten/v2/inpututil" - "github.com/omnipunk/gods/maps" - "fmt" + "github.com/di4f/gods/maps" + //"fmt" "time" ) @@ -18,11 +18,16 @@ func (l Layer) GetLayer() Layer { // Window configuration type. type WindowConfig struct { + // The title of the window. Title string + // Width and height of the window + // in pixels. Width, Height int + // Optional settings with + // self describing names. FixedSize, Fullscreen, VSync bool @@ -31,26 +36,30 @@ type WindowConfig struct { // The main structure that represents current state of [game] engine. type Engine struct { wcfg *WindowConfig - objects maps.Map[Object, struct{}] + + // The main holder for objects. + // Uses the map structure to quickly + // delete and create new objects. + Objects maps.Map[Object, struct{}] + + // The main camera to display in window. + // If is set to nil then the engine will panic. + Camera *Camera + + // The same delta time for all frames + // and all objects. lastTime time.Time dt Float - camera *Camera + + // Temporary stuff keys, prevKeys []Key + cursorPos, prevCursorPos Vector + mouseButtons, prevMouseButtons []MouseButton outerEvents, handleEvents EventChan } type engine Engine -// Return current camera. -func (e *Engine) Camera() *Camera { - return e.camera -} - -// Set new current camera. -func (e *Engine) SetCamera(c *Camera) { - e.camera = c -} - // Get currently pressed keys. func (e *Engine) Keys() []Key { return e.keys @@ -60,22 +69,29 @@ func (e *Engine) Keys() []Key { func NewEngine( cfg *WindowConfig, ) *Engine { - w := Float(cfg.Width) - h := Float(cfg.Height) - return &Engine{ - wcfg: cfg, - camera: &Camera{ - Transform: Transform{ - // Normal, no distortion. - S: Vector{1, 1}, - // Center. - RA: V(w/2, h/2), - }, - }, - objects: maps.NewOrdered[Object, struct{}](), - outerEvents: make(EventChan), - handleEvents: make(EventChan), - } + /*w := Float(cfg.Width) + h := Float(cfg.Height)*/ + + ret := &Engine{} + + ret.wcfg = cfg + ret.Camera = ret.NewCamera() + ret.outerEvents = make(EventChan) + ret.handleEvents = make(EventChan) + ret.Objects = maps.NewOrdered[Object, struct{}]() + return ret +} + +// Get the real window size in the current context. +func (c *Engine) RealWinSize() Vector { + return V( + Float(c.wcfg.Width), + Float(c.wcfg.Height), + ) +} + +func (c *Engine) AbsWinSize() Vector { + return c.RealWinSize().Div(c.Camera.Scale) } func (e *Engine) EventInput() EventChan { @@ -86,7 +102,7 @@ func (e *Engine) EventInput() EventChan { // interfaces it implements. func (e *Engine) Add(b any) error { object, _ := b.(Object) - if e.objects.Has(object) { + if e.Objects.Has(object) { return ObjectExistErr } /*o, ok := e.makeObject(b) @@ -101,7 +117,7 @@ func (e *Engine) Add(b any) error { }) } - e.objects.Set(object, struct{}{}) + e.Objects.Set(object, struct{}{}) return nil } @@ -109,7 +125,7 @@ func (e *Engine) Add(b any) error { // Delete object from Engine. func (e *Engine) Del(b any) error { object, _ := b.(Object) - if !e.objects.Has(object) { + if !e.Objects.Has(object) { return ObjectNotExistErr } @@ -120,14 +136,13 @@ func (e *Engine) Del(b any) error { }) } - e.objects.Del(b) + e.Objects.Del(b) return nil } func (e *engine) Update() error { - var err error eng := (*Engine)(e) e.prevKeys = e.keys @@ -152,7 +167,7 @@ func (e *engine) Update() error { } e.dt = time.Since(e.lastTime).Seconds() - for object := range e.objects.KeyChan() { + for object := range e.Objects.KeyChan() { eventer, ok := object.(Eventer) if ok { for _, event := range events { @@ -166,12 +181,9 @@ func (e *engine) Update() error { if !ok { continue } - err = updater.Update(&Context{ + updater.Update(&Context{ Engine: eng, }) - if err != nil { - return err - } } e.lastTime = time.Now() @@ -181,7 +193,7 @@ func (e *engine) Update() error { func (e *engine) Draw(i *ebiten.Image) { eng := (*Engine)(e) m := map[Layer][]Drawer{} - for object := range eng.objects.KeyChan() { + for object := range eng.Objects.KeyChan() { drawer, ok := object.(Drawer) if !ok { continue @@ -208,6 +220,8 @@ func (e *engine) Draw(i *ebiten.Image) { }) } } + // Empty the buff to generate it again. + eng.Camera.buf = nil } func (e *engine) Layout(ow, oh int) (int, int) { @@ -231,7 +245,7 @@ func (e *Engine) Run() error { ebiten.SetVsyncEnabled(e.wcfg.VSync) e.lastTime = time.Now() - fmt.Println(e.objects) + //fmt.Println(e.Objects) return ebiten.RunGame((*engine)(e)) } diff --git a/go.mod b/go.mod index ad78b1f..28a80be 100644 --- a/go.mod +++ b/go.mod @@ -5,8 +5,8 @@ go 1.21 toolchain go1.21.3 require ( + github.com/di4f/gods v0.0.0-20231214190239-d523423d8d5e github.com/hajimehoshi/ebiten/v2 v2.6.0-alpha.3.0.20230521122940-90562ee84b9b - github.com/omnipunk/gods v0.0.0-20231112101528-1f82aa46d746 ) require ( diff --git a/go.sum b/go.sum index 483ca9c..7e087e5 100644 --- a/go.sum +++ b/go.sum @@ -1,3 +1,5 @@ +github.com/di4f/gods v0.0.0-20231214190239-d523423d8d5e h1:LdL9GpXPJikQ7DXQZFf5Zz5h5MYAS9R7itEztnscku8= +github.com/di4f/gods v0.0.0-20231214190239-d523423d8d5e/go.mod h1:5GbuWHGgBJIIhBiQB/00flvo3k421S8O1XvmrXKWO2U= github.com/ebitengine/purego v0.4.0-alpha.4 h1:Y7yIV06Yo5M2BAdD7EVPhfp6LZ0tEcQo5770OhYUVes= github.com/ebitengine/purego v0.4.0-alpha.4/go.mod h1:ah1In8AOtksoNK6yk5z1HTJeUkC1Ez4Wk2idgGslMwQ= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20221017161538-93cebf72946b h1:GgabKamyOYguHqHjSkDACcgoPIz3w0Dis/zJ1wyHHHU= @@ -6,8 +8,6 @@ github.com/hajimehoshi/ebiten/v2 v2.6.0-alpha.3.0.20230521122940-90562ee84b9b h1 github.com/hajimehoshi/ebiten/v2 v2.6.0-alpha.3.0.20230521122940-90562ee84b9b/go.mod h1:+fFI6Ag5YvbX1ivNQD2TxNhpWFDPuxEoew421TTQAxI= github.com/jezek/xgb v1.1.0 h1:wnpxJzP1+rkbGclEkmwpVFQWpuE2PUGNUzP8SbfFobk= github.com/jezek/xgb v1.1.0/go.mod h1:nrhwO0FX/enq75I7Y7G8iN1ubpSGZEiA3v9e9GyRFlk= -github.com/omnipunk/gods v0.0.0-20231112101528-1f82aa46d746 h1:3Hqv6OBITqr9SUhCqD9ZZG3xXMrYNDvyCsZK7BF4lTo= -github.com/omnipunk/gods v0.0.0-20231112101528-1f82aa46d746/go.mod h1:zARHZc37wTaPpL5GVzZd0FK59r8UbPnxodIXWQ59uLk= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= diff --git a/img.go b/img.go index 44d9772..96d860f 100644 --- a/img.go +++ b/img.go @@ -16,11 +16,20 @@ type Color struct { R, G, B, A ColorV } - const ( MaxColorV = math.MaxUint32 ) +// The wrapper to make RGBA color via +// values from 0 to 1 (no value at all and the max value). +func Rgba(r, g, b, a Float) Color { + return Color { + ColorV(r*MaxColorV), + ColorV(g*MaxColorV), + ColorV(b*MaxColorV), + ColorV(a*MaxColorV), + } +} func LoadImage(input io.Reader) (*Image, error) { img, _, err := image.Decode(input) diff --git a/mouse.go b/mouse.go index 3574c31..cf7c562 100644 --- a/mouse.go +++ b/mouse.go @@ -13,7 +13,7 @@ func (e *Engine) CursorPosition() Vector { func (e *Engine) AbsCursorPosition() Vector { m := &Matrix{} - m.Concat(e.Camera().AbsMatrix()) + m.Concat(e.Camera.AbsMatrix()) return e.CursorPosition().Apply(m) } diff --git a/object.go b/object.go index ee1b747..4e7a1ae 100644 --- a/object.go +++ b/object.go @@ -19,7 +19,7 @@ type Starter interface { // will call the function on each // engine iteration. type Updater interface { - Update(*Context) error + Update(*Context) } // Implementing the interface type diff --git a/polygon.go b/polygon.go index d5b5a6f..0734b87 100644 --- a/polygon.go +++ b/polygon.go @@ -13,6 +13,10 @@ type Polygon struct { Triangles } +func (p *Polygon) ContainsPoint(pnt Point) bool { + return p.MakeTriangles().ContainsPoint(pnt) +} + // Polygon that can be drawn. type DrawablePolygon struct { Polygon diff --git a/rect.go b/rect.go index a27434c..29f4726 100644 --- a/rect.go +++ b/rect.go @@ -9,23 +9,21 @@ import ( // The type describes rectangle geometry. type Rectangle struct { - // P - position of the rotating center. - // Scale represent width and height. Transform } // The type describes rectangle that can be drawn. type DrawableRectangle struct { Rectangle - ShaderOptions + ShaderOptions // Solid color of the rectangle. // Will be ignored if the Shader // field is not nil. - Color Color + Colority // Should be draw or not. - Visible bool + Visibility } // Return points of vertices of the rectangle. @@ -55,7 +53,6 @@ func (r Rectangle) Triangles() Triangles { p2 := pts[1] p3 := pts[2] p4 := pts[3] - //fmt.Println("in:", p1, p2, p3, p4) return Triangles{ Triangle{p1, p2, p3}, @@ -75,7 +72,7 @@ func (r *DrawableRectangle) IsVisible() bool { func (r *DrawableRectangle) Draw(c *Context) { m := r.Matrix() - rm := c.Camera().RealMatrix() + rm := c.Camera.RealMatrix() m.Concat(rm) // Draw solid color if no shader. if r.Shader == nil { diff --git a/sprite.go b/sprite.go index d98dffa..714bbb0 100644 --- a/sprite.go +++ b/sprite.go @@ -2,12 +2,14 @@ package gg import ( "github.com/hajimehoshi/ebiten/v2" + //"fmt" ) type Sprite struct { Transform ShaderOptions - Floating, Visible bool + Floating bool + Visibility } func (s *Sprite) Draw(c *Context) { @@ -20,7 +22,7 @@ func (s *Sprite) Draw(c *Context) { m := &Matrix{} m.Concat(t.Matrix()) if !s.Floating { - m.Concat(c.Camera().RealMatrix()) + m.Concat(c.Camera.RealMatrix()) } // Drawing without shader. @@ -41,11 +43,6 @@ func (s *Sprite) Draw(c *Context) { c.DrawRectShader(w, h, s.Shader, opts) } -// Check is sprite is visible. -func (s *Sprite) IsVisible() bool { - return s.Visible -} - // Return the rectangle that contains the sprite. func (s *Sprite) Rectangle() Rectangle { if s.Images[0] == nil { @@ -54,12 +51,14 @@ func (s *Sprite) Rectangle() Rectangle { w, h := s.Images[0].Size() t := s.Transform - t.RA.X *= Float(w) - t.RA.Y *= Float(h) + t.Around.X *= Float(w) + t.Around.Y *= Float(h) return Rectangle{t} } +// Get triangles of the rectangle that contains the sprite +// and has the same widght and height. func (s *Sprite) Triangles() Triangles { return s.Rectangle().Triangles() } diff --git a/transform.go b/transform.go index 6e0daf9..c9e1fa4 100644 --- a/transform.go +++ b/transform.go @@ -8,23 +8,26 @@ import ( // The structure represents basic transformation // features: positioning, rotating and scaling. type Transform struct { - // P - absolute phisycal position in engine itself. - // - // S - scale width and height (X and Y). - // - // RA - rotate around(relatively of position, not absolute). - // - // For example RA=Vector{0, 0} will rotate around right up corner - // and RA=Vector{.5, .5} will rotate around center. - P, S, RA Vector - // Rotation angle in radians. - R Float + // Absolute (if no parent) position and + // the scale. + Position, Scale Vector + // The object rotation in radians. + Rotation Float + // The not scaled offset vector from upper left corner + // which the object should be rotated around. + Around Vector + // Needs to be implemented. + // Makes transform depending on the other one. + // Is the root one if Parent == nil + Parent *Transform } -// Returns empty Transform. +// Returns empty Transform +// with standard scaling. (1/1) func T() Transform { ret := Transform{ - S: Vector{1, 1}, + Scale: Vector{1, 1}, + Around: V(.5, .5), } return ret } @@ -34,12 +37,12 @@ func (t Transform) ScaledToXY(x, y Float) Transform { } func (t Transform) ScaledToX(x Float) Transform { - t.S.X = x + t.Scale.X = x return t } func (t Transform) ScaledToY(y Float) Transform { - t.S.Y = y + t.Scale.Y = y return t } @@ -48,10 +51,18 @@ func (t Transform) ScaledToY(y Float) Transform { func (t Transform)Matrix() Matrix { g := &Matrix{} - g.Scale(t.S.X, t.S.Y) - g.Translate(-t.RA.X * t.S.X, -t.RA.Y * t.S.Y) - g.Rotate(t.R) - g.Translate(t.P.X, t.P.Y) + // Scale first. + g.Scale(t.Scale.X, t.Scale.Y) + + // Then move and rotate. + g.Translate( + -t.Around.X * t.Scale.X, + -t.Around.Y * t.Scale.Y, + ) + g.Rotate(t.Rotation) + + // And finally move to the absolute position. + g.Translate(t.Position.X, t.Position.Y) return *g } diff --git a/triangle.go b/triangle.go index 42c24f3..655b4a0 100644 --- a/triangle.go +++ b/triangle.go @@ -89,7 +89,7 @@ func (ts Triangles) ContainsPoint(p Point) bool { } func (r *DrawableTriangles) Draw(c *Context) { - m := c.Camera().RealMatrix() + m := c.Camera.RealMatrix() cm := &m // Draw solid color if no shader. diff --git a/vector.go b/vector.go index 0d0a678..f310c58 100644 --- a/vector.go +++ b/vector.go @@ -18,6 +18,24 @@ func V(x, y Float) Vector { return Vector{x, y} } +func V1(v Float) Vector { + return V(v, v) +} + +func (v Vector) Div(o Vector) Vector { + return V( + v.X / o.X, + v.Y / o.Y, + ) +} + +func (v Vector) Scale(o Vector) Vector { + return V( + v.X * o.X, + v.Y * o.Y, + ) +} + func (v Vector) Eq(o Vector) bool { return v.X == o.X && v.Y == o.Y }