From 5e4faa34a8cad7c94b3c24d89556fb71c1d5cd3e Mon Sep 17 00:00:00 2001 From: cpojer Date: Fri, 24 Jan 2025 11:01:47 +0900 Subject: [PATCH] Render main map on a single canvas instead of using two with a buffer. GitOrigin-RevId: 94c1e5f02f8b490ed47bfea4c1aeed6ae8230508 --- athena/info/Tile.tsx | 20 +++++++--- athena/lib/indexToSpriteVector.tsx | 2 +- hera/Tiles.tsx | 61 +++++++++--------------------- hera/render/renderFloatingTile.tsx | 2 - 4 files changed, 34 insertions(+), 51 deletions(-) diff --git a/athena/info/Tile.tsx b/athena/info/Tile.tsx index 13f917b0..92bfaed1 100644 --- a/athena/info/Tile.tsx +++ b/athena/info/Tile.tsx @@ -660,6 +660,7 @@ export const DeepSea = new TileInfo( { fallback: Sea, isolated: true }, ); +// Keep in sync with `getFloatingEdgeAnimation`. export const WaterfallModifiers = new Set([ Modifier.RiverFlowsFromTop, Modifier.RiverFlowsFromBottom, @@ -1570,12 +1571,21 @@ export const FloatingWaterEdge = new TileInfo( }, ); -export const getFloatingEdgeAnimation = (modifier: Modifier, biome: Biome) => { +const wallAreaAnimation = { ...SeaAnimation, offset: 1 }; +const areaAnimation = { ...SeaAnimation, offset: 2 }; + +export function getFloatingEdgeAnimation(modifier: Modifier, biome: Biome) { if (biome === Biome.Spaceship) { return null; } - if (WaterfallModifiers.has(modifier)) { + // Keep in sync with `WaterfallModifiers`. + if ( + modifier === Modifier.RiverFlowsFromTop || + modifier === Modifier.RiverFlowsFromBottom || + modifier === Modifier.RiverFlowsFromLeft || + modifier === Modifier.RiverFlowsFromRight + ) { return WaterfallAnimation; } @@ -1585,7 +1595,7 @@ export const getFloatingEdgeAnimation = (modifier: Modifier, biome: Biome) => { modifier === Modifier.LeftWallAreaDecorator || modifier === Modifier.RightWallAreaDecorator ) { - return { ...SeaAnimation, offset: 1 }; + return wallAreaAnimation; } if ( @@ -1594,11 +1604,11 @@ export const getFloatingEdgeAnimation = (modifier: Modifier, biome: Biome) => { modifier === Modifier.BottomLeftAreaDecorator || modifier === Modifier.BottomRightAreaDecorator ) { - return { ...SeaAnimation, offset: 2 }; + return areaAnimation; } return null; -}; +} export type MaybeTileID = number | null | false; diff --git a/athena/lib/indexToSpriteVector.tsx b/athena/lib/indexToSpriteVector.tsx index efcc5501..d18d1ae1 100644 --- a/athena/lib/indexToSpriteVector.tsx +++ b/athena/lib/indexToSpriteVector.tsx @@ -1,6 +1,6 @@ import SpriteVector from '../map/SpriteVector.tsx'; -export default function indexToVector( +export default function indexToSpriteVector( index: number, width: number, ): SpriteVector { diff --git a/hera/Tiles.tsx b/hera/Tiles.tsx index 58e50c20..7c62e072 100644 --- a/hera/Tiles.tsx +++ b/hera/Tiles.tsx @@ -9,6 +9,7 @@ import { } from '@deities/athena/info/Tile.tsx'; import getBiomeStyle from '@deities/athena/lib/getBiomeStyle.tsx'; import getFloatingEdgeModifier from '@deities/athena/lib/getFloatingEdgeModifier.tsx'; +import indexToVector from '@deities/athena/lib/indexToVector.tsx'; import { Biome } from '@deities/athena/map/Biome.tsx'; import vec from '@deities/athena/map/vec.tsx'; import Vector from '@deities/athena/map/Vector.tsx'; @@ -65,16 +66,6 @@ const sprites = { [Biome.Luna]: Tiles6, } as const; -const createCanvas = ( - mapSize: { height: number; width: number }, - size: number, -) => { - const canvas = document.createElement('canvas'); - canvas.height = (mapSize.height + 2) * size; - canvas.width = (mapSize.width + 2) * size; - return canvas; -}; - export default memo(function Tiles({ map, paused, @@ -91,37 +82,28 @@ export default memo(function Tiles({ vision: VisionT; }) { const ref = useRef(null); - const canvasRefs = useRef>([]); + const canvasRef = useRef(null); const isVisible = useVisibilityState(); const hasSprites = useSprites('all'); const { biome } = map.config; const biomeStyle = getBiomeStyle(biome); useLayoutEffect(() => { - if (!hasSprites) { + if (!hasSprites || !canvasRef.current) { return; } - if (!canvasRefs.current.length) { - canvasRefs.current = [ - createCanvas(map.size, size), - createCanvas(map.size, size), - ]; - } - const tileset = { buildings: spriteImage('BuildingsShadow', biome), structures: spriteImage('StructuresShadow', biome), tiles: sprites[biome], }; - const [visibleCanvas, mainCanvas] = canvasRefs.current; - const context = visibleCanvas.getContext('2d')!; - const mainContext = mainCanvas.getContext('2d')!; + const canvas = canvasRef.current; + const context = canvas.getContext('2d')!; const currentTick = getTick(); - context.clearRect(0, 0, visibleCanvas.width, visibleCanvas.height); - mainContext.clearRect(0, 0, mainCanvas.width, mainCanvas.height); + context.clearRect(0, 0, canvas.width, canvas.height); map.forEachTile( (vector: Vector, tile: TileInfo, layer: TileLayer, modifier: number) => { @@ -153,7 +135,6 @@ export default memo(function Tiles({ } renderFloatingTile( context, - mainContext, tileset, map, vision, @@ -168,22 +149,14 @@ export default memo(function Tiles({ } } - mainContext.drawImage(visibleCanvas, 0, 0); - if (style === 'clip') { - clip(mainContext, size, map); - } - - if (ref.current) { - ref.current.innerHTML = ''; - if (!mainCanvas.parentNode) { - ref.current.append(mainCanvas); - } + clip(context, size, map); } if (!paused && isVisible) { return tick((tick) => { - map.forEachField((vector: Vector) => { + for (let i = 0; i < map.map.length; i++) { + const vector = indexToVector(i, map.size.width); const tile = map.buildings.get(vector)?.info === Shelter ? Campsite @@ -199,8 +172,6 @@ export default memo(function Tiles({ frame != null || (layer1TileInfo?.sprite?.animation && !tile.sprite.animation); if (renderLayer0) { - mainContext.clearRect(vector.x * size, vector.y * size, size, size); - context.clearRect(vector.x * size, vector.y * size, size, size); renderTile( context, tileset, @@ -233,12 +204,11 @@ export default memo(function Tiles({ renderEntities, ); } - }); + } for (const [vector, modifier] of floatingTiles) { renderFloatingTile( context, - mainContext, tileset, map, vision, @@ -251,9 +221,8 @@ export default memo(function Tiles({ ); } - mainContext.drawImage(visibleCanvas, 0, 0); if (style === 'clip') { - clip(mainContext, size, map); + clip(context, size, map); } }); } @@ -295,7 +264,13 @@ export default memo(function Tiles({ position: 'absolute', top: -size, }} - /> + > + + ); }); diff --git a/hera/render/renderFloatingTile.tsx b/hera/render/renderFloatingTile.tsx index 4d0caaaf..223b5402 100644 --- a/hera/render/renderFloatingTile.tsx +++ b/hera/render/renderFloatingTile.tsx @@ -12,7 +12,6 @@ import renderTile, { TileSet } from './renderTile.tsx'; export default function renderFloatingTile( context: CanvasRenderingContext2D, - mainContext: CanvasRenderingContext2D, tileset: TileSet, map: MapData, vision: VisionT, @@ -27,7 +26,6 @@ export default function renderFloatingTile( const targetX = vector.x * size; const targetY = vector.y * size; context.clearRect(targetX, targetY, size, size); - mainContext.clearRect(targetX, targetY, size, size); } const [modifier0, modifier1] = Array.isArray(modifier)