From f2d7feb8a412b002dea04b6b68c8f4fc1c1eadaf Mon Sep 17 00:00:00 2001 From: surdeus Date: Sat, 13 Jan 2024 23:46:12 +0300 Subject: [PATCH] feat: implemented basic animation. (very raw) --- animation.go | 77 ++++++++++++++++++++++++++++++++++++++++---- cmd/test/main.go | 13 ++++++++ cmd/test/player.go | 15 ++++----- cmd/test/trianlge.go | 6 ++-- engine.go | 24 +++++++------- img.go | 5 +++ time.go | 7 ++++ 7 files changed, 118 insertions(+), 29 deletions(-) diff --git a/animation.go b/animation.go index fb4b1be..c6d2d36 100644 --- a/animation.go +++ b/animation.go @@ -1,15 +1,67 @@ package gg import ( - "time" + "github.com/hajimehoshi/ebiten/v2" + "fmt" ) -type Animation struct { - Frames []*Image +type Animation []*Image + +type AnimationDefine struct{ + Id AnimationId + Indexes []ImageRectIndex +} +// Animation define +func AD(id AnimationId, indexes ...ImageRectIndex) AnimationDefine { + return AnimationDefine{ + Id: id, + Indexes: indexes, + } +} +func (ad AnimationDefine) DefRow(y int, xs ...int) AnimationDefine { + for _, x := range xs { + ad.Indexes = append(ad.Indexes, ImageRectIndex{x, y}) + } + return ad +} + +func (ad AnimationDefine) DefCol(x int, ys ...int) AnimationDefine { + for _, y := range ys { + ad.Indexes = append(ad.Indexes, ImageRectIndex{x, y}) + } + return ad } type AnimationId int -type AnimationSet map[AnimationId] *Animation +type AnimationSet map[AnimationId] Animation +func AnimationSetFromImage( + img *Image, + w, h int, + defines ...AnimationDefine, +) (AnimationSet, error) { + set := AnimationSet{} + + r := img.Bounds() + fw, fh := r.Dx()/w, r.Dy()/h + + for _, define := range defines { + fmt.Println("def-idx-len:", len(define.Indexes)) + animation := make(Animation, len(define.Indexes)) + for i := range animation { + idx := define.Indexes[i] + animation[i] = ebiten.NewImageFromImage(img.SubImage( + ImageRect{ + Min: ImagePoint{idx.X*fw, idx.Y*fh}, + Max: ImagePoint{(idx.X+1)*fw, (idx.Y+1)*fh}, + }, + )) + } + fmt.Println("animation-len:", len(animation)) + set[define.Id] = animation + } + + return set, nil +} // The type implements animated sprites. type AnimatedSprite struct { @@ -17,18 +69,31 @@ type AnimatedSprite struct { Animations AnimationSet AnimationId AnimationId CurrentFrame int - TimeBetweenFrames time.Duration + // This is time between animation frames, not + // engine ones. + TimeBetweenFrames Duration + duration Duration } func (as *AnimatedSprite) Animate(id AnimationId) bool { _, ok := as.Animations[id] if ok { + as.duration = 0 as.AnimationId = id } return ok } func (as *AnimatedSprite) Draw(c *Context) []EVertex { - return nil + as.duration += c.DrawDt() + frames := as.Animations[as.AnimationId] + fmt.Println("len:", len(frames)) + fullTime := Duration(len(frames)) * as.TimeBetweenFrames + if as.duration > fullTime { + as.duration -= fullTime + } + as.Images[0] = frames[(as.duration/as.TimeBetweenFrames)%Duration(len(frames))] + + return as.Sprite.Draw(c) } diff --git a/cmd/test/main.go b/cmd/test/main.go index 022fd30..1439c4c 100644 --- a/cmd/test/main.go +++ b/cmd/test/main.go @@ -21,7 +21,13 @@ const ( LowestL ) +const ( + Stand gg.AnimationId = iota + Walk +) + var ( + playerAnimations gg.AnimationSet playerImg *gg.Image player *Player rectMove gg.Rectangle @@ -45,6 +51,12 @@ func main() { } rect = NewRect() + + playerAnimations, _ = gg.AnimationSetFromImage( + playerImg, + 8, 3, + gg.AD(Walk).DefRow(0, 0, 1, 2, 3, 4), + ) player = NewPlayer() tri = NewTri() @@ -82,6 +94,7 @@ func main() { } e.Spawn(txt) + //fmt.Println(rect.GetLayer(), player.GetLayer()) fmt.Println("Starting...") err = e.Run() diff --git a/cmd/test/player.go b/cmd/test/player.go index 25b59c1..9e74153 100644 --- a/cmd/test/player.go +++ b/cmd/test/player.go @@ -3,12 +3,13 @@ package main import ( //"math/rand" "fmt" + "time" ) import "vultras.su/core/gg" type Player struct { - gg.Sprite + gg.AnimatedSprite MoveSpeed gg.Float ScaleSpeed gg.Float Spawned bool @@ -17,17 +18,15 @@ type Player struct { func NewPlayer() *Player { ret := &Player{} ret.Transform = gg.T() - fmt.Println("transform:", ret.Transform) - //ret.Parent = rect ret.Scale = gg.V2(1) - // Around center. ret.Around = gg.V2(.5) ret.MoveSpeed = 90. ret.ScaleSpeed = .2 + ret.Animations = playerAnimations + ret.TimeBetweenFrames = time.Second/5 + fmt.Println("player-walk", ret.Animate(Walk)) ret.Visible = true - - ret.Images[0] = playerImg ret.Layer = PlayerL return ret @@ -40,7 +39,7 @@ func (p *Player) Update(c *Context) { if p.Spawned { return } - dt := c.Dt() + dt := c.Dt().Seconds() cam := c.Camera keys := c.Keys() @@ -124,7 +123,7 @@ func (p *Player) Update(c *Context) { c.Camera.Position = pos.Sub(ec.Abs) case *gg.WheelChange: c.Camera.Scale = c.Camera.Scale.Add(gg.V2( - ec.Offset.Y * c.Dt() * p.ScaleSpeed * 40, + ec.Offset.Y * dt * p.ScaleSpeed * 40, )) }} } diff --git a/cmd/test/trianlge.go b/cmd/test/trianlge.go index 323611d..053b04c 100644 --- a/cmd/test/trianlge.go +++ b/cmd/test/trianlge.go @@ -32,7 +32,7 @@ func NewTri() *Tri { } func (t *Tri) Update(c *Context) { - dt := c.Dt() + dt := c.Dt().Seconds() if t.ContainsPoint(c.AbsCursorPosition()) { t.Color = gg.Rgba(0, 1, 0, 1) } else { @@ -53,12 +53,12 @@ func (t *Tri) Update(c *Context) { case gg.KeyM: absPos := t.AbsPosition() t.SetAbsPosition( - absPos.Add(gg.V(0, 100*c.Dt()*d)), + absPos.Add(gg.V(0, 100*dt*d)), ) case gg.KeyN: absPos := t.AbsPosition() t.SetAbsPosition( - absPos.Add(gg.V(100*c.Dt()*d, 0)), + absPos.Add(gg.V(100*dt*d, 0)), ) case gg.KeyV: t.Rotation += d * gg.Pi * 0.3 * dt diff --git a/engine.go b/engine.go index 77d4648..2bad949 100644 --- a/engine.go +++ b/engine.go @@ -61,12 +61,12 @@ type Engine struct { // If is set to nil then the engine will panic. Camera *Camera - lastTime time.Time - dt Float + drawLastTime time.Time + drawdt Duration // Frame delta time. - fdt Float - fLastTime time.Time + dt Duration + lastTime time.Time // Temporary stuff keys, prevKeys []Key @@ -228,7 +228,7 @@ func (e *Engine) AbsCursorPosition() Vector { func (e *engine) Update() error { eng := (*Engine)(e) - e.fdt = time.Since(e.fLastTime).Seconds() + e.dt = time.Since(e.lastTime) // Buffering the context for faster. @@ -302,7 +302,7 @@ func (e *engine) Update() error { object.Input() <- c } e.wg.Wait() - e.fLastTime = time.Now() + e.lastTime = time.Now() e.frame++ return nil } @@ -323,7 +323,7 @@ var ( defaultTriOptions = &ebiten.DrawTrianglesOptions{} ) func (e *engine) Draw(img *ebiten.Image) { - e.dt = time.Since(e.lastTime).Seconds() + e.drawdt = time.Since(e.drawLastTime) eng := (*Engine)(e) m := map[Layer][]Drawer{} for _, object := range eng.Objects.store { @@ -378,7 +378,7 @@ func (e *engine) Draw(img *ebiten.Image) { // Empty the buff to generate it again. eng.Camera.buffered = false - e.lastTime = time.Now() + e.drawLastTime = time.Now() e.dframe++ } @@ -391,8 +391,8 @@ func (e *engine) Layout(ow, oh int) (int, int) { } // Return the delta time. -func (e *Engine) Dt() Float { - return e.dt +func (e *Engine) DrawDt() Duration { + return e.drawdt } func (e *Engine) Dframe() uint { @@ -400,8 +400,8 @@ func (e *Engine) Dframe() uint { } // Return the current fixed delta time. -func (e *Engine) Fdt() Float { - return 1/60 +func (e *Engine) Dt() Duration { + return e.dt } func (e *Engine) Frame() uint { diff --git a/img.go b/img.go index 96d860f..68c9500 100644 --- a/img.go +++ b/img.go @@ -9,6 +9,11 @@ import ( ) type Image = ebiten.Image +type ImageRect = image.Rectangle +type ImagePoint = image.Point +type ImageRectIndex struct { + X, Y int +} type ColorV uint32 type ColorM = ebiten.ColorM diff --git a/time.go b/time.go index 5c435db..4e780d5 100644 --- a/time.go +++ b/time.go @@ -7,3 +7,10 @@ import ( type Time = time.Time type Duration = time.Duration +const ( + ZeroDuration Duration = 0 + Microsecond = time.Microsecond + Millisecond = time.Millisecond + Second = time.Second +) +