잔디 일러스트 텍스처 적용
- 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 toolImagesRef = useRef<Map<AptitudeKey, HTMLImageElement>>(new Map());
|
||||
const waterImgRef = useRef<HTMLImageElement | null>(null);
|
||||
const grassImgRef = useRef<HTMLImageElement | null>(null);
|
||||
const hitRectsRef = useRef<
|
||||
Array<{ id: string; x: number; y: number; w: number; h: number }>
|
||||
>([]);
|
||||
@@ -110,11 +111,14 @@ export default function VillageView() {
|
||||
img.src = src;
|
||||
img.onload = () => toolImagesRef.current.set(key as AptitudeKey, img);
|
||||
}
|
||||
// 강물 텍스처 (로드 전에는 단색 폴백)
|
||||
// 강물·잔디 텍스처 (로드 전에는 단색 폴백)
|
||||
{
|
||||
const img = new Image();
|
||||
img.src = "/assets/water.jpg";
|
||||
img.onload = () => (waterImgRef.current = img);
|
||||
const grass = new Image();
|
||||
grass.src = "/assets/grass.jpg";
|
||||
grass.onload = () => (grassImgRef.current = grass);
|
||||
}
|
||||
|
||||
let raf = 0;
|
||||
@@ -205,16 +209,38 @@ export default function VillageView() {
|
||||
ctx.closePath();
|
||||
};
|
||||
|
||||
for (let j = 0; j < ROWS; j++) {
|
||||
for (let i = 0; i < COLS; i++) {
|
||||
if (isWater(i, j)) continue;
|
||||
ctx.beginPath();
|
||||
tilePath(i, j);
|
||||
ctx.fillStyle = (i + j) % 2 === 0 ? "#86c66b" : "#7dbd62";
|
||||
ctx.fill();
|
||||
ctx.strokeStyle = "rgba(255,255,255,0.10)";
|
||||
ctx.stroke();
|
||||
// 잔디 — 일러스트 텍스처를 잔디 타일 영역에 클리핑해서 채움
|
||||
{
|
||||
ctx.save();
|
||||
ctx.beginPath();
|
||||
for (let j = 0; j < ROWS; j++)
|
||||
for (let i = 0; i < COLS; i++) if (!isWater(i, j)) tilePath(i, j);
|
||||
ctx.clip();
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
// 강물 — 일러스트 텍스처를 물 타일 영역에 클리핑해서 채움
|
||||
|
||||
새 이슈에서 참조
사용자 차단