feat: achieve 600 files milestone in AI knowledge base
This commit is contained in:
@@ -0,0 +1,110 @@
|
||||
/**
|
||||
* Custom Hook: 웹 워커를 사용하여 고성능 게임 로직을 관리합니다.
|
||||
* 이 훅은 React의 상태(State)와 생명 주기(Lifecycle)를 이용하여 Web Worker와의 통신을 담당합니다.
|
||||
* @returns {{ grid: Array<Array<number>>, score: number, isGameRunning: boolean, moveLeft: Function, moveRight: Function, rotate: Function }}
|
||||
*/
|
||||
|
||||
import { useState, useEffect, useCallback } from 'react';
|
||||
|
||||
// 🚨 주의: 실제 프로젝트 구조에 따라 경로를 수정해야 합니다.
|
||||
const worker = new Worker(new URL('../tetris-worker.js', import.meta.url), { type: 'module' });
|
||||
|
||||
/**
|
||||
* Web Worker 기반의 Tetris 게임 로직을 관리하는 커스텀 훅.
|
||||
*/
|
||||
export const useGameLogic = () => {
|
||||
// 초기 상태 설정 (Worker가 INIT 메시지를 보낼 때까지 대기)
|
||||
const [grid, setGrid] = useState([]);
|
||||
const [score, setScore] = useState(0);
|
||||
const [isGameRunning, setIsGameRunning] = useState(false);
|
||||
|
||||
/**
|
||||
* 🌐 Worker로부터 메시지 수신 처리 핸들러
|
||||
*/
|
||||
useEffect(() => {
|
||||
// 워커가 로드되었을 때의 리스너 설정
|
||||
worker.onmessage = (event) => {
|
||||
const data = event.data;
|
||||
|
||||
switch (data.type) {
|
||||
case 'READY':
|
||||
console.log("Worker is ready. Game initialized.");
|
||||
// 워커가 준비되면 게임 시작 플래그를 활성화할 수 있습니다.
|
||||
setIsGameRunning(true);
|
||||
break;
|
||||
case 'INIT':
|
||||
setGrid(data.data.grid);
|
||||
setScore(data.data.score);
|
||||
console.log("Game initialized successfully.");
|
||||
// 게임 시작 후 첫 번째 메시지 처리 완료
|
||||
break;
|
||||
case 'UPDATE':
|
||||
// 핵심 상태 업데이트: Worker가 계산한 새로운 그리드와 점수
|
||||
setGrid(data.data.grid);
|
||||
setScore(data.data.score);
|
||||
console.log("Game state updated via worker.");
|
||||
break;
|
||||
default:
|
||||
console.warn('Unknown message type received from worker:', data.type);
|
||||
}
|
||||
};
|
||||
|
||||
// 컴포넌트 언마운트 시 워커 종료 (메모리 누수 방지)
|
||||
return () => {
|
||||
worker.terminate();
|
||||
};
|
||||
}, []);
|
||||
|
||||
|
||||
/**
|
||||
* ⬅️ 이동 로직 호출 (좌측 이동)
|
||||
*/
|
||||
const moveLeft = useCallback(() => {
|
||||
if (!isGameRunning) return;
|
||||
// Web Worker에게 명령 전송: 충돌 검사 및 상태 업데이트는 워커 내부에서 처리됨.
|
||||
worker.postMessage({ type: 'MOVE_LEFT', command: 'left' });
|
||||
}, [isGameRunning]);
|
||||
|
||||
/**
|
||||
* ➡️ 이동 로직 호출 (우측 이동)
|
||||
*/
|
||||
const moveRight = useCallback(() => {
|
||||
if (!isGameRunning) return;
|
||||
worker.postMessage({ type: 'MOVE_RIGHT', command: 'right' });
|
||||
}, [isGameRunning]);
|
||||
|
||||
/**
|
||||
* 🔄 회전 로직 호출
|
||||
*/
|
||||
const rotate = useCallback(() => {
|
||||
if (!isGameRunning) return;
|
||||
worker.postMessage({ type: 'ROTATE', command: 'rotate' });
|
||||
}, [isGameRunning]);
|
||||
|
||||
/**
|
||||
* ⬇️ 게임 틱 시작 (게임 루프 시작)
|
||||
*/
|
||||
const startGameTick = useCallback(() => {
|
||||
if (!isGameRunning) {
|
||||
// 초기화 메시지 전송 (혹시 모를 재설정을 위해)
|
||||
worker.postMessage({ type: 'INIT' });
|
||||
// 게임 틱을 시작하는 명령 전송
|
||||
setTimeout(() => {
|
||||
worker.postMessage({ type: 'TICK' });
|
||||
}, 50); // 약간의 딜레이 후 첫 틱 실행
|
||||
}
|
||||
}, [isGameRunning]);
|
||||
|
||||
|
||||
return {
|
||||
grid,
|
||||
score,
|
||||
isGameRunning,
|
||||
moveLeft,
|
||||
moveRight,
|
||||
rotate,
|
||||
startGameTick
|
||||
};
|
||||
};
|
||||
|
||||
export default useGameLogic;
|
||||
@@ -0,0 +1,82 @@
|
||||
/**
|
||||
* Web Worker 파일: Tetris의 모든 무거운 계산(Game Loop, 물리 엔진)을 전담합니다.
|
||||
*/
|
||||
|
||||
// 게임 그리드 상수 정의 (20줄 x 10칸)
|
||||
const GRID_WIDTH = 10;
|
||||
const GRID_HEIGHT = 20;
|
||||
let gridState = []; // 현재 게임 상태를 저장하는 2차원 배열
|
||||
let score = 0;
|
||||
|
||||
/**
|
||||
* 초기화 함수: 워커가 로드될 때 호출됩니다.
|
||||
*/
|
||||
function initializeGame() {
|
||||
// 빈 그리드로 초기화
|
||||
gridState = Array(GRID_HEIGHT).fill(null).map(() => Array(GRID_WIDTH).fill(0));
|
||||
score = 0;
|
||||
|
||||
postMessage({
|
||||
type: 'INIT',
|
||||
data: { grid: gridState, score: score }
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 게임 루프 (Game Tick) 시뮬레이션 함수.
|
||||
* 이 함수가 주기적으로 호출되어 중력과 충돌을 처리합니다.
|
||||
*/
|
||||
function gameTick() {
|
||||
// TODO: 1. 현재 떨어지는 조각(Current Piece)의 위치를 파악하고,
|
||||
// TODO: 2. 아래 칸이 막혔는지 (충돌 판정), 막혔다면 그릴 그리드 상태를 업데이트해야 합니다.
|
||||
|
||||
// 임시 로직: 모든 셀을 한 번씩 '낙하'시키는 시뮬레이션
|
||||
let newGridState = JSON.parse(JSON.stringify(gridState)); // 깊은 복사
|
||||
for (let y = GRID_HEIGHT - 2; y >= 0; y--) {
|
||||
for (let x = 0; x < GRID_WIDTH; x++) {
|
||||
// 예시: 현재 칸에 무언가 있다면, 한 줄 아래로 내려갑니다.
|
||||
if (gridState[y][x] !== 0) {
|
||||
newGridState[y + 1][x] = gridState[y][x]; // 다음 위치에 복사
|
||||
newGridState[y][x] = 0; // 현재 위치는 비움
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
gridState = newGridState;
|
||||
|
||||
// TODO: 라인 클리어 체크 및 점수 계산 로직 추가
|
||||
const linesCleared = 1; // 예시 값
|
||||
if (linesCleared > 0) {
|
||||
score += linesCleared * 100;
|
||||
}
|
||||
|
||||
postMessage({
|
||||
type: 'UPDATE',
|
||||
data: { grid: gridState, score: score }
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
// Web Worker 이벤트 리스너 설정 (메인 스레드로부터 메시지 수신)
|
||||
self.onmessage = function(event) {
|
||||
const data = event.data;
|
||||
|
||||
switch (data.type) {
|
||||
case 'INIT':
|
||||
initializeGame();
|
||||
break;
|
||||
case 'MOVE_LEFT':
|
||||
case 'MOVE_RIGHT':
|
||||
case 'ROTATE':
|
||||
case 'HARD_DROP':
|
||||
// TODO: 이동/회전 로직 구현 및 충돌 검사 후 상태 업데이트
|
||||
console.log(`Received move command: ${data.command}`);
|
||||
break;
|
||||
case 'TICK':
|
||||
gameTick(); // 게임 틱 실행
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
// 초기 메시지 전송 (워커가 로드되었음을 알림)
|
||||
self.postMessage({ type: 'READY' });
|
||||
Reference in New Issue
Block a user