今天要來把第一個遊戲“Brick Breaker”做個收尾。
第一件事情是處理原先resetBrick放的位置,原本是放在window.onload中,修改成下面這樣
window.onload = () => {
canvas = document.getElementById('playground');
canvasContext = canvas.getContext('2d');
//一秒更新幾次畫面
var timesPerSec = 30;
setInterval(drawAll, 1000 / timesPerSec);
canvas.addEventListener('mousemove', mousePos);
resetBrick();
}
做一次resetBrick,來畫出第一次對舉陣陣列.
昨天的遊戲如果正常執行會遇到一個問題,當所有磚塊被消除時,磚塊並不會重置,這是因為檢查的if條件並沒有寫在我們定期執行的函式drawall
中,所以只有第一次進入遊戲時會執行,要解決這問題,我嗎可已把判斷式移到跟paddle互動觸發時.
// paddle互動
actionPaddle = () => {
// 定義paddle四個角的座標
var paddleTopEdgeY = canvas.height - PADDLE_HEIGHT - PADDLE_HIGH;
var paddleBottomEdgeY = paddleTopEdgeY + PADDLE_HEIGHT;
var paddleLeftEdgeX = paddle_x;
var paddleRightEdgeX = paddleLeftEdgeX + PADDLE_WIDTH;
// 碰到paddle反彈
if (ball_y > (paddleTopEdgeY - ball_r) &&
ball_y < paddleBottomEdgeY &&
ball_x > paddleLeftEdgeX &&
ball_x < paddleRightEdgeX) {
// 如果沒磚塊重置
if (bricks_left == 0) {
resetBrick();
}
// 加速
ball_speed += 0.5;
// paddle中心
var centerOfPaddleX = paddle_x + PADDLE_WIDTH / 2;
// 計算X軸球速
ball_speed_x = ball_speed * ((ball_x - centerOfPaddleX) / (0.51 * PADDLE_WIDTH));
// 計算Y軸球速
ball_speed_y = -1 * countBallSpeedY(ball_speed, ball_speed_x);
}
}
好啦~ 稍微整理一下程式排版,我們第一個遊戲算是完成了.總共花了八天的時間,比我想得快多了,看來有望在這30天完成3個遊戲左右.
繼續加油吧,以下附上完整的code
<!DOCTYPE html>
<html lang="en">
<head>
<title>First Game</title>
<meta name="description" content="第一個遊戲">
<meta content="text/html;charset=utf-8" http-equiv="Content-Type">
<meta content="utf-8" http-equiv="encoding">
</head>
<body>
<canvas id="playground" width="800" height="630"></canvas>
<script>
var canvas, canvasContext;
// 球的參數
var ball_x = 300;
var ball_y = 300;
var ball_r = 8;
var ball_speed = 5;
var ball_speed_x = 4;
var ball_speed_y = 3;
// paddle參數
const PADDLE_WIDTH = 100;
const PADDLE_HEIGHT = 10;
const PADDLE_HIGH = 50; // paddle 距離底部高度
var paddle_x = 400;
// 磚參數
const BRICK_WIDTH = 80;
const BRICK_HEIGHT = 30;
const BRICK_GAP = 2;
const BRICK_COLS = 10;
const BRICK_ROWS = 9;
var brick_grid = new Array(BRICK_COLS * BRICK_ROWS);
var bricks_left = 0;
// 生命設定
const LIFE_SIZE = 30;
var chance = 3;
window.onload = () => {
canvas = document.getElementById('playground');
canvasContext = canvas.getContext('2d');
//一秒更新幾次畫面
var timesPerSec = 30;
setInterval(drawAll, 1000 / timesPerSec);
canvas.addEventListener('mousemove', mousePos);
resetBrick();
}
// 負責更新畫面
drawAll = () => {
move();
draw();
}
// 負責畫畫
draw = () => {
// background
drawRectangle(0, 0, canvas.width, canvas.height, 'black');
// paddle
drawRectangle(paddle_x, canvas.height - PADDLE_HEIGHT - PADDLE_HIGH,
PADDLE_WIDTH, PADDLE_HEIGHT, 'white');
// 畫球
drawCircle(ball_x, ball_y, ball_r, 'red');
// 畫線
drawLine(0, LIFE_SIZE, canvas.width, LIFE_SIZE, 'white')
// 畫出生命
showLife(chance);
// 畫磚
showBricks();
}
// 負責處理動作
move = () => {
// 處理球的動作
moveBall()
// paddle互動
actionPaddle()
// 磚互動
actionBrick()
}
// 球移動
moveBall = () => {
ball_x += ball_speed_x;
ball_y += ball_speed_y;
// 碰觸邊界動作
if (ball_x < (0 + ball_r)) {
ball_speed_x *= -1;
}
if (ball_x > (canvas.width - ball_r)) {
ball_speed_x *= -1;
}
if (ball_y < (LIFE_SIZE + ball_r)) {
ball_speed_y *= -1;
}
if (ball_y > canvas.height) {
resetBall();
chance -= 1;
if (chance < 0) {
chance = 3;
}
}
}
// paddle互動
actionPaddle = () => {
// 定義paddle四個角的座標
var paddleTopEdgeY = canvas.height - PADDLE_HEIGHT - PADDLE_HIGH;
var paddleBottomEdgeY = paddleTopEdgeY + PADDLE_HEIGHT;
var paddleLeftEdgeX = paddle_x;
var paddleRightEdgeX = paddleLeftEdgeX + PADDLE_WIDTH;
// 碰到paddle反彈
if (ball_y > (paddleTopEdgeY - ball_r) &&
ball_y < paddleBottomEdgeY &&
ball_x > paddleLeftEdgeX &&
ball_x < paddleRightEdgeX) {
// 如果沒磚塊重置
if (bricks_left == 0) {
resetBrick();
}
// 加速
ball_speed += 0.5;
// paddle中心
var centerOfPaddleX = paddle_x + PADDLE_WIDTH / 2;
// 計算X軸球速
ball_speed_x = ball_speed * ((ball_x - centerOfPaddleX) / (0.51 * PADDLE_WIDTH));
// 計算Y軸球速
ball_speed_y = -1 * countBallSpeedY(ball_speed, ball_speed_x);
}
}
//確認磚塊是不是存在
isBrickExist = (col, row) => {
if (col >= 0 && col < BRICK_COLS &&
row >= 0 && row < BRICK_ROWS) {
var brickIndexUnderCoord = rowColToArrayIndex(col, row);
return brick_grid[brickIndexUnderCoord];
} else {
return false;
}
}
// 碰到磚的動作
actionBrick = () => {
// 球的座標轉換成磚塊的陣列位置
var ballBrickCol = Math.floor(ball_x / BRICK_WIDTH);
var ballBrickRow = Math.floor(ball_y / BRICK_HEIGHT);
var brickIndexUnderBall = rowColToArrayIndex(ballBrickCol, ballBrickRow);
// 判斷球的位置是否在我們設計有磚塊的區域內
if (ballBrickCol >= 0 && ballBrickCol < BRICK_COLS &&
ballBrickRow >= 0 && ballBrickRow < BRICK_ROWS) {
// 確認該位置的磚塊在不在
if (isBrickExist(ballBrickCol, ballBrickRow)) {
// 消除方塊
brick_grid[brickIndexUnderBall] = false;
// 剩餘磚塊的數量減一
bricks_left--;
console.log(bricks_left);
// 方便判斷撞擊位置的參數
var prevBallX = ball_x - ball_speed_x;
var prevBallY = ball_y - ball_speed_y;
var prevBrickCol = Math.floor(prevBallX / BRICK_WIDTH);
var prevBrickRow = Math.floor(prevBallY / BRICK_HEIGHT);
// 預設非側邊或上下面撞擊
var bothTestFailed = true;
// 側邊撞擊
if (prevBrickCol != ballBrickCol) {
if (isBrickExist(ballBrickCol, ballBrickRow) == false) {
ball_speed_x *= -1;
bothTestFailed = false;
}
}
// 上下面撞擊
if (prevBrickRow != ballBrickRow) {
if (isBrickExist(ballBrickCol, ballBrickRow) == false) {
ball_speed_y *= -1;
bothTestFailed = false;
}
}
// 撞到角的情況
if (bothTestFailed) {
ball_speed_x *= -1;
ball_speed_y *= -1;
}
}
}
}
// 矩形元件
drawRectangle = (topLeftX, topLeftY, boxWidth, boxHeight, color) => {
canvasContext.fillStyle = color;
canvasContext.fillRect(topLeftX, topLeftY, boxWidth, boxHeight);
}
// 圓形元件
drawCircle = (centerX, centerY, r, color) => {
canvasContext.fillStyle = color;
canvasContext.beginPath();
canvasContext.arc(centerX, centerY, r, 0, Math.PI * 2);
canvasContext.fill();
}
// 畫線
drawLine = (beginX, beginY, endX, endY, color) => {
canvasContext.strokeStyle = color;
canvasContext.beginPath();
canvasContext.moveTo(beginX, beginY);
canvasContext.lineTo(endX, endY);
canvasContext.stroke();
}
// 畫生命
drawLife = (lifeX, lifeY, size, color) => {
canvasContext.fillStyle = color;
canvasContext.beginPath();
canvasContext.arc(lifeX + size / 4, lifeY + size / 4, size / 4, Math.PI * 1, Math.PI * 0);
canvasContext.arc(lifeX + size * 3 / 4, lifeY + size / 4, size / 4, Math.PI * 1, Math.PI * 0);
canvasContext.moveTo(lifeX + size, lifeY + size / 4);
canvasContext.lineTo(lifeX + size / 2, lifeY + size);
canvasContext.lineTo(lifeX, lifeY + size / 4);
canvasContext.fill();
}
// 畫出生命
showLife = (num) => {
for (let life_count = 0; life_count < num; life_count++) {
drawLife(LIFE_SIZE * life_count, 0, LIFE_SIZE, 'red');
}
}
// 游標位置
mousePos = (event) => {
var rect = canvas.getBoundingClientRect();
var root = document.documentElement;
var mouse_x = event.clientX - rect.left - root.scrollLeft;
// 設定paddle控制點在中央
paddle_x = mouse_x - PADDLE_WIDTH / 2;
}
// 重置球
resetBall = () => {
ball_x = 300;
ball_y = 300;
ball_speed = 8;
ball_speed_x = 5;
ball_speed_y = countBallSpeedY(ball_speed, ball_speed_x);
}
// 重置磚
resetBrick = () => {
bricks_left = 0;
var i;
for (i = 0; i < 3 * BRICK_COLS; i++) {
brick_grid[i] = false;
}
for (; i < BRICK_COLS * BRICK_ROWS; i++) {
brick_grid[i] = true;
bricks_left++;
}
}
// 轉換磚塊位置
rowColToArrayIndex = (col, row) => {
return col + BRICK_COLS * row;
}
// 顯示磚塊
showBricks = () => {
for (var count_rows = 0; count_rows < BRICK_ROWS; count_rows++) {
for (var count_cols = 0; count_cols < BRICK_COLS; count_cols++) {
var arrayIndex = rowColToArrayIndex(count_cols, count_rows);
if (brick_grid[arrayIndex]) {
drawRectangle(BRICK_WIDTH * count_cols, BRICK_HEIGHT * count_rows, BRICK_WIDTH - BRICK_GAP, BRICK_HEIGHT - BRICK_GAP, 'blue');
}
}
}
}
// 計算Y軸球速
countBallSpeedY = (speed, speedX) => {
return (speed ** 2 - speedX ** 2) ** 0.5;
}
</script>
</body>
</html>