From cb3ccb323cbaae9c353cffe936d393194ab0b9e6 Mon Sep 17 00:00:00 2001 From: Thomas von Dein Date: Thu, 4 Apr 2024 18:24:44 +0200 Subject: [PATCH] better and easier to understand asset loading --- assets/loader-sprites.go | 100 +++++++++++++++++++++++++-------------- 1 file changed, 65 insertions(+), 35 deletions(-) diff --git a/assets/loader-sprites.go b/assets/loader-sprites.go index 8c32cce..9a4fa94 100644 --- a/assets/loader-sprites.go +++ b/assets/loader-sprites.go @@ -6,6 +6,7 @@ import ( "encoding/json" "image" _ "image/png" + "io/fs" "log" "log/slog" "path" @@ -17,6 +18,11 @@ import ( // Maps image name to image data type AssetRegistry map[string]*ebiten.Image +// A helper to pass the registry easier around +type assetData struct { + Registry AssetRegistry +} + type AnimationGeo struct { X int `json:"x"` Y int `json:"y"` @@ -52,13 +58,15 @@ type AnimationRegistry map[string]AnimationSet //go:embed sprites/*.png fonts/*.ttf levels/*.ldtk shaders/*.kg sprites/*.json var assetfs embed.FS +// Called at build time, creates the global asset and animation registries var Assets, Animations = LoadImages() +// load pngs and json files func LoadImages() (AssetRegistry, AnimationRegistry) { dir := "sprites" - images := AssetRegistry{} + imagedata := &assetData{} + imagedata.Registry = AssetRegistry{} rawanimations := []AnimationJSON{} - animations := AnimationRegistry{} // we use embed.FS to iterate over all files in ./assets/ entries, err := assetfs.ReadDir(dir) @@ -69,53 +77,75 @@ func LoadImages() (AssetRegistry, AnimationRegistry) { for _, imagefile := range entries { path := path.Join(dir, imagefile.Name()) + fd, err := assetfs.Open(path) + if err != nil { + log.Fatalf("failed to open file %s: %s", imagefile.Name(), err) + } + defer fd.Close() + 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, image := ReadImage(imagefile, fd) + imagedata.Registry[name] = image - 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) - } - + animationjson := ReadJson(imagefile, fd) rawanimations = append(rawanimations, animationjson) } + + slog.Debug("loaded asset", "path", path) } // preprocess animation sprites + animations := ProcessAnimations(rawanimations, imagedata) + + return imagedata.Registry, animations +} + +func ReadImage(imagefile fs.DirEntry, fd fs.File) (string, *ebiten.Image) { + 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) + } + + image := ebiten.NewImageFromImage(img) + + return name, image +} + +func ReadJson(imagefile fs.DirEntry, fd fs.File) AnimationJSON { + 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) + } + + return animationjson +} + +// turn a raw JSON from Asesprite into something we can use better +// internally we also load all the sprites of an animation by using +// image.SubImage() on the spriteset matching the animation name. +// so, if the animation JSON is called "player-idle.json", then we +// expect to receive the spriteset in a file "player-idle.png", which +// has of course already been loaded at this stage. These spritesets +// must contain a row of sprites. We get the measurements from the JSON. +func ProcessAnimations(rawanimations []AnimationJSON, imagedata *assetData) AnimationRegistry { + animations := AnimationRegistry{} + for _, animation := range rawanimations { animationset := AnimationSet{} animationset.File = strings.TrimSuffix(animation.Meta.Name, ".png") for _, frame := range animation.Frames { - sprite := images[animationset.File].SubImage( + sprite := imagedata.Registry[animationset.File].SubImage( image.Rect( frame.Position.X, frame.Position.Y, @@ -132,5 +162,5 @@ func LoadImages() (AssetRegistry, AnimationRegistry) { animations[animationset.File] = animationset } - return images, animations + return animations }