화면 기록 2024-10-23 오후 12.02.40.mov
https://bolt.new/ 에서 AI가 만든 스페이스 슈팅 게임

// [ useLoadImage.tsx ] : 별도의 이미지 load hook 생성
export const useLoadImage = () => {
// 이미지 로드 확인용
const [isImgLoaded, setIsImgLoaded] = useState<boolean[]>([false, false]);
const isCompleted = isImgLoaded.some(item => !item); // 모든 이미지 불러오기 완료
// 이미지 load 완료할 경우 상태 값 변경
const handleImageLoad = (idx: number) => {
if(isImgLoaded[idx]) return;
setIsImgLoaded([
...isImgLoaded.slice(0, idx),
true,
...isImgLoaded.slice(idx + 1, isImgLoaded.length)
]);
}
// 이미지 불러오기
const bgImg = new Image();
bgImg.src = '/images/space.png';
bgImg.onload = () => handleImageLoad(0);
const fighterImg = new Image();
fighterImg.src = '/images/fighter.png';
fighterImg.onload = () => handleImageLoad(1);
return {
// 이미지 load 상태값
isCompleted,
// 이미지 객체
bgImg, fighterImg,
}
}
// [ FighterVO.tsx ] : 비행기 객체 생성
interface IFighterVO {
x: number;
y: number;
speed: number;
}
export default class FighterVO {
x: number;
y: number;
speed: number;
constructor(vo: IFighterVO) {
this.x = vo.x;
this.y = vo.y;
this.speed = vo.speed;
}
}
// [ Shooter.tsx ]
// 전체 캔버스 영역 사이즈
const ctxW = 600;
const ctxH = 400;
// 비행기 객체 좌표 가지고 있는 객체 생성
const fighter = new FighterVO({
x: 50,
y: ctxH / 2,
speed: 5
});
const Shooter = () => {
const { isCompleted, bgImg, fighterImg } = useLoadImage();
// 배경 이미지 그리기
const animate = (ctx: CanvasRenderingContext2D) => {
if(isCompleted) return;
ctx.drawImage(bgImg, 0,0);
ctx.drawImage(fighterImg, fighter.x, fighter.y);
};
const ref = useCanvas(ctxW, ctxH, animate);
return (
<canvas ref={ref}/>
);
}
화면 기록 2024-10-20 오전 1.49.08.mov
// [ FighterVO.tsx ]
// 키보드 key에 따른 비행기 좌표 수정
update = (code: string) =>{
// 키보드 키에 따른 비행기 좌표 변경
switch (code) {
case 'w':
this.y -= this.speed;
break;
case 's':
this.y += this.speed;
break;
case 'a':
this.x -= this.speed;
break;
case 'd':
this.x += this.speed;
break;
}
// 비행기가 화면 밖으로 안나가도록 조건 처리
if(this.x <= 0) {
this.x = 0;
}
if(this.x >= this.ctxW - 60) {
this.x = this.ctxW - 60;
}
if(this.y <= 0) {
this.y = 0;
}
if(this.y >= this.ctxH - 30) {
this.y = this.ctxH - 30;
}
}
// useKeyBoadEvents.ts
export const useKeydownEvent = (onEvent: (code: string) => void) => {
useEffect(() => {
window.addEventListener('keydown', (e: KeyboardEvent) => {
onEvent(e.key);
})
return () => {
window.removeEventListener('keydown', (e: KeyboardEvent) => {
onEvent(e.key);
})
}
}, []);
}
// [ Shooter.tsx ]
// 비행기 객체
const fighter = new FighterVO({
x: 50,
y: 400 / 2,
speed: 5,
//화면 벗어남 방지 위한 캔버스 사이즈 값 추가
ctxW,
ctxH
});
// 프레임 시간 조절 함
let lastUpdateTime = 0;
let acDelta = 0;
let msPerFrame = 1000;
const animate = (ctx: CanvasRenderingContext2D) => {
if(isCompleted) return;
let delta = Date.now() - lastUpdateTime;
if(acDelta > msPerFrame) {
acDelta = 0;
ctx.drawImage(bgImg, 0,0);
ctx.drawImage(fighterImg, fighter.x, fighter.y);
lastUpdateTime = Date.now(); // 예제 소스와 다른 부분!!
} else {
acDelta = delta;
}
};
//키도브 이벤트 등록
useKeydownEvent(fighter.update);
화면 기록 2024-10-20 오전 2.32.09.mov
// [ LaserVO.ts ] > 미사일 객체
interface ILaserVO {
x: number;
y: number;
}
export default class LaserVO {
x: number;
y: number;
constructor(vo: ILaserVO) {
this.x = vo.x;
this.y = vo.y;
}
//
moveX() {
this.x += 20;
}
}
// [ useLoadImage.tsx ]
// 미사일 이미지 추가
const laserImg = new Image();
laserImg.src = '/images/laser.png';
laserImg.onload = () => handleImageLoad(2);
// [ Shooter.tsx ]
// 레이저 관련
let lasers: LaserVO[] = [];
const laserTotal = 10;
// 미사일 그림 그리는 함수
const drawLaser = (ctx: CanvasRenderingContext2D) => {
if(lasers.length) {
lasers.forEach((item, idx) => {
ctx.drawImage(laserImg, item.x, item.y);
//미사일 이동 처리
item.moveX();
// 미사일이 캔버스 영역 벗어날 경우 삭제 처리
if(item.x > ctxW) {
lasers.splice(idx, 1);
}
})
}
}
const animate = (ctx: CanvasRenderingContext2D) => {
if(isCompleted) return;
let delta = Date.now() - lastUpdateTime;
if(acDelta > msPerFrame) {
acDelta = 0;
ctx.drawImage(bgImg, 0,0);
ctx.drawImage(fighterImg, fighter.x, fighter.y);
drawLaser(ctx); // 미사일 함수 추가
lastUpdateTime = Date.now();
} else {
acDelta = delta;
}
};
useKeydownEvent((key) => {
// 비행기 객체 > update에서 현재 비행기 좌표 값 반환하도록 수정
// FighterVO > update() > return {x: this.x, y: this.y}; 소스 추가
const {x, y} = fighter.update(key);
// 스페이스바 키보드 이벤트시 미사일 객체 추가
if(key == ' ' && lasers.length <= laserTotal){
lasers.push(new LaserVO({
x: x + 50,
y: y + 10
}));
}
});