잔디 일러스트 텍스처 적용
- public/assets/grass.jpg (1200px JPEG 최적화) - 잔디 타일 영역 클리핑 채움, 격자 힌트는 미세한 화이트 오버레이로 유지 - 로드 전 단색 폴백 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
이 커밋은 다음에 포함됨:
바이너리 파일은 표시되지 않음.
|
이후 너비: | 높이: | 크기: 424 KiB |
@@ -89,6 +89,7 @@ export default function VillageView() {
|
|||||||
const bubblesRef = useRef<Bubble[]>([]);
|
const bubblesRef = useRef<Bubble[]>([]);
|
||||||
const toolImagesRef = useRef<Map<AptitudeKey, HTMLImageElement>>(new Map());
|
const toolImagesRef = useRef<Map<AptitudeKey, HTMLImageElement>>(new Map());
|
||||||
const waterImgRef = useRef<HTMLImageElement | null>(null);
|
const waterImgRef = useRef<HTMLImageElement | null>(null);
|
||||||
|
const grassImgRef = useRef<HTMLImageElement | null>(null);
|
||||||
const hitRectsRef = useRef<
|
const hitRectsRef = useRef<
|
||||||
Array<{ id: string; x: number; y: number; w: number; h: number }>
|
Array<{ id: string; x: number; y: number; w: number; h: number }>
|
||||||
>([]);
|
>([]);
|
||||||
@@ -110,11 +111,14 @@ export default function VillageView() {
|
|||||||
img.src = src;
|
img.src = src;
|
||||||
img.onload = () => toolImagesRef.current.set(key as AptitudeKey, img);
|
img.onload = () => toolImagesRef.current.set(key as AptitudeKey, img);
|
||||||
}
|
}
|
||||||
// 강물 텍스처 (로드 전에는 단색 폴백)
|
// 강물·잔디 텍스처 (로드 전에는 단색 폴백)
|
||||||
{
|
{
|
||||||
const img = new Image();
|
const img = new Image();
|
||||||
img.src = "/assets/water.jpg";
|
img.src = "/assets/water.jpg";
|
||||||
img.onload = () => (waterImgRef.current = img);
|
img.onload = () => (waterImgRef.current = img);
|
||||||
|
const grass = new Image();
|
||||||
|
grass.src = "/assets/grass.jpg";
|
||||||
|
grass.onload = () => (grassImgRef.current = grass);
|
||||||
}
|
}
|
||||||
|
|
||||||
let raf = 0;
|
let raf = 0;
|
||||||
@@ -205,16 +209,38 @@ export default function VillageView() {
|
|||||||
ctx.closePath();
|
ctx.closePath();
|
||||||
};
|
};
|
||||||
|
|
||||||
for (let j = 0; j < ROWS; j++) {
|
// 잔디 — 일러스트 텍스처를 잔디 타일 영역에 클리핑해서 채움
|
||||||
for (let i = 0; i < COLS; i++) {
|
{
|
||||||
if (isWater(i, j)) continue;
|
ctx.save();
|
||||||
ctx.beginPath();
|
ctx.beginPath();
|
||||||
tilePath(i, j);
|
for (let j = 0; j < ROWS; j++)
|
||||||
ctx.fillStyle = (i + j) % 2 === 0 ? "#86c66b" : "#7dbd62";
|
for (let i = 0; i < COLS; i++) if (!isWater(i, j)) tilePath(i, j);
|
||||||
ctx.fill();
|
ctx.clip();
|
||||||
ctx.strokeStyle = "rgba(255,255,255,0.10)";
|
|
||||||
ctx.stroke();
|
const grass = grassImgRef.current;
|
||||||
|
if (grass) {
|
||||||
|
const scale = Math.max(CANVAS_W / grass.width, CANVAS_H / grass.height);
|
||||||
|
ctx.drawImage(grass, 0, 0, grass.width * scale, grass.height * scale);
|
||||||
|
} else {
|
||||||
|
ctx.fillStyle = "#82c368";
|
||||||
|
ctx.fillRect(0, 0, CANVAS_W, CANVAS_H);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 타일 격자 힌트 (배치 가독성 유지)
|
||||||
|
for (let j = 0; j < ROWS; j++) {
|
||||||
|
for (let i = 0; i < COLS; i++) {
|
||||||
|
if (isWater(i, j)) continue;
|
||||||
|
ctx.beginPath();
|
||||||
|
tilePath(i, j);
|
||||||
|
if ((i + j) % 2 === 0) {
|
||||||
|
ctx.fillStyle = "rgba(255,255,255,0.045)";
|
||||||
|
ctx.fill();
|
||||||
|
}
|
||||||
|
ctx.strokeStyle = "rgba(255,255,255,0.08)";
|
||||||
|
ctx.stroke();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ctx.restore();
|
||||||
}
|
}
|
||||||
|
|
||||||
// 강물 — 일러스트 텍스처를 물 타일 영역에 클리핑해서 채움
|
// 강물 — 일러스트 텍스처를 물 타일 영역에 클리핑해서 채움
|
||||||
|
|||||||
새 이슈에서 참조
사용자 차단