import "./styles.css";
import useBall from "./hooks/useBall";
import useBoard from "./hooks/useBoard";
import React, { useEffect, useState } from "react";
const width = 500;
const height = 500;
const refreshInterval = 50;
//建立一個小球
const ballRadius = width / 30;
const ballX = width / 2 - ballRadius;
//建立一個擋板
const boardWidth = width / 6;
const boardHeight = 10;
const boardX = (width - boardWidth) / 2;
const boardY = height - 100;
export default function App() {
const [started, setSarted] = useState(false);
const [stateCode, setSateCode] = useState(0);
const [timer, setTimer] = useState(null);
const [context, setConText] = useState(null);
const { board, reset: resetBoard, drawMe: drawBoard, setBoard } = useBoard(
boardX,
boardY,
boardWidth,
boardHeight,
context
);
const {
ball,
move: moveBall,
drawMe: drawBall,
reset: resetBall,
setBall
} = useBall(ballX, 50, ballRadius, context);
console.log("board", board);
useEffect(() => {
const canvas = document.getElementById("canvas");
if (canvas) {
const ctx = canvas.getContext("2d");
setConText(ctx);
}
}, [stateCode]);
useEffect(() => {
if (context) startGame();
}, [context]);
/** 執行開始遊戲 */
const handleStartGameBtnClick = () => {
setSateCode(1);
};
const clear = () => {
if (context) {
context.clearRect(0, 0, width, height);
}
};
const startGame = () => {
resetBall();
resetBoard();
refreshGameView();
const updateTimer = setInterval(refreshGameView, refreshInterval);
setTimer(updateTimer);
setSarted(true);
};
const refreshGameView = () => {
/** 重新整理遊戲區域 */
//每次重新整理前都需要清除背景,不然小球和擋板上次的位置會被保留
clear();
const { x, y } = moveBall();
var ballX = x;
var ballY = y;
if (ballX < 0) {
setBall((prev) => ({ ...prev, x: 0, speedX: prev?.speedX * -1 }));
}
if (ballY < 0) {
setBall((prev) => ({ ...prev, y: 0, speedY: prev?.speedY * -1 }));
}
if (ballX + 2 * ball.radius > width) {
setBall((prev) => ({
...prev,
x: width - 2 * ball?.radius,
speedY: prev?.speedX * -1
}));
}
const ballBottomY = y + 2 * ball.radius;
const ballCenterX = x + ball.radius;
if (ballBottomY >= board.y) {
if (ballCenterX >= board.x && ballCenterX <= board.x + board.width) {
//反彈
setBall((prev) => ({
...prev,
speedY: prev?.speedY * -1
}));
} else {
//遊戲結束
if (timer != null) {
clearInterval(timer);
setSateCode(-1);
}
}
}
//畫小球和擋板
// drawBall();
// drawBoard();
};
const handleMouseMove = (event) => {
/** 處理滑鼠的移動事件,移動滑鼠的同時移動擋板 */
const x = getClientOffset(event)?.x;
//將擋板的水平中心位置移到x處
let boardX = x - board?.width / 2;
if (boardX < 0) {
boardX = 0;
}
if (boardX + board?.width > width) {
boardX = width - board?.width;
}
setBoard({ ...board, x: boardX });
};
/** 取得位置 */
const getClientOffset = (event) => {
const canvas = document.getElementById("canvas");
let rect = canvas.getBoundingClientRect();
const point = {
x: event.clientX - rect.left,
y: event.clientY - rect.top
};
return point;
};
/** 遊戲結束 */
const renderGameOverView = () => (
<div>
<p>遊戲結束</p>
<button className="start-game-btn" onClick={handleStartGameBtnClick}>
開始遊戲
</button>
</div>
);
/** 未開始 */
const renderStartView = () => (
<button className="start-game-btn" onClick={handleStartGameBtnClick}>
開始遊戲
</button>
);
/** 遊戲中 */
const renderGameView = () => (
<canvas
id="canvas"
onMouseMove={handleMouseMove}
width={width}
height={height}
/>
);
const stateCodeHtml = {
"-1": renderGameOverView() /** 遊戲結束 */,
0: renderStartView() /** 遊戲開始 */,
1: renderGameView() /** 遊戲畫面 */
};
return (
<div className="App">
<div id="container">{stateCodeHtml[stateCode]}</div>
</div>
);
}