152 lines
2.9 KiB
Go
152 lines
2.9 KiB
Go
package hero
|
|
|
|
import (
|
|
"fmt"
|
|
"image"
|
|
_ "image/png"
|
|
"os"
|
|
"path/filepath"
|
|
"sort"
|
|
|
|
"github.com/hajimehoshi/ebiten/v2"
|
|
)
|
|
|
|
const (
|
|
heroDir = "assets/hero"
|
|
)
|
|
|
|
type animState int
|
|
|
|
const (
|
|
animIdle animState = iota
|
|
animMove
|
|
)
|
|
|
|
type animationKey struct {
|
|
direction Direction
|
|
state animState
|
|
}
|
|
|
|
var (
|
|
knightAnimations map[animationKey][]*ebiten.Image
|
|
)
|
|
|
|
func init() {
|
|
knightAnimations = make(map[animationKey][]*ebiten.Image)
|
|
|
|
if err := loadKnightAnimations(); err != nil {
|
|
panic(err)
|
|
}
|
|
}
|
|
|
|
func getKnightSprite(direction Direction, moving bool, frameIndex int) *ebiten.Image {
|
|
state := animIdle
|
|
if moving {
|
|
state = animMove
|
|
}
|
|
return frameFromSet(direction, state, frameIndex)
|
|
}
|
|
|
|
func loadKnightAnimations() error {
|
|
frames, err := loadAnimationFrames(heroDir)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
knightAnimations[animationKey{DirLeft, animMove}] = frames
|
|
knightAnimations[animationKey{DirLeft, animIdle}] = frames
|
|
|
|
flippedFrames := make([]*ebiten.Image, len(frames))
|
|
for i, frame := range frames {
|
|
flippedFrames[i] = flipImageHorizontally(frame)
|
|
}
|
|
|
|
knightAnimations[animationKey{DirRight, animMove}] = flippedFrames
|
|
knightAnimations[animationKey{DirRight, animIdle}] = flippedFrames
|
|
|
|
return nil
|
|
}
|
|
|
|
func loadAnimationFrames(dir string) ([]*ebiten.Image, error) {
|
|
entries, err := os.ReadDir(dir)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
var files []string
|
|
for _, entry := range entries {
|
|
if entry.IsDir() {
|
|
continue
|
|
}
|
|
name := entry.Name()
|
|
if filepath.Ext(name) == ".png" {
|
|
files = append(files, filepath.Join(dir, name))
|
|
}
|
|
}
|
|
|
|
sort.Strings(files)
|
|
|
|
var frames []*ebiten.Image
|
|
for _, file := range files {
|
|
img, err := loadImage(file)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("load frame %s: %w", file, err)
|
|
}
|
|
frames = append(frames, img)
|
|
}
|
|
|
|
return frames, nil
|
|
}
|
|
|
|
func loadImage(path string) (*ebiten.Image, error) {
|
|
f, err := os.Open(path)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer f.Close()
|
|
|
|
img, _, err := image.Decode(f)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return ebiten.NewImageFromImage(img), nil
|
|
}
|
|
|
|
func flipImageHorizontally(img *ebiten.Image) *ebiten.Image {
|
|
bounds := img.Bounds()
|
|
w, h := bounds.Dx(), bounds.Dy()
|
|
|
|
flipped := ebiten.NewImage(w, h)
|
|
|
|
op := &ebiten.DrawImageOptions{}
|
|
op.GeoM.Scale(-1, 1)
|
|
op.GeoM.Translate(float64(w), 0)
|
|
|
|
flipped.DrawImage(img, op)
|
|
|
|
return flipped
|
|
}
|
|
|
|
func frameFromSet(direction Direction, state animState, frameIndex int) *ebiten.Image {
|
|
if frameIndex < 0 {
|
|
frameIndex = 0
|
|
}
|
|
|
|
if frames := knightAnimations[animationKey{direction, state}]; len(frames) > 0 {
|
|
return frames[frameIndex%len(frames)]
|
|
}
|
|
|
|
if state == animMove {
|
|
if frames := knightAnimations[animationKey{direction, animIdle}]; len(frames) > 0 {
|
|
return frames[frameIndex%len(frames)]
|
|
}
|
|
} else {
|
|
if frames := knightAnimations[animationKey{direction, animMove}]; len(frames) > 0 {
|
|
return frames[frameIndex%len(frames)]
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|