package assets import ( "bytes" "embed" "encoding/json" "image" _ "image/png" "log" "log/slog" "path" "strings" "github.com/alecthomas/repr" "github.com/hajimehoshi/ebiten/v2" ) // Maps image name to image data type AssetRegistry map[string]*ebiten.Image type AnimationGeo struct { X int `json:"x"` Y int `json:"y"` Width int `json:"w"` Height int `json:"h"` } type AnimationFrame struct { Position AnimationGeo `json:"frame"` // FIXME: maybe also add delay etc? might be cool to tweak these things from LDTK } type AnimationMeta struct { Name string `json:"image"` Geo AnimationGeo `json:"size"` } // Needed to thaw sprite set written by asesprite type AnimationJSON struct { Meta AnimationMeta `json:"Meta"` Frames []AnimationFrame `json:"frames"` } // Animation data type AnimationSet struct { Width, Height int Sprites []*ebiten.Image File string } type AnimationRegistry map[string]AnimationSet //go:embed sprites/*.png fonts/*.ttf levels/*.ldtk shaders/*.kg sprites/*.json var assetfs embed.FS var Assets, Animations = LoadImages() func LoadImages() (AssetRegistry, AnimationRegistry) { dir := "sprites" images := AssetRegistry{} rawanimations := []AnimationJSON{} animations := AnimationRegistry{} // we use embed.FS to iterate over all files in ./assets/ entries, err := assetfs.ReadDir(dir) if err != nil { log.Fatalf("failed to read assets dir %s: %s", dir, err) } for _, imagefile := range entries { path := path.Join(dir, imagefile.Name()) switch { case strings.HasSuffix(path, ".png"): fd, err := assetfs.Open(path) if err != nil { log.Fatalf("failed to open image file %s: %s", imagefile.Name(), err) } defer fd.Close() name := strings.TrimSuffix(imagefile.Name(), ".png") img, _, err := image.Decode(fd) if err != nil { log.Fatalf("failed to decode image %s: %s", imagefile.Name(), err) } images[name] = ebiten.NewImageFromImage(img) slog.Debug("loaded asset", "path", path) case strings.HasSuffix(path, ".json"): fd, err := assetfs.Open(path) if err != nil { log.Fatalf("failed to open json file %s: %s", imagefile.Name(), err) } defer fd.Close() buf := new(bytes.Buffer) buf.ReadFrom(fd) animationjson := AnimationJSON{} err = json.Unmarshal(buf.Bytes(), &animationjson) if err != nil { log.Fatalf("failed to parse JSON: %s", err) } rawanimations = append(rawanimations, animationjson) } } // preprocess animation sprites for _, animation := range rawanimations { animationset := AnimationSet{} animationset.File = strings.TrimSuffix(animation.Meta.Name, ".png") animationset.Width = animation.Meta.Geo.Width animationset.Height = animation.Meta.Geo.Height for _, frame := range animation.Frames { sprite := images[animationset.File].SubImage( image.Rect( frame.Position.X, frame.Position.Y, frame.Position.Width, frame.Position.Height, )).(*ebiten.Image) animationset.Sprites = append(animationset.Sprites, sprite) } animations[animationset.File] = animationset } repr.Println(animations) return images, animations }