From 61a1b4f148d572cb4a6b03df8d1dd3b9f7e3afc6 Mon Sep 17 00:00:00 2001 From: trafficlunar Date: Tue, 17 Dec 2024 19:17:13 +0000 Subject: [PATCH] fix: replace with tilemap --- package.json | 1 + src/components/canvas/Blocks.tsx | 103 ++++++++++++++++++++----------- src/components/canvas/Canvas.tsx | 34 +++------- 3 files changed, 78 insertions(+), 60 deletions(-) diff --git a/package.json b/package.json index 292d236..28d2f3d 100644 --- a/package.json +++ b/package.json @@ -11,6 +11,7 @@ }, "dependencies": { "@pixi/react": "7", + "@pixi/tilemap": "4.1.0", "@radix-ui/react-dialog": "^1.1.3", "@radix-ui/react-label": "^2.1.1", "@radix-ui/react-menubar": "^1.1.2", diff --git a/src/components/canvas/Blocks.tsx b/src/components/canvas/Blocks.tsx index b7b08ed..89e29a6 100644 --- a/src/components/canvas/Blocks.tsx +++ b/src/components/canvas/Blocks.tsx @@ -1,20 +1,27 @@ -import { useEffect, useState } from "react"; -import { Sprite } from "@pixi/react"; +import { useEffect, useRef, useState } from "react"; +import { Sprite, useApp } from "@pixi/react"; import blocksData from "@/data/blocks/programmer-art/average_colors.json"; import * as PIXI from "pixi.js"; +import { CompositeTilemap } from "@pixi/tilemap"; + interface Props { blocks: Block[]; setBlocks: React.Dispatch>; textures: Record; image: HTMLImageElement | undefined; imageDimensions: Dimension; + coords: Position; + scale: number; } -function Blocks({ blocks, setBlocks, textures, image, imageDimensions }: Props) { +function Blocks({ blocks, setBlocks, textures, image, imageDimensions, coords, scale }: Props) { + const app = useApp(); const [missingTexture, setMissingTexture] = useState(); + const tilemapRef = useRef(); + const findClosestBlock = (r: number, g: number, b: number, a: number) => { let closestBlock = ""; let closestDistance = Infinity; @@ -30,55 +37,79 @@ function Blocks({ blocks, setBlocks, textures, image, imageDimensions }: Props) return closestBlock; }; + const tileBlocks = () => { + if (!tilemapRef.current) return; + const tilemap = tilemapRef.current; + tilemap.clear(); + + console.log(tilemap.texturesPerTilemap); + + blocks.forEach((block) => { + tilemap.tile(textures[block.name] ?? missingTexture, block.x * 16, block.y * 16); + }); + }; + useEffect(() => { + // Load the missing texture const loadMissingTexture = async () => { const mTexture = await PIXI.Texture.from("/blocks/missing.png"); setMissingTexture(mTexture); }; loadMissingTexture(); - if (image) { - const canvas = document.createElement("canvas"); - const ctx = canvas.getContext("2d"); + // Create tilemap + const tilemap = new CompositeTilemap(); + tilemapRef.current = tilemap; + tilemap.cullable = true; + app.stage.addChildAt(tilemap, 0); - if (ctx) { - canvas.width = imageDimensions.width; - canvas.height = imageDimensions.height; - ctx.drawImage(image, 0, 0, imageDimensions.width, imageDimensions.height); + tileBlocks(); + }, []); - const imageData = ctx.getImageData(0, 0, imageDimensions.width, imageDimensions.height); - const newBlocks: Block[] = []; + useEffect(tileBlocks, [blocks]); - for (let i = 0; i < imageData.data.length; i += 4) { - const block = findClosestBlock(imageData.data[i], imageData.data[i + 1], imageData.data[i + 2], imageData.data[i + 3]); + useEffect(() => { + if (!tilemapRef.current) return; - const x = Math.floor((i / 4) % imageData.width); - const y = Math.floor(i / 4 / imageData.width); + tileBlocks(); - newBlocks.push({ - name: block, - x, - y, - }); - } + tilemapRef.current.x = coords.x; + tilemapRef.current.y = coords.y; + tilemapRef.current.scale.set(scale, scale); + }, [coords, scale]); - setBlocks(newBlocks); + useEffect(() => { + if (!image) return; + + const canvas = document.createElement("canvas"); + const ctx = canvas.getContext("2d"); + + if (ctx) { + canvas.width = imageDimensions.width; + canvas.height = imageDimensions.height; + ctx.drawImage(image, 0, 0, imageDimensions.width, imageDimensions.height); + + const imageData = ctx.getImageData(0, 0, imageDimensions.width, imageDimensions.height); + const newBlocks: Block[] = []; + + for (let i = 0; i < imageData.data.length; i += 4) { + const block = findClosestBlock(imageData.data[i], imageData.data[i + 1], imageData.data[i + 2], imageData.data[i + 3]); + + const x = Math.floor((i / 4) % imageData.width); + const y = Math.floor(i / 4 / imageData.width); + + newBlocks.push({ + name: block, + x, + y, + }); } + + setBlocks(newBlocks); } - }, [image, imageDimensions, setBlocks]); + }, [image, imageDimensions]); - return ( - <> - {blocks.map((block, index) => { - const texture = textures[block.name] ?? missingTexture; - if (!texture) { - console.warn(`Texture not found for block: ${block.name}`); - } - - return ; - })} - - ); + return null; } export default Blocks; diff --git a/src/components/canvas/Canvas.tsx b/src/components/canvas/Canvas.tsx index 5fad8bd..9e053b5 100644 --- a/src/components/canvas/Canvas.tsx +++ b/src/components/canvas/Canvas.tsx @@ -65,29 +65,6 @@ function Canvas() { }; }, [blocks]); - const visibleArea = useMemo(() => { - const blockSize = 16 * scale; - - const visibleWidthBlocks = Math.ceil(stageSize.width / blockSize); - const visibleHeightBlocks = Math.ceil(stageSize.height / blockSize); - - const startX = Math.floor(-coords.x / blockSize); - const startY = Math.floor(-coords.y / blockSize); - - return { - startX, - startY, - endX: startX + visibleWidthBlocks + 1, - endY: startY + visibleHeightBlocks + 1, - }; - }, [coords, scale, stageSize]); - - const visibleBlocks = useMemo(() => { - return blocks.filter( - (block) => block.x >= visibleArea.startX && block.x < visibleArea.endX && block.y >= visibleArea.startY && block.y < visibleArea.endY - ); - }, [blocks, visibleArea]); - const zoomToMousePosition = useCallback( (newScale: number) => { setCoords({ @@ -264,8 +241,17 @@ function Canvas() { onWheel={onWheel} onClick={onClick} > + + - {settings.canvasBorder && }