From 4ffbb1362e83ccb4611cb48deff2c2c8dbdc745c Mon Sep 17 00:00:00 2001 From: Armin Schlegel Date: Tue, 19 Aug 2025 07:47:42 +0200 Subject: [PATCH] refactor ecs renderer for raylib 3d --- cmd/animations_ecs/cmd/root.go | 2 +- cmd/island_ecs/cmd/root.go | 103 +++++-------- pkg/ecs/components/island.go | 3 +- pkg/ecs/components/tile.go | 4 +- pkg/ecs/systems/input.go | 31 ++-- pkg/ecs/systems/mouseSelector.go | 5 +- pkg/ecs/systems/renderer.go | 211 +++++---------------------- pkg/texture/animations/animations.go | 13 +- 8 files changed, 103 insertions(+), 269 deletions(-) diff --git a/cmd/animations_ecs/cmd/root.go b/cmd/animations_ecs/cmd/root.go index 2ca90b5..a3e1534 100644 --- a/cmd/animations_ecs/cmd/root.go +++ b/cmd/animations_ecs/cmd/root.go @@ -203,7 +203,7 @@ type Game struct { } func (g *Game) Draw(screen *ebiten.Image) { - systems.RenderSystem(g.world.World, screen) + systems.RenderSystem(g.world.World, nil, false, g.rotation) } // func (g *Game) DrawBuildingInfo(screen *ebiten.Image) { diff --git a/cmd/island_ecs/cmd/root.go b/cmd/island_ecs/cmd/root.go index f940a3b..1d87259 100644 --- a/cmd/island_ecs/cmd/root.go +++ b/cmd/island_ecs/cmd/root.go @@ -17,14 +17,11 @@ package cmd import ( "fmt" - "image/color" - "log" "os" "path/filepath" "time" - "github.com/hajimehoshi/ebiten/v2" - "github.com/hajimehoshi/ebiten/v2/text" + rl "github.com/gen2brain/raylib-go/raylib" "github.com/siredmar/mdcii-engine/pkg/bsh" "github.com/siredmar/mdcii-engine/pkg/cod" buildingsCod "github.com/siredmar/mdcii-engine/pkg/cod/buildings" @@ -33,11 +30,12 @@ import ( "github.com/siredmar/mdcii-engine/pkg/ecs/world" "github.com/siredmar/mdcii-engine/pkg/files" "github.com/siredmar/mdcii-engine/pkg/gam" + r3d "github.com/siredmar/mdcii-engine/pkg/renderer/raylib" animations "github.com/siredmar/mdcii-engine/pkg/texture/animations" "github.com/siredmar/mdcii-engine/pkg/texture/atlas" "github.com/siredmar/mdcii-engine/pkg/world/rotation" + "github.com/siredmar/mdcii-engine/pkg/world/zoom" "github.com/spf13/cobra" - "golang.org/x/image/font/basicfont" donburi "github.com/yohamta/donburi" "github.com/yohamta/donburi/filter" @@ -116,6 +114,12 @@ var rootCmd = &cobra.Command{ fmt.Println("Error:", err) return } + + rl.InitWindow(int32(ScreenWidth), int32(ScreenHeight), "animations") + rl.SetTargetFPS(60) + renderer := r3d.NewRenderer(float32(zoom.TileSize())) + defer rl.CloseWindow() + ani, err := animations.New(atlas) if err != nil { fmt.Println("Error:", err) @@ -140,9 +144,6 @@ var rootCmd = &cobra.Command{ os.Exit(1) } - ebiten.SetWindowSize(ScreenWidth, ScreenHeight) - ebiten.SetWindowTitle("animations") - w := world.New() // Create an entity and get its Entry w.World.Create(components.AnimationType, components.TileType, components.PositionType, components.BuildingType, components.IslandType) @@ -178,16 +179,13 @@ var rootCmd = &cobra.Command{ // } game := &Game{ - world: w, - animations: ani, - // animation: nil, + world: w, + animations: ani, + renderer: renderer, buildingIndex: buildingIndex, - // buildingId: id, - // count: 0, - buildings: buildings, - rotation: rotation.Rotation(rotationArg), - // entry: entry, - grid: true, + buildings: buildings, + rotation: rotation.Rotation(rotationArg), + grid: true, } // components.BuildingType.Set(entry, &components.Building{ @@ -219,8 +217,9 @@ var rootCmd = &cobra.Command{ // return // } // game.animation = game.animations.GetAnimation(buildingParam, rotation.DEG0) - if err := ebiten.RunGame(game); err != nil { - log.Fatal(err) + for !rl.WindowShouldClose() { + game.Update() + game.Draw() } }, } @@ -234,21 +233,16 @@ func Execute() { } type Game struct { - world *world.World - animations *animations.Animations - ScreenWidth int - ScreenHeight int - // lastKeyPressTime time.Time - // buildingId int + world *world.World + animations *animations.Animations + renderer *r3d.Renderer buildingIndex int rotation rotation.Rotation - // lastTime time.Time - // entry *donburi.Entry - buildings *buildingsCod.Buildings - grid bool + buildings *buildingsCod.Buildings + grid bool } -func (g *Game) Draw(screen *ebiten.Image) { +func (g *Game) Draw() { var rot rotation.Rotation var grid bool @@ -258,9 +252,13 @@ func (g *Game) Draw(screen *ebiten.Image) { rot = ctrl.Rotation grid = ctrl.GridVisible }) - systems.RenderSystem(g.world.World, screen, grid, rot) - // systems.MouseSelectorSystem(g.world.World) // Add the mouse selector system - // systems.RenderSystemAscii(g.world.World) + + rl.BeginDrawing() + rl.ClearBackground(rl.Black) + systems.RenderSystem(g.world.World, g.renderer, grid, rot) + g.DrawUsage() + rl.EndDrawing() + // systems.MouseSelectorSystem(g.world.World) } // func (g *Game) DrawBuildingInfo(screen *ebiten.Image) { @@ -278,44 +276,11 @@ func (g *Game) Draw(screen *ebiten.Image) { // // text.Draw(screen, fmt.Sprintf("CurrentAnimationStep: %d", g.Building.CurrentAnimationStep), face, 10, 80, textColor) // } -func (g *Game) DrawUsage(screen *ebiten.Image) { - textColor := color.RGBA{255, 255, 255, 255} - face := basicfont.Face7x13 - - text.Draw(screen, "Up: Animation Step, Left/Right: Rotate, N: next, M: previous", face, 10, ScreenHeight-20, textColor) - +func (g *Game) DrawUsage() { + rl.DrawText("Up: Animation Step, Left/Right: Rotate, N: next, M: previous", 10, int32(ScreenHeight-20), 10, rl.White) } -func (g *Game) Update() error { +func (g *Game) Update() { systems.AnimationSystem(g.world.World, g.animations, 1.0/60.0) systems.InputSystem(g.world.World) - return nil - // const debounceDuration = time.Millisecond * 250 - // now := time.Now() - - // if now.Sub(g.lastKeyPressTime) >= debounceDuration { - // if ebiten.IsKeyPressed(ebiten.KeyLeft) { - // fmt.Println(int(g.rotation)) - // g.rotation.Increment() - // fmt.Println(int(g.rotation)) - // g.lastKeyPressTime = now - - // } else if ebiten.IsKeyPressed(ebiten.KeyRight) { - // fmt.Println(int(g.rotation)) - // g.rotation.Decrement() - // fmt.Println(int(g.rotation)) - // g.lastKeyPressTime = now - // } else if ebiten.IsKeyPressed(ebiten.KeyG) { - // g.grid = !g.grid - // g.lastKeyPressTime = now - // } else if ebiten.IsKeyPressed(ebiten.KeyEscape) { - // os.Exit(0) - // } - // } - // g.lastTime = now - // return nil -} - -func (g *Game) Layout(outsideWidth, outsideHeight int) (int, int) { - return ScreenWidth, ScreenHeight } diff --git a/pkg/ecs/components/island.go b/pkg/ecs/components/island.go index 9afa557..6024403 100644 --- a/pkg/ecs/components/island.go +++ b/pkg/ecs/components/island.go @@ -4,6 +4,7 @@ import ( "encoding/json" "fmt" + rl "github.com/gen2brain/raylib-go/raylib" "github.com/siredmar/mdcii-engine/pkg/building" island5 "github.com/siredmar/mdcii-engine/pkg/chunks" "github.com/siredmar/mdcii-engine/pkg/cod/buildings" @@ -248,7 +249,7 @@ func CreateIslandFromChunk(world donburi.World, ani *animations.Animations, i *i PositionType.Set(occupyEntry, p) TileType.Set(occupyEntry, &Tile{ Size: Size{Width: 1, Height: 1, Z: size.H - zoom.TileHeight()}, - Image: nil, + Image: rl.Texture2D{}, Occupation: true, }) diff --git a/pkg/ecs/components/tile.go b/pkg/ecs/components/tile.go index 6e637fc..066ace6 100644 --- a/pkg/ecs/components/tile.go +++ b/pkg/ecs/components/tile.go @@ -1,7 +1,7 @@ package components import ( - "github.com/hajimehoshi/ebiten/v2" + rl "github.com/gen2brain/raylib-go/raylib" "github.com/yohamta/donburi" ) @@ -12,7 +12,7 @@ type Size struct { } type Tile struct { - Image *ebiten.Image // Placeholder for current frame + Image rl.Texture2D // Placeholder for current frame Size Size Occupation bool // Parent *donburi.Entry diff --git a/pkg/ecs/systems/input.go b/pkg/ecs/systems/input.go index 23eef24..0ef9e08 100644 --- a/pkg/ecs/systems/input.go +++ b/pkg/ecs/systems/input.go @@ -5,8 +5,9 @@ import ( "os" "time" - "github.com/hajimehoshi/ebiten/v2" + rl "github.com/gen2brain/raylib-go/raylib" "github.com/siredmar/mdcii-engine/pkg/ecs/components" + "github.com/siredmar/mdcii-engine/pkg/world/zoom" "github.com/yohamta/donburi" "github.com/yohamta/donburi/filter" ) @@ -16,7 +17,7 @@ var cameraQuery = donburi.NewQuery(filter.Contains(components.CameraType)) func InputSystem(world donburi.World) { const debounce = time.Millisecond * 250 - const cameraSpeed = 10.0 + const cameraSpeed = 0.1 // 🔁 Handle control input (rotation, toggles, etc.) inputQuery.Each(world, func(entry *donburi.Entry) { @@ -24,29 +25,31 @@ func InputSystem(world donburi.World) { now := time.Now() if now.Sub(ctrl.LastKeyPressTime) >= debounce { - if ebiten.IsKeyPressed(ebiten.KeyQ) { + if rl.IsKeyDown(rl.KeyQ) { ctrl.Rotation.Increment() fmt.Println("Rotation Incremented:", ctrl.Rotation) ctrl.LastKeyPressTime = now } - if ebiten.IsKeyPressed(ebiten.KeyE) { + if rl.IsKeyDown(rl.KeyE) { ctrl.Rotation.Decrement() fmt.Println("Rotation Decremented:", ctrl.Rotation) ctrl.LastKeyPressTime = now } - if ebiten.IsKeyPressed(ebiten.KeyG) { + if rl.IsKeyDown(rl.KeyG) { ctrl.GridVisible = !ctrl.GridVisible ctrl.LastKeyPressTime = now } - if ebiten.IsKeyPressed(ebiten.KeyEscape) { + if rl.IsKeyDown(rl.KeyEscape) { os.Exit(0) } } // 🖱️ Right mouse drag for scrolling - mouseX, mouseY := ebiten.CursorPosition() + mouse := rl.GetMousePosition() + mouseX := int(mouse.X) + mouseY := int(mouse.Y) - if ebiten.IsMouseButtonPressed(ebiten.MouseButtonRight) { + if rl.IsMouseButtonDown(rl.MouseButtonRight) { if !ctrl.Dragging { // Start drag ctrl.Dragging = true @@ -59,8 +62,8 @@ func InputSystem(world donburi.World) { cameraQuery.Each(world, func(camEntry *donburi.Entry) { cam := components.CameraType.Get(camEntry) - cam.X -= dx - cam.Y -= dy + cam.X -= dx / float64(zoom.TileSize()) + cam.Y -= dy / float64(zoom.TileSize()) }) // Update last position @@ -76,16 +79,16 @@ func InputSystem(world donburi.World) { cameraQuery.Each(world, func(entry *donburi.Entry) { cam := components.CameraType.Get(entry) - if ebiten.IsKeyPressed(ebiten.KeyW) || ebiten.IsKeyPressed(ebiten.KeyArrowUp) { + if rl.IsKeyDown(rl.KeyW) || rl.IsKeyDown(rl.KeyUp) { cam.Y -= cameraSpeed } - if ebiten.IsKeyPressed(ebiten.KeyS) || ebiten.IsKeyPressed(ebiten.KeyArrowDown) { + if rl.IsKeyDown(rl.KeyS) || rl.IsKeyDown(rl.KeyDown) { cam.Y += cameraSpeed } - if ebiten.IsKeyPressed(ebiten.KeyA) || ebiten.IsKeyPressed(ebiten.KeyArrowLeft) { + if rl.IsKeyDown(rl.KeyA) || rl.IsKeyDown(rl.KeyLeft) { cam.X -= cameraSpeed } - if ebiten.IsKeyPressed(ebiten.KeyD) || ebiten.IsKeyPressed(ebiten.KeyArrowRight) { + if rl.IsKeyDown(rl.KeyD) || rl.IsKeyDown(rl.KeyRight) { cam.X += cameraSpeed } }) diff --git a/pkg/ecs/systems/mouseSelector.go b/pkg/ecs/systems/mouseSelector.go index 6aca298..5723c47 100644 --- a/pkg/ecs/systems/mouseSelector.go +++ b/pkg/ecs/systems/mouseSelector.go @@ -3,7 +3,7 @@ package systems import ( "fmt" - "github.com/hajimehoshi/ebiten/v2" + rl "github.com/gen2brain/raylib-go/raylib" "github.com/siredmar/mdcii-engine/pkg/ecs/components" "github.com/siredmar/mdcii-engine/pkg/world/zoom" "github.com/yohamta/donburi" @@ -21,7 +21,8 @@ func MouseSelectorSystem(world donburi.World) { tileHeight := zoom.TileHeight() // Tile height in pixels // Get the mouse position - mouseX, mouseY := ebiten.CursorPosition() + mouse := rl.GetMousePosition() + mouseX, mouseY := int(mouse.X), int(mouse.Y) // Reverse isometric projection to get grid coordinates gridX, gridY := getMouseTilePosition(mouseX, mouseY, tileWidth, tileHeight) diff --git a/pkg/ecs/systems/renderer.go b/pkg/ecs/systems/renderer.go index c5c6995..edb6e7d 100644 --- a/pkg/ecs/systems/renderer.go +++ b/pkg/ecs/systems/renderer.go @@ -1,149 +1,45 @@ package systems import ( - "bytes" - _ "embed" - "image" - "image/png" - "log" - "sort" - - "github.com/hajimehoshi/ebiten/v2" + rl "github.com/gen2brain/raylib-go/raylib" "github.com/siredmar/mdcii-engine/pkg/building" "github.com/siredmar/mdcii-engine/pkg/cod/buildings" "github.com/siredmar/mdcii-engine/pkg/ecs/components" + r3d "github.com/siredmar/mdcii-engine/pkg/renderer/raylib" "github.com/siredmar/mdcii-engine/pkg/world/rotation" "github.com/siredmar/mdcii-engine/pkg/world/zoom" "github.com/yohamta/donburi" "github.com/yohamta/donburi/filter" ) -// Embed optional debug tiles -// -//go:embed assets/gfx/0.png -var grid0Bytes []byte - -var grid0 = createImage(grid0Bytes) - -func createImage(data []byte) *ebiten.Image { - img, err := png.Decode(bytes.NewReader(data)) - if err != nil { - log.Fatalf("Failed to decode PNG: %v", err) - } - - rgbaImg := image.NewRGBA(img.Bounds()) - drawer := image.NewRGBA(img.Bounds()) - for y := img.Bounds().Min.Y; y < img.Bounds().Max.Y; y++ { - for x := img.Bounds().Min.X; x < img.Bounds().Max.X; x++ { - drawer.Set(x, y, img.At(x, y)) - } - } - copy(rgbaImg.Pix, drawer.Pix) - - return ebiten.NewImageFromImage(rgbaImg) -} - -const ( - TILE_WIDTH = 64 - TILE_HEIGHT = 32 -) - -// // Alignment offsets for multi-tile buildings -// -// var AlignmentMap = map[building.BuildingSizeIdentifier][2]float64{ -// building.BuildingSize2x3: {-32 * 2, (32.0 / 2) * 3}, -// building.BuildingSize2x2: {-32, (32.0 / 2) * 2}, -// building.BuildingSize1x2: {-32 / 2, 32}, -// building.BuildingSize2x1: {-32 * 1, 32.0 / 2}, -// building.BuildingSize4x3: {-32 * 3, (32.0 / 2) * 5}, -// } -var AlignmentMaps = map[rotation.Rotation]map[building.BuildingSizeIdentifier][2]float64{ - rotation.DEG0: { - building.BuildingSize2x2: {-32, 32}, - building.BuildingSize2x3: {-64, 48}, - building.BuildingSize1x2: {-16, 32}, - building.BuildingSize2x1: {-32, 16}, - building.BuildingSize4x3: {-96, 80}, - }, - rotation.DEG90: { - building.BuildingSize2x2: {-32, 32}, - building.BuildingSize2x3: {-48, 64}, - building.BuildingSize1x2: {-16, 32}, - building.BuildingSize2x1: {-32, 16}, - building.BuildingSize4x3: {-80, 96}, - }, - rotation.DEG180: { - building.BuildingSize2x2: {-32, 32}, - building.BuildingSize2x3: {-64, 48}, - building.BuildingSize1x2: {-16, 32}, - building.BuildingSize2x1: {-32, 16}, - building.BuildingSize4x3: {-96, 80}, - }, - rotation.DEG270: { - building.BuildingSize2x2: {-32, 32}, - building.BuildingSize2x3: {-48, 64}, - building.BuildingSize1x2: {-16, 32}, - building.BuildingSize2x1: {-32, 16}, - building.BuildingSize4x3: {-80, 96}, - }, -} - -// // AlignmentOffset returns the pixel offset to correctly align multi-tile buildings -// func AlignmentOffset(size building.BuildingSizeIdentifier, rot rotation.Rotation) (float64, float64) { -// w, h := size.Width(), size.Height() - -// // Anchor offset in tile-space: bottom-left (0-indexed) -// anchorTileX := 0 -// anchorTileY := h - 1 - -// // Compute how far the anchor is from top-left (0,0) of the building -// offsetTileX := -anchorTileX -// offsetTileY := -anchorTileY - -// // Rotate that offset in tile-space -// rotatedX, rotatedY := rotation.RotatePosition( -// offsetTileX, offsetTileY, -// w, h, -// rot, -// ) - -// // Convert to isometric pixel-space -// pixelX := (float64(rotatedX) - float64(rotatedY)) * (TILE_WIDTH / 2) -// pixelY := (float64(rotatedX) + float64(rotatedY)) * (TILE_HEIGHT / 2) - -// return pixelX, pixelY -// } - -// Renderer query var rendererQuery = donburi.NewQuery( filter.Contains(components.IslandType), ) -// A renderable tile or building piece -type RenderableTile struct { - isoX, isoY float64 - Z float64 - topX, topY int - Image *ebiten.Image -} - -func RenderSystem(world donburi.World, screen *ebiten.Image, grid bool, currentRotation rotation.Rotation) { - tileWidth := zoom.TileSize() - tileHeight := zoom.TileHeight() - - var renderableTiles []RenderableTile +// RenderSystem draws all tiles in 3D using raylib. +func RenderSystem(world donburi.World, r *r3d.Renderer, grid bool, currentRotation rotation.Rotation) { + if r == nil { + return + } - // 👉 Fetch camera (added) - var camera *components.Camera - cameraQuery := donburi.NewQuery(filter.Contains(components.CameraType)) - cameraQuery.Each(world, func(entry *donburi.Entry) { - camera = components.CameraType.Get(entry) + var camComp *components.Camera + camQuery := donburi.NewQuery(filter.Contains(components.CameraType)) + camQuery.Each(world, func(entry *donburi.Entry) { + camComp = components.CameraType.Get(entry) }) - if camera == nil { - log.Println("No camera entity found") + if camComp == nil { return } + // Update camera based on component + r.PPU = float32(zoom.TileSize()) + r.OnResize() + offset := rl.Vector3Subtract(r.Camera.Position, r.Camera.Target) + r.Camera.Target = rl.NewVector3(float32(camComp.X), 0, float32(camComp.Y)) + r.Camera.Position = rl.Vector3Add(r.Camera.Target, offset) + + r.Begin() + rendererQuery.Each(world, func(entry *donburi.Entry) { island := components.IslandType.Get(entry) @@ -157,68 +53,33 @@ func RenderSystem(world donburi.World, screen *ebiten.Image, grid bool, currentR for _, tileEntry := range island.Tiles[layerID] { pos := components.PositionType.Get(tileEntry) tile := components.TileType.Get(tileEntry) - building := components.BuildingType.Get(tileEntry) + b := components.BuildingType.Get(tileEntry) - if tile.Image == nil { + if tile.Image.ID == 0 { continue } - // 🔁 Apply rotation to world position rotatedX, rotatedY := rotation.RotatePosition(int(pos.X), int(pos.Y), island.Width, island.Height, currentRotation) - // 📐 Isometric projection - isoX := ((float64(rotatedX)-island.X)-(float64(rotatedY)-island.Y))*(float64(tileWidth)/2) + float64(island.X)*(float64(tileWidth)/2) - isoY := ((float64(rotatedX)-island.X)+(float64(rotatedY)-island.Y))*(float64(tileHeight)/2) + float64(island.Y)*(float64(tileHeight)/2) - isoY -= pos.Offset - - // ⬇ Adjust image height for visual stacking - tileImageHeight := float64(tile.Image.Bounds().Dy()) - isoY -= tileImageHeight - float64(tileHeight) - - // 🧭 Alignment offset (rotation-specific) - if rotationMap, ok := AlignmentMaps[currentRotation]; ok { - if offset, ok := rotationMap[building.Size]; ok { - isoX += offset[0] - isoY += offset[1] - } - } + centerX := float32(rotatedX) + float32(b.Size.Width())/2 + centerZ := float32(rotatedY) + float32(b.Size.Height())/2 - // 📏 Visual depth - visualZ := tileImageHeight / float64(tileHeight) - - renderableTiles = append(renderableTiles, RenderableTile{ - isoX: isoX, - isoY: isoY, - topX: rotatedX, - topY: rotatedY, - Z: visualZ, - Image: tile.Image, - }) - } - } - }) + w := float32(tile.Image.Width) / r.PPU + h := float32(tile.Image.Height) / r.PPU - // 🔄 Depth sort for correct layering - sort.Slice(renderableTiles, func(i, j int) bool { - depthI := renderableTiles[i].topX + renderableTiles[i].topY + int(renderableTiles[i].Z) - depthJ := renderableTiles[j].topX + renderableTiles[j].topY + int(renderableTiles[j].Z) - return depthI < depthJ - }) + posY := h/2 - float32(pos.Offset)/r.PPU + position := rl.NewVector3(centerX, posY, centerZ) + size := rl.NewVector2(w, h) + src := rl.NewRectangle(0, 0, float32(tile.Image.Width), float32(tile.Image.Height)) - // 🎨 Draw everything, applying camera offset - for _, tile := range renderableTiles { - if tile.Image != nil { - op := &ebiten.DrawImageOptions{} - op.GeoM.Translate(tile.isoX-camera.X, tile.isoY-camera.Y) - screen.DrawImage(tile.Image, op) + rl.DrawBillboardRec(r.Camera, tile.Image, src, position, size, rl.White) + } } - } + }) if grid { - renderDebugGrid(world, screen, float64(tileWidth), float64(tileHeight)) + // TODO: implement grid rendering if needed } -} -func renderDebugGrid(world donburi.World, screen *ebiten.Image, tileWidth, tileHeight float64) { - // Implement if needed + r.End() } diff --git a/pkg/texture/animations/animations.go b/pkg/texture/animations/animations.go index caca24d..2ea1736 100644 --- a/pkg/texture/animations/animations.go +++ b/pkg/texture/animations/animations.go @@ -2,8 +2,8 @@ package animation import ( "errors" - "image" + rl "github.com/gen2brain/raylib-go/raylib" "github.com/siredmar/mdcii-engine/pkg/texture/atlas" "github.com/siredmar/mdcii-engine/pkg/world/rotation" ) @@ -13,7 +13,7 @@ type Animations struct { } type Animation struct { - Frames []image.Image + Frames []rl.Texture2D Steps int Animated bool FrameDuration int @@ -39,10 +39,13 @@ func New(atlas *atlas.TextureAtlas) (*Animations, error) { for _, rot := range rotation.AllRotations { animation := imageSetForRotation.Animations[rot] a.Animations[buildingId][rot] = &Animation{ - Frames: func() []image.Image { - out := []image.Image{} + Frames: func() []rl.Texture2D { + out := []rl.Texture2D{} for _, img := range animation.Images { - out = append(out, img.Sprite) + rlImg := rl.NewImageFromImage(img.Sprite) + tex := rl.LoadTextureFromImage(rlImg) + rl.UnloadImage(rlImg) + out = append(out, tex) } return out }(),