feat: implemented basic animation. (very raw)

This commit is contained in:
Andrey Parhomenko 2024-01-13 23:46:12 +03:00
parent 60cd4e9e75
commit f2d7feb8a4
7 changed files with 118 additions and 29 deletions

View file

@ -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)
}

View file

@ -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()

View file

@ -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,
))
}}
}

View file

@ -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

View file

@ -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 {

5
img.go
View file

@ -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

View file

@ -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
)