고양이 스프라이트 적용: 4프레임 (대기·걷기 2·일하기)
- cat_sprite.png 시트를 4프레임으로 슬라이스, 가장자리 플러드필로 배경 투명화 (배 크림색 보존), 256px 축소 → public/assets/cats/ - 걷기 180ms 프레임 교대, 일할 때 work 포즈 + 바운스, 좌우 반전 지원 - 로드 전 이모지 폴백 유지 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
이 커밋은 다음에 포함됨:
바이너리 파일은 표시되지 않음.
|
이후 너비: | 높이: | 크기: 70 KiB |
바이너리 파일은 표시되지 않음.
|
이후 너비: | 높이: | 크기: 70 KiB |
바이너리 파일은 표시되지 않음.
|
이후 너비: | 높이: | 크기: 70 KiB |
바이너리 파일은 표시되지 않음.
|
이후 너비: | 높이: | 크기: 73 KiB |
@@ -90,6 +90,7 @@ export default function VillageView() {
|
||||
const toolImagesRef = useRef<Map<AptitudeKey, HTMLImageElement>>(new Map());
|
||||
const waterImgRef = useRef<HTMLImageElement | null>(null);
|
||||
const grassImgRef = useRef<HTMLImageElement | null>(null);
|
||||
const catFramesRef = useRef<Map<string, HTMLImageElement>>(new Map());
|
||||
const hitRectsRef = useRef<
|
||||
Array<{ id: string; x: number; y: number; w: number; h: number }>
|
||||
>([]);
|
||||
@@ -119,6 +120,12 @@ export default function VillageView() {
|
||||
const grass = new Image();
|
||||
grass.src = "/assets/grass.jpg";
|
||||
grass.onload = () => (grassImgRef.current = grass);
|
||||
// 고양이 스프라이트 4프레임
|
||||
for (const frame of ["idle", "walk1", "walk2", "work"]) {
|
||||
const catImg = new Image();
|
||||
catImg.src = `/assets/cats/${frame}.png`;
|
||||
catImg.onload = () => catFramesRef.current.set(frame, catImg);
|
||||
}
|
||||
}
|
||||
|
||||
let raf = 0;
|
||||
@@ -403,13 +410,31 @@ export default function VillageView() {
|
||||
sprites.push({
|
||||
y: anim.y,
|
||||
draw: () => {
|
||||
shadow(anim.x, anim.y + 2, 10);
|
||||
drawEmoji(cat.emoji, anim.x, anim.y + bob, 30, anim.flip);
|
||||
shadow(anim.x, anim.y + 2, 11);
|
||||
const frameName = working
|
||||
? "work"
|
||||
: moving
|
||||
? Math.floor(now / 180) % 2 === 0
|
||||
? "walk1"
|
||||
: "walk2"
|
||||
: "idle";
|
||||
const sprite = catFramesRef.current.get(frameName);
|
||||
if (sprite) {
|
||||
const sh = 42;
|
||||
const sw = (sprite.width / sprite.height) * sh;
|
||||
ctx.save();
|
||||
ctx.translate(anim.x, anim.y + bob);
|
||||
if (anim.flip) ctx.scale(-1, 1);
|
||||
ctx.drawImage(sprite, -sw / 2, -sh + 4, sw, sh);
|
||||
ctx.restore();
|
||||
} else {
|
||||
drawEmoji(cat.emoji, anim.x, anim.y + bob, 30, anim.flip);
|
||||
}
|
||||
ctx.font = "10px sans-serif";
|
||||
ctx.textAlign = "center";
|
||||
ctx.fillStyle = "rgba(55,65,81,0.85)";
|
||||
ctx.fillText(cat.name, anim.x, anim.y + 12);
|
||||
if (working) drawEmoji("💦", anim.x + 12, anim.y - 20 + bob, 10);
|
||||
ctx.fillText(cat.name, anim.x, anim.y + 14);
|
||||
if (working) drawEmoji("💦", anim.x + 14, anim.y - 30 + bob, 11);
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
새 이슈에서 참조
사용자 차단