More optimizations

This commit is contained in:
2025-11-25 11:59:59 -07:00
parent c84ba37353
commit fb034b9b93
4 changed files with 148 additions and 52 deletions

View File

@@ -9,6 +9,7 @@ import (
"github.com/atridad/LilGuy/internal/save" "github.com/atridad/LilGuy/internal/save"
"github.com/atridad/LilGuy/internal/screens" "github.com/atridad/LilGuy/internal/screens"
"github.com/atridad/LilGuy/internal/ui/menu" "github.com/atridad/LilGuy/internal/ui/menu"
"github.com/atridad/LilGuy/internal/maps"
) )
const ( 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 // Initialize screens
s.splashScreen = screens.NewSplashScreen() s.splashScreen = screens.NewSplashScreen()
s.titleScreen = screens.NewTitleScreen() 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() s.pauseMenu = menu.NewPauseMenu()
// Wire up settings references // Wire up settings references

56
internal/maps/manager.go Normal file
View File

@@ -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
}

View File

@@ -3,6 +3,7 @@ package maps
import ( import (
"image/color" "image/color"
"github.com/hajimehoshi/ebiten/v2"
"github.com/atridad/LilGuy/internal/portal" "github.com/atridad/LilGuy/internal/portal"
"github.com/atridad/LilGuy/internal/world" "github.com/atridad/LilGuy/internal/world"
) )
@@ -16,6 +17,8 @@ type Map struct {
World *world.World World *world.World
Portals []*portal.Portal Portals []*portal.Portal
BackgroundColor color.NRGBA BackgroundColor color.NRGBA
bakedImage *ebiten.Image
} }
func NewMap(id string, number int, displayName string, width, height float64) *Map { 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 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) { func CreateDefaultMaps(screenWidth, screenHeight float64) (*Map, *Map) {
plains := CreatePlains(screenWidth, screenHeight) plains := CreatePlains(screenWidth, screenHeight)
desert := CreateDesert(screenWidth, screenHeight) desert := CreateDesert(screenWidth, screenHeight)

View File

@@ -34,6 +34,7 @@ type GameplayScreen struct {
world *world.World world *world.World
projectiles *projectile.Manager projectiles *projectile.Manager
portals *portal.Manager portals *portal.Manager
mapManager *maps.Manager
bounds hero.Bounds bounds hero.Bounds
lastTick time.Time lastTick time.Time
gameStartTime time.Time gameStartTime time.Time
@@ -47,22 +48,19 @@ type GameplayScreen struct {
showSaveNotification bool showSaveNotification bool
portalVisibility *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() cfg := config.Default()
plains, desert := maps.CreateDefaultMaps(float64(screenWidth), float64(screenHeight))
allMaps := make(map[string]*maps.Map) // Ensure we have a current map
allMaps["plains"] = plains if mapManager.CurrentMap() == nil {
allMaps["desert"] = desert // Fallback or error later
}
currentMap := mapManager.CurrentMap()
portalMgr := portal.NewManager() portalMgr := portal.NewManager()
for _, p := range currentMap.Portals {
for _, p := range plains.Portals {
portalMgr.AddPortal(p) portalMgr.AddPortal(p)
} }
@@ -87,11 +85,12 @@ func NewGameplayScreen(screenWidth, screenHeight int, fpsEnabled *bool, portalVi
X: cfg.HUD.X, X: cfg.HUD.X,
Y: cfg.HUD.Y, Y: cfg.HUD.Y,
Color: color.White, Color: color.White,
ScreenName: "Plains", ScreenName: currentMap.DisplayName,
}, },
world: plains.World, world: currentMap.World,
projectiles: projectile.NewManager(), projectiles: projectile.NewManager(),
portals: portalMgr, portals: portalMgr,
mapManager: mapManager,
bounds: hero.Bounds{ bounds: hero.Bounds{
Width: float64(screenWidth), Width: float64(screenWidth),
Height: float64(screenHeight), Height: float64(screenHeight),
@@ -101,8 +100,6 @@ func NewGameplayScreen(screenWidth, screenHeight int, fpsEnabled *bool, portalVi
gameStartTime: time.Now(), gameStartTime: time.Now(),
fpsEnabled: fpsEnabled, fpsEnabled: fpsEnabled,
portalVisibility: portalVisibility, portalVisibility: portalVisibility,
currentMap: plains,
allMaps: allMaps,
} }
gs.portals.OnTransition = gs.handlePortalTransition gs.portals.OnTransition = gs.handlePortalTransition
@@ -166,13 +163,13 @@ func (g *GameplayScreen) checkPortalCollision() {
} }
func (g *GameplayScreen) handlePortalTransition(event portal.TransitionEvent) { func (g *GameplayScreen) handlePortalTransition(event portal.TransitionEvent) {
destMap, exists := g.allMaps[event.DestinationMap] destMap, destPortal := g.mapManager.ResolveTransition(event)
if !exists { if destMap == nil {
return return
} }
// Switch to destination map // Switch to destination map
g.currentMap = destMap g.mapManager.SetCurrentMap(destMap.ID)
g.world = destMap.World g.world = destMap.World
// Clear and reload portals for new map // Clear and reload portals for new map
@@ -190,7 +187,6 @@ func (g *GameplayScreen) handlePortalTransition(event portal.TransitionEvent) {
} }
// Find destination portal and position hero // Find destination portal and position hero
destPortal := destMap.GetPortalByID(event.DestinationPortal)
if destPortal != nil { if destPortal != nil {
// Position hero based on which side they're entering from // Position hero based on which side they're entering from
switch destPortal.Side { switch destPortal.Side {
@@ -229,21 +225,18 @@ func (g *GameplayScreen) trackFPS(delta time.Duration) {
// Rendering // Rendering
func (g *GameplayScreen) Draw(screen *ebiten.Image) { func (g *GameplayScreen) Draw(screen *ebiten.Image) {
cfg := config.Default() cfg := config.Default()
bgColor := cfg.Visual.BackgroundColor
if g.currentMap != nil {
bgColor = g.currentMap.BackgroundColor
}
screen.Fill(bgColor)
g.world.Draw(screen) currentMap := g.mapManager.CurrentMap()
g.portals.Draw(screen) if currentMap != nil {
currentMap.Draw(screen)
g.hud.ScreenName = currentMap.DisplayName
} else {
screen.Fill(cfg.Visual.BackgroundColor)
}
g.projectiles.Draw(screen) g.projectiles.Draw(screen)
g.hero.Draw(screen) g.hero.Draw(screen)
if g.currentMap != nil {
g.hud.ScreenName = g.currentMap.DisplayName
}
staminaColor := cfg.Visual.StaminaNormalColor staminaColor := cfg.Visual.StaminaNormalColor
if g.hero.Stamina < g.hero.MaxStamina*config.StaminaLowThreshold { if g.hero.Stamina < g.hero.MaxStamina*config.StaminaLowThreshold {
staminaColor = cfg.Visual.StaminaLowColor staminaColor = cfg.Visual.StaminaLowColor
@@ -326,11 +319,9 @@ func (g *GameplayScreen) Reset() {
screenWidth := int(g.bounds.Width) screenWidth := int(g.bounds.Width)
screenHeight := int(g.bounds.Height) screenHeight := int(g.bounds.Height)
plains, desert := maps.CreateDefaultMaps(float64(screenWidth), float64(screenHeight)) g.mapManager.Reset()
g.allMaps = make(map[string]*maps.Map) g.mapManager.SetCurrentMap("plains")
g.allMaps["plains"] = plains currentMap := g.mapManager.CurrentMap()
g.allMaps["desert"] = desert
g.currentMap = plains
g.hero = hero.New(hero.Config{ g.hero = hero.New(hero.Config{
StartX: cfg.Hero.StartX, StartX: cfg.Hero.StartX,
@@ -342,10 +333,10 @@ func (g *GameplayScreen) Reset() {
StaminaDrain: cfg.Hero.StaminaDrain, StaminaDrain: cfg.Hero.StaminaDrain,
StaminaRegen: cfg.Hero.StaminaRegen, StaminaRegen: cfg.Hero.StaminaRegen,
}) })
g.world = plains.World g.world = currentMap.World
g.projectiles = projectile.NewManager() g.projectiles = projectile.NewManager()
g.portals = portal.NewManager() g.portals = portal.NewManager()
for _, p := range plains.Portals { for _, p := range currentMap.Portals {
g.portals.AddPortal(p) g.portals.AddPortal(p)
} }
g.portals.OnTransition = g.handlePortalTransition g.portals.OnTransition = g.handlePortalTransition
@@ -372,9 +363,9 @@ func (g *GameplayScreen) Reset() {
} }
func (g *GameplayScreen) SaveState() *save.GameState { func (g *GameplayScreen) SaveState() *save.GameState {
currentMapID := "map1" currentMapID := "plains"
if g.currentMap != nil { if g.mapManager.CurrentMap() != nil {
currentMapID = g.currentMap.ID currentMapID = g.mapManager.CurrentMap().ID
} }
return &save.GameState{ return &save.GameState{
CurrentMap: currentMapID, CurrentMap: currentMapID,
@@ -390,17 +381,18 @@ func (g *GameplayScreen) LoadState(state *save.GameState) {
screenWidth := int(g.bounds.Width) screenWidth := int(g.bounds.Width)
screenHeight := int(g.bounds.Height) screenHeight := int(g.bounds.Height)
plains, desert := maps.CreateDefaultMaps(float64(screenWidth), float64(screenHeight)) g.mapManager.Reset()
g.allMaps = make(map[string]*maps.Map)
g.allMaps["plains"] = plains
g.allMaps["desert"] = desert
// load the saved map or default to plains mapID := state.CurrentMap
savedMap := g.allMaps[state.CurrentMap] if mapID == "" {
if savedMap == nil { mapID = "plains"
savedMap = 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{ g.hero = hero.New(hero.Config{
StartX: state.HeroX, StartX: state.HeroX,
@@ -413,10 +405,10 @@ func (g *GameplayScreen) LoadState(state *save.GameState) {
StaminaRegen: cfg.Hero.StaminaRegen, StaminaRegen: cfg.Hero.StaminaRegen,
}) })
g.hero.Stamina = state.HeroStamina g.hero.Stamina = state.HeroStamina
g.world = g.currentMap.World g.world = currentMap.World
g.projectiles = projectile.NewManager() g.projectiles = projectile.NewManager()
g.portals = portal.NewManager() g.portals = portal.NewManager()
for _, p := range g.currentMap.Portals { for _, p := range currentMap.Portals {
g.portals.AddPortal(p) g.portals.AddPortal(p)
} }
g.portals.OnTransition = g.handlePortalTransition g.portals.OnTransition = g.handlePortalTransition