From fb034b9b93f8a17556d331d9ab6767395ab97e92 Mon Sep 17 00:00:00 2001 From: Atridad Lahiji Date: Tue, 25 Nov 2025 11:59:59 -0700 Subject: [PATCH] More optimizations --- internal/game/game.go | 15 +++++- internal/maps/manager.go | 56 +++++++++++++++++++++ internal/maps/map.go | 35 ++++++++++++++ internal/screens/gameplay.go | 94 +++++++++++++++++------------------- 4 files changed, 148 insertions(+), 52 deletions(-) create mode 100644 internal/maps/manager.go diff --git a/internal/game/game.go b/internal/game/game.go index 6c9c83d..6519c82 100644 --- a/internal/game/game.go +++ b/internal/game/game.go @@ -9,6 +9,7 @@ import ( "github.com/atridad/LilGuy/internal/save" "github.com/atridad/LilGuy/internal/screens" "github.com/atridad/LilGuy/internal/ui/menu" + "github.com/atridad/LilGuy/internal/maps" ) const ( @@ -149,10 +150,22 @@ func newState() *state { } } + // Initialize Map Manager + mapManager := maps.NewManager() + plains, desert := maps.CreateDefaultMaps(float64(ScreenWidth), float64(ScreenHeight)) + + // Bake maps for performance + plains.Bake() + desert.Bake() + + mapManager.RegisterMap(plains) + mapManager.RegisterMap(desert) + mapManager.SetCurrentMap("plains") + // Initialize screens s.splashScreen = screens.NewSplashScreen() s.titleScreen = screens.NewTitleScreen() - s.gameplayScreen = screens.NewGameplayScreen(ScreenWidth, ScreenHeight, &s.fpsEnabled, &s.portalVisibility) + s.gameplayScreen = screens.NewGameplayScreen(ScreenWidth, ScreenHeight, mapManager, &s.fpsEnabled, &s.portalVisibility) s.pauseMenu = menu.NewPauseMenu() // Wire up settings references diff --git a/internal/maps/manager.go b/internal/maps/manager.go new file mode 100644 index 0000000..ba217a1 --- /dev/null +++ b/internal/maps/manager.go @@ -0,0 +1,56 @@ +package maps + +import ( + "fmt" + + "github.com/atridad/LilGuy/internal/portal" +) + +type Manager struct { + maps map[string]*Map + currentMap *Map +} + +func NewManager() *Manager { + return &Manager{ + maps: make(map[string]*Map), + } +} + +func (m *Manager) RegisterMap(mp *Map) { + m.maps[mp.ID] = mp +} + +func (m *Manager) GetMap(id string) *Map { + return m.maps[id] +} + +func (m *Manager) SetCurrentMap(id string) error { + mp, ok := m.maps[id] + if !ok { + return fmt.Errorf("map not found: %s", id) + } + m.currentMap = mp + return nil +} + +func (m *Manager) CurrentMap() *Map { + return m.currentMap +} + +func (m *Manager) ResolveTransition(event portal.TransitionEvent) (*Map, *portal.Portal) { + destMap := m.GetMap(event.DestinationMap) + if destMap == nil { + return nil, nil + } + + destPortal := destMap.GetPortalByID(event.DestinationPortal) + return destMap, destPortal +} + +func (m *Manager) Reset() { + for _, mp := range m.maps { + mp.Reset() + } + // Reset to default map if needed, or let the caller do it +} diff --git a/internal/maps/map.go b/internal/maps/map.go index 8062b22..3e14abe 100644 --- a/internal/maps/map.go +++ b/internal/maps/map.go @@ -3,6 +3,7 @@ package maps import ( "image/color" + "github.com/hajimehoshi/ebiten/v2" "github.com/atridad/LilGuy/internal/portal" "github.com/atridad/LilGuy/internal/world" ) @@ -16,6 +17,8 @@ type Map struct { World *world.World Portals []*portal.Portal BackgroundColor color.NRGBA + + bakedImage *ebiten.Image } func NewMap(id string, number int, displayName string, width, height float64) *Map { @@ -44,6 +47,38 @@ func (m *Map) GetPortalByID(id string) *portal.Portal { return nil } +// Renders the static world geometry to an offscreen image +func (m *Map) Bake() { + if m.Width <= 0 || m.Height <= 0 { + return + } + m.bakedImage = ebiten.NewImage(int(m.Width), int(m.Height)) + m.bakedImage.Fill(m.BackgroundColor) + m.World.Draw(m.bakedImage) +} + +func (m *Map) Draw(screen *ebiten.Image) { + if m.bakedImage != nil { + screen.DrawImage(m.bakedImage, nil) + } else { + screen.Fill(m.BackgroundColor) + m.World.Draw(screen) + } + + // Draw portals + for _, p := range m.Portals { + p.Draw(screen) + } +} + +// Resets the map state +func (m *Map) Reset() { + for _, p := range m.Portals { + p.Enabled = true + p.Visible = true + } +} + func CreateDefaultMaps(screenWidth, screenHeight float64) (*Map, *Map) { plains := CreatePlains(screenWidth, screenHeight) desert := CreateDesert(screenWidth, screenHeight) diff --git a/internal/screens/gameplay.go b/internal/screens/gameplay.go index 7732e60..8943919 100644 --- a/internal/screens/gameplay.go +++ b/internal/screens/gameplay.go @@ -34,6 +34,7 @@ type GameplayScreen struct { world *world.World projectiles *projectile.Manager portals *portal.Manager + mapManager *maps.Manager bounds hero.Bounds lastTick time.Time gameStartTime time.Time @@ -47,22 +48,19 @@ type GameplayScreen struct { showSaveNotification bool portalVisibility *bool - - currentMap *maps.Map - allMaps map[string]*maps.Map } -func NewGameplayScreen(screenWidth, screenHeight int, fpsEnabled *bool, portalVisibility *bool) *GameplayScreen { +func NewGameplayScreen(screenWidth, screenHeight int, mapManager *maps.Manager, fpsEnabled *bool, portalVisibility *bool) *GameplayScreen { cfg := config.Default() - plains, desert := maps.CreateDefaultMaps(float64(screenWidth), float64(screenHeight)) - - allMaps := make(map[string]*maps.Map) - allMaps["plains"] = plains - allMaps["desert"] = desert + + // Ensure we have a current map + if mapManager.CurrentMap() == nil { + // Fallback or error later + } + currentMap := mapManager.CurrentMap() portalMgr := portal.NewManager() - - for _, p := range plains.Portals { + for _, p := range currentMap.Portals { portalMgr.AddPortal(p) } @@ -87,11 +85,12 @@ func NewGameplayScreen(screenWidth, screenHeight int, fpsEnabled *bool, portalVi X: cfg.HUD.X, Y: cfg.HUD.Y, Color: color.White, - ScreenName: "Plains", + ScreenName: currentMap.DisplayName, }, - world: plains.World, + world: currentMap.World, projectiles: projectile.NewManager(), portals: portalMgr, + mapManager: mapManager, bounds: hero.Bounds{ Width: float64(screenWidth), Height: float64(screenHeight), @@ -101,8 +100,6 @@ func NewGameplayScreen(screenWidth, screenHeight int, fpsEnabled *bool, portalVi gameStartTime: time.Now(), fpsEnabled: fpsEnabled, portalVisibility: portalVisibility, - currentMap: plains, - allMaps: allMaps, } gs.portals.OnTransition = gs.handlePortalTransition @@ -166,13 +163,13 @@ func (g *GameplayScreen) checkPortalCollision() { } func (g *GameplayScreen) handlePortalTransition(event portal.TransitionEvent) { - destMap, exists := g.allMaps[event.DestinationMap] - if !exists { + destMap, destPortal := g.mapManager.ResolveTransition(event) + if destMap == nil { return } // Switch to destination map - g.currentMap = destMap + g.mapManager.SetCurrentMap(destMap.ID) g.world = destMap.World // Clear and reload portals for new map @@ -190,7 +187,6 @@ func (g *GameplayScreen) handlePortalTransition(event portal.TransitionEvent) { } // Find destination portal and position hero - destPortal := destMap.GetPortalByID(event.DestinationPortal) if destPortal != nil { // Position hero based on which side they're entering from switch destPortal.Side { @@ -229,21 +225,18 @@ func (g *GameplayScreen) trackFPS(delta time.Duration) { // Rendering func (g *GameplayScreen) Draw(screen *ebiten.Image) { cfg := config.Default() - bgColor := cfg.Visual.BackgroundColor - if g.currentMap != nil { - bgColor = g.currentMap.BackgroundColor + + currentMap := g.mapManager.CurrentMap() + if currentMap != nil { + currentMap.Draw(screen) + g.hud.ScreenName = currentMap.DisplayName + } else { + screen.Fill(cfg.Visual.BackgroundColor) } - screen.Fill(bgColor) - g.world.Draw(screen) - g.portals.Draw(screen) g.projectiles.Draw(screen) g.hero.Draw(screen) - if g.currentMap != nil { - g.hud.ScreenName = g.currentMap.DisplayName - } - staminaColor := cfg.Visual.StaminaNormalColor if g.hero.Stamina < g.hero.MaxStamina*config.StaminaLowThreshold { staminaColor = cfg.Visual.StaminaLowColor @@ -326,11 +319,9 @@ func (g *GameplayScreen) Reset() { screenWidth := int(g.bounds.Width) screenHeight := int(g.bounds.Height) - plains, desert := maps.CreateDefaultMaps(float64(screenWidth), float64(screenHeight)) - g.allMaps = make(map[string]*maps.Map) - g.allMaps["plains"] = plains - g.allMaps["desert"] = desert - g.currentMap = plains + g.mapManager.Reset() + g.mapManager.SetCurrentMap("plains") + currentMap := g.mapManager.CurrentMap() g.hero = hero.New(hero.Config{ StartX: cfg.Hero.StartX, @@ -342,10 +333,10 @@ func (g *GameplayScreen) Reset() { StaminaDrain: cfg.Hero.StaminaDrain, StaminaRegen: cfg.Hero.StaminaRegen, }) - g.world = plains.World + g.world = currentMap.World g.projectiles = projectile.NewManager() g.portals = portal.NewManager() - for _, p := range plains.Portals { + for _, p := range currentMap.Portals { g.portals.AddPortal(p) } g.portals.OnTransition = g.handlePortalTransition @@ -372,9 +363,9 @@ func (g *GameplayScreen) Reset() { } func (g *GameplayScreen) SaveState() *save.GameState { - currentMapID := "map1" - if g.currentMap != nil { - currentMapID = g.currentMap.ID + currentMapID := "plains" + if g.mapManager.CurrentMap() != nil { + currentMapID = g.mapManager.CurrentMap().ID } return &save.GameState{ CurrentMap: currentMapID, @@ -390,17 +381,18 @@ func (g *GameplayScreen) LoadState(state *save.GameState) { screenWidth := int(g.bounds.Width) screenHeight := int(g.bounds.Height) - plains, desert := maps.CreateDefaultMaps(float64(screenWidth), float64(screenHeight)) - g.allMaps = make(map[string]*maps.Map) - g.allMaps["plains"] = plains - g.allMaps["desert"] = desert - - // load the saved map or default to plains - savedMap := g.allMaps[state.CurrentMap] - if savedMap == nil { - savedMap = plains + g.mapManager.Reset() + + mapID := state.CurrentMap + if mapID == "" { + mapID = "plains" } - g.currentMap = savedMap + + if err := g.mapManager.SetCurrentMap(mapID); err != nil { + // Fallback to plains if map not found + g.mapManager.SetCurrentMap("plains") + } + currentMap := g.mapManager.CurrentMap() g.hero = hero.New(hero.Config{ StartX: state.HeroX, @@ -413,10 +405,10 @@ func (g *GameplayScreen) LoadState(state *save.GameState) { StaminaRegen: cfg.Hero.StaminaRegen, }) g.hero.Stamina = state.HeroStamina - g.world = g.currentMap.World + g.world = currentMap.World g.projectiles = projectile.NewManager() g.portals = portal.NewManager() - for _, p := range g.currentMap.Portals { + for _, p := range currentMap.Portals { g.portals.AddPortal(p) } g.portals.OnTransition = g.handlePortalTransition