Make animations work in the options screen
This commit is contained in:
15
internal/ui/animation.go
Normal file
15
internal/ui/animation.go
Normal file
@@ -0,0 +1,15 @@
|
||||
package ui
|
||||
|
||||
import (
|
||||
"github.com/hajimehoshi/ebiten"
|
||||
)
|
||||
|
||||
type animation []*ebiten.Image
|
||||
|
||||
func (a animation) image(step int) *ebiten.Image {
|
||||
if len(a) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
return a[step%len(a)]
|
||||
}
|
@@ -4,7 +4,6 @@ import (
|
||||
"fmt"
|
||||
"image"
|
||||
"log"
|
||||
"reflect" // For DeepEqual
|
||||
|
||||
"github.com/hajimehoshi/ebiten"
|
||||
"github.com/hajimehoshi/ebiten/ebitenutil"
|
||||
@@ -23,7 +22,7 @@ import (
|
||||
type Interface struct {
|
||||
Name string
|
||||
menu *assetstore.Menu
|
||||
static []*staticElement
|
||||
static []*noninteractive
|
||||
ticks int
|
||||
widgets []*Widget
|
||||
}
|
||||
@@ -44,14 +43,14 @@ func NewInterface(menu *assetstore.Menu) (*Interface, error) {
|
||||
}
|
||||
|
||||
// Find a widget by its hierarchical ID path
|
||||
func (i *Interface) Widget(path ...int) (*Widget, error) {
|
||||
func (i *Interface) Widget(path string) (*Widget, error) {
|
||||
for _, widget := range i.widgets {
|
||||
if reflect.DeepEqual(path, widget.path) {
|
||||
if path == widget.path {
|
||||
return widget, nil
|
||||
}
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("Couldn't find widget %#+v", path)
|
||||
return nil, fmt.Errorf("Couldn't find widget %v", path)
|
||||
}
|
||||
|
||||
func (i *Interface) Update(screenX, screenY int) error {
|
||||
@@ -82,9 +81,9 @@ func (i *Interface) Draw(screen *ebiten.Image) error {
|
||||
do := &ebiten.DrawImageOptions{GeoM: geo}
|
||||
|
||||
for _, s := range i.static {
|
||||
if s.image != nil {
|
||||
do.GeoM.Translate(geo.Apply(float64(s.bounds.Min.X), float64(s.bounds.Min.X)))
|
||||
if err := screen.DrawImage(s.image, do); err != nil {
|
||||
if image := s.image(i.ticks); image != nil {
|
||||
do.GeoM.Translate(geo.Apply(float64(s.bounds.Min.X), float64(s.bounds.Min.Y)))
|
||||
if err := screen.DrawImage(image, do); err != nil {
|
||||
return err
|
||||
}
|
||||
do.GeoM = geo
|
||||
|
20
internal/ui/noninteractive.go
Normal file
20
internal/ui/noninteractive.go
Normal file
@@ -0,0 +1,20 @@
|
||||
package ui
|
||||
|
||||
import (
|
||||
"github.com/hajimehoshi/ebiten"
|
||||
"image"
|
||||
)
|
||||
|
||||
// A non-interactive element is not a widget; it merely displays some pixels and
|
||||
// may optionally have a tooltip for display within bounds.
|
||||
//
|
||||
// For non-animated non-interactive elements, just give them a single frame.
|
||||
type noninteractive struct {
|
||||
bounds image.Rectangle
|
||||
frames animation
|
||||
tooltip string
|
||||
}
|
||||
|
||||
func (n *noninteractive) image(step int) *ebiten.Image {
|
||||
return n.frames.image(step)
|
||||
}
|
@@ -17,7 +17,7 @@ var setupHandlers = map[menus.MenuType]func(i *Interface, r *menus.Record) error
|
||||
menus.TypeOverlay: nil, // FIXME: What's it for?
|
||||
menus.TypeHypertext: handleHypertext,
|
||||
menus.TypeCheckbox: handleCheckbox,
|
||||
menus.TypeAnimationSample: nil, // FIXME: handle this
|
||||
menus.TypeAnimationSample: handleAnimation,
|
||||
menus.TypeMainButton: handleMainButton,
|
||||
menus.TypeSlider: nil, // FIXME: handle this
|
||||
}
|
||||
@@ -35,9 +35,9 @@ func handleStatic(i *Interface, record *menus.Record) error {
|
||||
return err
|
||||
}
|
||||
|
||||
static := &staticElement{
|
||||
static := &noninteractive{
|
||||
bounds: sprite.Rect,
|
||||
image: sprite.Image,
|
||||
frames: animation{sprite.Image},
|
||||
tooltip: record.Desc,
|
||||
}
|
||||
|
||||
@@ -54,9 +54,9 @@ func handleHypertext(i *Interface, record *menus.Record) error {
|
||||
return err
|
||||
}
|
||||
|
||||
static := &staticElement{
|
||||
static := &noninteractive{
|
||||
bounds: sprite.Rect,
|
||||
image: nil,
|
||||
frames: nil,
|
||||
tooltip: record.Desc,
|
||||
}
|
||||
|
||||
@@ -106,6 +106,29 @@ func handleCheckbox(i *Interface, record *menus.Record) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// An animation is a non-interactive element that displays something in a loop
|
||||
func handleAnimation(i *Interface, record *menus.Record) error {
|
||||
sprite, err := i.menu.Sprite(record.SpriteId[0])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
frames, err := i.menu.Images(record.SpriteId[0], record.DrawType)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
ani := &noninteractive{
|
||||
bounds: sprite.Rect,
|
||||
frames: animation(frames),
|
||||
tooltip: record.Desc,
|
||||
}
|
||||
|
||||
i.static = append(i.static, ani)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func handleButton(i *Interface, record *menus.Record) error {
|
||||
spriteId := record.SpriteId[0]
|
||||
widget, err := i.widgetFromRecord(record, spriteId)
|
||||
@@ -179,7 +202,7 @@ func handleMainButton(i *Interface, record *menus.Record) error {
|
||||
|
||||
widget.mouseButtonDownImage = pressed.Image
|
||||
widget.disabledImage = disabled.Image
|
||||
widget.hoverAnimation = hovers
|
||||
widget.hoverAnimation = animation(hovers)
|
||||
|
||||
i.widgets = append(i.widgets, widget)
|
||||
|
||||
@@ -194,15 +217,10 @@ func (i *Interface) widgetFromRecord(record *menus.Record, spriteId int) (*Widge
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var path []int
|
||||
for r := record; r != nil; r = r.Parent {
|
||||
path = append([]int{r.Id}, path...)
|
||||
}
|
||||
|
||||
widget := &Widget{
|
||||
Bounds: sprite.Rect,
|
||||
Tooltip: record.Desc,
|
||||
path: path,
|
||||
path: record.Path(),
|
||||
record: record,
|
||||
sprite: sprite,
|
||||
}
|
||||
|
@@ -1,15 +0,0 @@
|
||||
package ui
|
||||
|
||||
import (
|
||||
"image"
|
||||
|
||||
"github.com/hajimehoshi/ebiten"
|
||||
)
|
||||
|
||||
// A static element is not a widget; it merely displays some pixels and may
|
||||
// optionally have a tooltip for display within bounds
|
||||
type staticElement struct {
|
||||
bounds image.Rectangle
|
||||
image *ebiten.Image
|
||||
tooltip string
|
||||
}
|
@@ -30,7 +30,7 @@ type Widget struct {
|
||||
disabledImage *ebiten.Image
|
||||
|
||||
// These are expected to have the same dimensions as the Bounds
|
||||
hoverAnimation []*ebiten.Image
|
||||
hoverAnimation animation
|
||||
hoverState bool
|
||||
|
||||
// FIXME: We assume right mouse button isn't needed here
|
||||
@@ -38,7 +38,7 @@ type Widget struct {
|
||||
mouseButtonDownImage *ebiten.Image
|
||||
mouseButtonState bool
|
||||
|
||||
path []int
|
||||
path string
|
||||
record *menus.Record
|
||||
sprite *assetstore.Sprite
|
||||
|
||||
@@ -97,8 +97,8 @@ func (w *Widget) Image(aniStep int) (*ebiten.Image, error) {
|
||||
return w.mouseButtonDownImage, nil
|
||||
}
|
||||
|
||||
if w.hoverState && len(w.hoverAnimation) > 0 {
|
||||
return w.hoverAnimation[(aniStep)%len(w.hoverAnimation)], nil
|
||||
if w.hoverState && w.hoverAnimation != nil {
|
||||
return w.hoverAnimation.image(aniStep), nil
|
||||
}
|
||||
|
||||
if w.valueToImage != nil {
|
||||
|
Reference in New Issue
Block a user