fix: replace with tilemap

This commit is contained in:
trafficlunar 2024-12-17 19:17:13 +00:00
parent af4bb8e7f3
commit 61a1b4f148
3 changed files with 78 additions and 60 deletions

View file

@ -11,6 +11,7 @@
}, },
"dependencies": { "dependencies": {
"@pixi/react": "7", "@pixi/react": "7",
"@pixi/tilemap": "4.1.0",
"@radix-ui/react-dialog": "^1.1.3", "@radix-ui/react-dialog": "^1.1.3",
"@radix-ui/react-label": "^2.1.1", "@radix-ui/react-label": "^2.1.1",
"@radix-ui/react-menubar": "^1.1.2", "@radix-ui/react-menubar": "^1.1.2",

View file

@ -1,20 +1,27 @@
import { useEffect, useState } from "react"; import { useEffect, useRef, useState } from "react";
import { Sprite } from "@pixi/react"; import { Sprite, useApp } from "@pixi/react";
import blocksData from "@/data/blocks/programmer-art/average_colors.json"; import blocksData from "@/data/blocks/programmer-art/average_colors.json";
import * as PIXI from "pixi.js"; import * as PIXI from "pixi.js";
import { CompositeTilemap } from "@pixi/tilemap";
interface Props { interface Props {
blocks: Block[]; blocks: Block[];
setBlocks: React.Dispatch<React.SetStateAction<Block[]>>; setBlocks: React.Dispatch<React.SetStateAction<Block[]>>;
textures: Record<string, PIXI.Texture>; textures: Record<string, PIXI.Texture>;
image: HTMLImageElement | undefined; image: HTMLImageElement | undefined;
imageDimensions: Dimension; 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<PIXI.Texture>(); const [missingTexture, setMissingTexture] = useState<PIXI.Texture>();
const tilemapRef = useRef<CompositeTilemap>();
const findClosestBlock = (r: number, g: number, b: number, a: number) => { const findClosestBlock = (r: number, g: number, b: number, a: number) => {
let closestBlock = ""; let closestBlock = "";
let closestDistance = Infinity; let closestDistance = Infinity;
@ -30,14 +37,50 @@ function Blocks({ blocks, setBlocks, textures, image, imageDimensions }: Props)
return closestBlock; 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(() => { useEffect(() => {
// Load the missing texture
const loadMissingTexture = async () => { const loadMissingTexture = async () => {
const mTexture = await PIXI.Texture.from("/blocks/missing.png"); const mTexture = await PIXI.Texture.from("/blocks/missing.png");
setMissingTexture(mTexture); setMissingTexture(mTexture);
}; };
loadMissingTexture(); loadMissingTexture();
if (image) { // Create tilemap
const tilemap = new CompositeTilemap();
tilemapRef.current = tilemap;
tilemap.cullable = true;
app.stage.addChildAt(tilemap, 0);
tileBlocks();
}, []);
useEffect(tileBlocks, [blocks]);
useEffect(() => {
if (!tilemapRef.current) return;
tileBlocks();
tilemapRef.current.x = coords.x;
tilemapRef.current.y = coords.y;
tilemapRef.current.scale.set(scale, scale);
}, [coords, scale]);
useEffect(() => {
if (!image) return;
const canvas = document.createElement("canvas"); const canvas = document.createElement("canvas");
const ctx = canvas.getContext("2d"); const ctx = canvas.getContext("2d");
@ -64,21 +107,9 @@ function Blocks({ blocks, setBlocks, textures, image, imageDimensions }: Props)
setBlocks(newBlocks); setBlocks(newBlocks);
} }
} }, [image, imageDimensions]);
}, [image, imageDimensions, setBlocks]);
return ( return null;
<>
{blocks.map((block, index) => {
const texture = textures[block.name] ?? missingTexture;
if (!texture) {
console.warn(`Texture not found for block: ${block.name}`);
}
return <Sprite key={index} texture={texture} x={block.x * 16} y={block.y * 16} />;
})}
</>
);
} }
export default Blocks; export default Blocks;

View file

@ -65,29 +65,6 @@ function Canvas() {
}; };
}, [blocks]); }, [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( const zoomToMousePosition = useCallback(
(newScale: number) => { (newScale: number) => {
setCoords({ setCoords({
@ -264,8 +241,17 @@ function Canvas() {
onWheel={onWheel} onWheel={onWheel}
onClick={onClick} onClick={onClick}
> >
<Blocks
blocks={blocks}
setBlocks={setBlocks}
textures={textures}
image={image}
imageDimensions={imageDimensions}
coords={coords}
scale={scale}
/>
<Container x={coords.x} y={coords.y} scale={scale}> <Container x={coords.x} y={coords.y} scale={scale}>
<Blocks blocks={visibleBlocks} setBlocks={setBlocks} textures={textures} image={image} imageDimensions={imageDimensions} />
{settings.canvasBorder && <CanvasBorder canvasSize={canvasSize} />} {settings.canvasBorder && <CanvasBorder canvasSize={canvasSize} />}
<Cursor mouseCoords={mouseCoords} /> <Cursor mouseCoords={mouseCoords} />
</Container> </Container>