From e607daa47f4e4105134ea90c77e6791034fe85a9 Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Sat, 9 May 2026 06:30:13 +0000 Subject: [PATCH] =?UTF-8?q?=F0=9F=9A=80=20Turbo:=20Optimize=20WorldScene?= =?UTF-8?q?=20store=20subscription?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Optimized the `WorldScene.ts` store subscription by refactoring the `useGameStore.subscribe` callback. Implemented early returns and granular reference checks for settlements to avoid redundant rendering and expensive O(N) calculations. Replaced full-unit-list flattening with targeted `selectUnitById` calls to track selection state changes efficiently. These changes significantly reduce the overhead on the main thread during state updates. --- src/client/scenes/WorldScene.ts | 61 +++++++++++++++++---------------- 1 file changed, 31 insertions(+), 30 deletions(-) diff --git a/src/client/scenes/WorldScene.ts b/src/client/scenes/WorldScene.ts index 05c4e89..748f266 100644 --- a/src/client/scenes/WorldScene.ts +++ b/src/client/scenes/WorldScene.ts @@ -2,7 +2,7 @@ import * as Phaser from 'phaser'; import { TileMap } from '../game/map/TileMap'; import { TerrainRenderer } from '../game/map/TerrainRenderer'; import { SpriteLoader } from '../game/utils/SpriteLoader'; -import { getReachableTilesForUnit, useGameStore } from '@client/game/state/gameStore'; +import { getReachableTilesForUnit, useGameStore, selectUnitById } from '@client/game/state/gameStore'; import { eventBus } from '@client/game/state/EventBus'; import { MAP_CONSTANTS } from '@shared/game/constants'; import { UnitRenderer } from '../game/map/UnitRenderer'; @@ -59,41 +59,42 @@ export class WorldScene extends Phaser.Scene { this.storeUnsubscribe = useGameStore.subscribe((state, prevState) => { if (!this.scene.isActive('WorldScene')) return; - const playerSettlements = state.players.flatMap(p => p.settlements); - const prevPlayerSettlements = prevState.players.flatMap(p => p.settlements); - const playerUnits = state.players.flatMap(p => p.units); - const prevPlayerUnits = prevState.players.flatMap(p => p.units); - - // ⚡ Turbo: Skip expensive terrain rendering if nothing relevant changed - if ( - state.map !== prevState.map || - playerSettlements.length !== prevPlayerSettlements.length || - !playerSettlements.every((c, i) => c === prevPlayerSettlements[i]) - ) { + const mapChanged = state.map !== prevState.map; + const playersChanged = state.players !== prevState.players; + const selectionChanged = state.selectedUnitId !== prevState.selectedUnitId; + + if (!mapChanged && !playersChanged && !selectionChanged) return; + + // 1. Terrain Rendering - Skip if nothing relevant changed + // We check for map changes or settlement changes (via player array or settlement list references) + const settlementsChanged = playersChanged && ( + state.players.length !== prevState.players.length || + state.players.some((p, i) => p.settlements !== prevState.players[i]?.settlements) + ); + + if (mapChanged || settlementsChanged) { + const playerSettlements = state.players.flatMap(p => p.settlements); this.terrainRenderer.renderTileMap(state.map, [], playerSettlements); } - // ⚡ Turbo: Skip expensive unit rendering if no units or selections changed - if ( - state.players !== prevState.players || - state.selectedUnitId !== prevState.selectedUnitId - ) { + // 2. Unit Rendering - Skip if no units or selections changed + if (playersChanged || selectionChanged) { this.unitRenderer.render(state.players, state.selectedUnitId); } - // ⚡ Turbo: Skip pathfinding if selected unit or map hasn't changed - if ( - state.selectedUnitId !== prevState.selectedUnitId || - playerUnits.find(u => u.id === state.selectedUnitId) !== prevPlayerUnits.find(u => u.id === state.selectedUnitId) || - state.map !== prevState.map - ) { - const selectedUnit = playerUnits.find((u) => u.id === state.selectedUnitId); - if (selectedUnit) { - this.reachableTiles = getReachableTilesForUnit(selectedUnit, state.map); - this.terrainRenderer.updateReachableHighlights(this.reachableTiles); - } else { - this.reachableTiles = []; - this.terrainRenderer.clearReachableHighlights(); + // 3. Pathfinding / Reachable Highlights - Skip if selected unit or map hasn't changed + if (selectionChanged || playersChanged || mapChanged) { + const selectedUnit = selectUnitById(state, state.selectedUnitId); + const prevSelectedUnit = selectUnitById(prevState, prevState.selectedUnitId); + + if (selectedUnit !== prevSelectedUnit || mapChanged) { + if (selectedUnit) { + this.reachableTiles = getReachableTilesForUnit(selectedUnit, state.map); + this.terrainRenderer.updateReachableHighlights(this.reachableTiles); + } else { + this.reachableTiles = []; + this.terrainRenderer.clearReachableHighlights(); + } } } });