fix: replace with tilemap
This commit is contained in:
parent
af4bb8e7f3
commit
61a1b4f148
3 changed files with 78 additions and 60 deletions
|
|
@ -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",
|
||||||
|
|
|
||||||
|
|
@ -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,55 +37,79 @@ 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 canvas = document.createElement("canvas");
|
const tilemap = new CompositeTilemap();
|
||||||
const ctx = canvas.getContext("2d");
|
tilemapRef.current = tilemap;
|
||||||
|
tilemap.cullable = true;
|
||||||
|
app.stage.addChildAt(tilemap, 0);
|
||||||
|
|
||||||
if (ctx) {
|
tileBlocks();
|
||||||
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);
|
useEffect(tileBlocks, [blocks]);
|
||||||
const newBlocks: Block[] = [];
|
|
||||||
|
|
||||||
for (let i = 0; i < imageData.data.length; i += 4) {
|
useEffect(() => {
|
||||||
const block = findClosestBlock(imageData.data[i], imageData.data[i + 1], imageData.data[i + 2], imageData.data[i + 3]);
|
if (!tilemapRef.current) return;
|
||||||
|
|
||||||
const x = Math.floor((i / 4) % imageData.width);
|
tileBlocks();
|
||||||
const y = Math.floor(i / 4 / imageData.width);
|
|
||||||
|
|
||||||
newBlocks.push({
|
tilemapRef.current.x = coords.x;
|
||||||
name: block,
|
tilemapRef.current.y = coords.y;
|
||||||
x,
|
tilemapRef.current.scale.set(scale, scale);
|
||||||
y,
|
}, [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 (
|
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;
|
||||||
|
|
|
||||||
|
|
@ -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>
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue