From f94f3b94484bb936a1a840151a1b5aa93ce9eb59 Mon Sep 17 00:00:00 2001 From: Ben Vezzani Date: Mon, 25 Aug 2025 11:56:44 -0400 Subject: [PATCH] sprite stuff --- common/sprites/v1/sprite.go | 133 ++++++++++++++++++++++++++++++++++++ common/window/v1/window.go | 5 ++ 2 files changed, 138 insertions(+) create mode 100644 common/sprites/v1/sprite.go create mode 100644 common/window/v1/window.go diff --git a/common/sprites/v1/sprite.go b/common/sprites/v1/sprite.go new file mode 100644 index 0000000..7316f42 --- /dev/null +++ b/common/sprites/v1/sprite.go @@ -0,0 +1,133 @@ +package sprites + +import ( + "image" + + "git.vezzani.net/ben/games/common/window/v1" + "github.com/hajimehoshi/ebiten/v2" +) + +var ( + updateCount int +) + +func Update() { + updateCount++ +} + +type animation struct { + RowNumber uint8 + FrameCount uint8 + Scale float32 +} + +type subImager interface { + image.Image + SubImage(r image.Rectangle) image.Image +} + +type Sprite struct { + width, height int + imgData subImager + animations map[string]animation + + baseAnim *animation +} + +func (s *Sprite) baseAnimation() animation { + if s.baseAnim != nil { + return *s.baseAnim + } + + for i := range s.animations { + if s.animations[i].RowNumber == 0 { + a := s.animations[i] + s.baseAnim = &a + return a + } + } + + return animation{} +} + +func (s *Sprite) getAnimation(id string) animation { + if id == "" { + return s.baseAnimation() + } + + if anim, ok := s.animations[id]; ok { + return anim + } + + return s.baseAnimation() +} + +func (s *Sprite) Image(ops imageOptions) image.Image { + anim := s.getAnimation(ops.animation) + + xOffset := updateCount % int(anim.FrameCount) * s.width + yOffset := int(anim.RowNumber) * s.height + + r := image.Rect(xOffset, yOffset, s.width, s.height).Intersect(s.imgData.Bounds()) + + return s.imgData.SubImage(r) +} + +func (s *Sprite) Draw(screen *ebiten.Image, options ...ImageOption) int { + ops := imageOptions{ + scaleX: window.Scale, + scaleY: window.Scale, + } + + for _, o := range options { + o(&ops) + } + + geom := ebiten.GeoM{} + geom.Translate(ops.x, ops.y) + geom.Scale(ops.scaleX, ops.scaleY) + geom.Rotate(ops.rotateTheta) + + screen.DrawImage( + ebiten.NewImageFromImage( + s.Image(ops), + ), + &ebiten.DrawImageOptions{ + GeoM: geom, + }, + ) +} + +type imageOptions struct { + x, y float64 + scaleX, scaleY float64 + rotateTheta float64 + animation string +} + +type ImageOption func(options *imageOptions) + +func ToScale(x, y float64) ImageOption { + return func(o *imageOptions) { + o.scaleX *= x + o.scaleY *= y + } +} + +func Animation(name string) ImageOption { + return func(o *imageOptions) { + o.animation = name + } +} + +func AtPosition(x, y float64) ImageOption { + return func(o *imageOptions) { + o.x, o.y = x, y + } +} + +func Rotate(theta float64) ImageOption { + return func(o *imageOptions) { + o.rotateTheta = theta + } +} diff --git a/common/window/v1/window.go b/common/window/v1/window.go new file mode 100644 index 0000000..be1d8b8 --- /dev/null +++ b/common/window/v1/window.go @@ -0,0 +1,5 @@ +package window + +var ( + Scale float64 = 1.0 +)