游戏概述
“弹弹球”小游戏通过HTML5 Canvas、CSS7以及JavaScript实现。
玩家使用键盘控制挡板,反弹小球,避免小球掉落。
游戏界面为粉色,小球速度和颜色会随机变化。
使用工具
本篇文章有用到GPT-4o代码纠错,国内可稳定使用,感兴趣的大佬试试363Ai工具箱。
游戏玩法
- 控制挡板:使用键盘上的左右箭头键,移动挡板接住反弹的小球。
- 目标:尽可能长时间地保持小球在画布上方反弹。
- 失败条件:小球未被接住时,屏幕会显示“哈哈!你真笨!”,提示玩家重新尝试。
实现步骤
HTML结构
HTML部分定义了游戏的基本结构,包括画布、按钮和消息显示区。
代码展示:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<style>
/* 样式在这里定义 */
</style>
<title>弹弹球游戏</title>
</head>
<body>
<div id="message">哈哈!你真笨!</div>
<canvas id="gameCanvas" width="800" height="600"></canvas>
<button id="startButton">开始</button>
<button id="endButton">结束</button>
<script>
/* JavaScript代码在这里定义 */
</script>
</body>
</html>
运行界面:
解释:HTML页面包含一个canvas元素用于游戏绘制,两个按钮用于控制游戏开始和结束,还有一个显示失败信息的div。
CSS样式
代码展示:
body {
margin: 0;
overflow: hidden;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
height: 100vh;
background-color: pink;
}
canvas {
background: pink;
display: block;
border: 3px solid #fff;
}
#message {
position: absolute;
color: white;
font-size: 48px;
display: none;
}
button {
margin: 10px;
padding: 10px 20px;
font-size: 16px;
}
解释:页面使用粉色背景,canvas也为粉色。按钮样式简单,居中显示。
#message在游戏失败时显示信息。
JavaScript实现
JavaScript负责游戏的动态逻辑,包括小球和挡板的运动、碰撞检测和用户交互。
const canvas = document.getElementById('gameCanvas');
const ctx = canvas.getContext('2d');
const message = document.getElementById('message');
const startButton = document.getElementById('startButton');
const endButton = document.getElementById('endButton');
function randomSpeed() {
return (Math.random() * 4 + 2) * (Math.random() < 0.5 ? 1 : -1);
}
let ball = {
x: canvas.width / 2,
y: canvas.height / 2,
dx: randomSpeed(),
dy: randomSpeed(),
radius: 10,
color: '#00FFFF'
};
let paddle = {
width: 100,
height: 10,
x: canvas.width / 2 - 50,
color: '#FFFFFF', // 白色挡板
speed: 10 // 减慢速度
};
let rightPressed = false;
let leftPressed = false;
let animationId;
let isRunning = false;
解释:
- 元素获取:获取canvas和按钮元素。
- 随机速度函数:randomSpeed函数为小球生成随机速度。
- 对象初始化:定义ball和paddle对象,包含位置、速度和颜色属性。
绘制函数
负责在Canvas上绘制小球和挡板。
function drawBall() {
ctx.beginPath();
ctx.arc(ball.x, ball.y, ball.radius, 0, Math.PI * 2);
ctx.fillStyle = ball.color;
ctx.fill();
ctx.closePath();
}
function drawPaddle() {
ctx.beginPath();
ctx.rect(paddle.x, canvas.height - paddle.height, paddle.width, paddle.height);
ctx.fillStyle = paddle.color;
ctx.fill();
ctx.closePath();
}
解释:drawBall和drawPaddle函数在Canvas上绘制小球和挡板。
运动和碰撞逻辑
处理小球和挡板的运动逻辑及碰撞检测。
function movePaddle() {
if (rightPressed && paddle.x < canvas.width - paddle.width) {
paddle.x += paddle.speed;
}
if (leftPressed && paddle.x > 0) {
paddle.x -= paddle.speed;
}
}
function changeBallColor() {
const colors = ['#FF5733', '#33FF57', '#3357FF', '#FF33A8', '#F3FF33'];
ball.color = colors[Math.floor(Math.random() * colors.length)];
}
function updateBall() {
ball.x += ball.dx;
ball.y += ball.dy;
if (ball.x + ball.radius > canvas.width || ball.x - ball.radius < 0) {
ball.dx = -ball.dx;
changeBallColor();
}
if (ball.y - ball.radius < 0) {
ball.dy = -ball.dy;
changeBallColor();
} else if (ball.y + ball.radius > canvas.height - paddle.height) {
if (ball.x > paddle.x && ball.x < paddle.x + paddle.width) {
ball.dy = -ball.dy;
changeBallColor();
} else {
message.style.display = 'block';
cancelAnimationFrame(animationId);
isRunning = false;
}
}
}
解释:
- 挡板移动:movePaddle根据键盘输入移动挡板。
- 颜色变化:changeBallColor在小球碰撞时改变其颜色。
- 更新逻辑:updateBall处理小球的运动和碰撞检测,碰撞后改变方向和颜色。
游戏循环
负责游戏的持续更新和重绘。
负责游戏的持续更新和重绘。
function draw() {
if (!isRunning) return;
ctx.clearRect(0, 0, canvas.width, canvas.height);
drawBall();
drawPaddle();
movePaddle();
updateBall();
animationId = requestAnimationFrame(draw);
}
function resetGame() {
ball.x = canvas.width / 2;
ball.y = canvas.height / 2;
ball.dx = randomSpeed();
ball.dy = randomSpeed();
message.style.display = 'none';
}
解释:
- 游戏循环:draw函数负责游戏的持续更新和重绘。
- 重置游戏:resetGame重置小球位置和速度,隐藏失败信息。
事件监听
处理用户输入和按钮点击事件。
document.addEventListener('keydown', (e) => {
if (e.key === 'ArrowRight') {
rightPressed = true;
} else if (e.key === 'ArrowLeft') {
leftPressed = true;
}
});
document.addEventListener('keyup', (e) => {
if (e.key === 'ArrowRight') {
rightPressed = false;
} else if (e.key === 'ArrowLeft') {
leftPressed = false;
}
});
startButton.addEventListener('click', () => {
if (!isRunning) {
resetGame();
isRunning = true;
draw();
}
});
endButton.addEventListener('click', () => {
isRunning = false;
cancelAnimationFrame(animationId);
message.style.display = 'none';
});
解释:
- 键盘监听:监听左右箭头按键,控制挡板移动。
- 按钮事件:监听开始和结束按钮,控制游戏状态。
以下是完整的代码:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<style>
body {
margin: 0;
overflow: hidden;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
height: 100vh;
background-color: pink;
}
canvas {
background: pink;
display: block;
border: 3px solid #fff;
}
#message {
position: absolute;
color: white;
font-size: 48px;
display: none;
}
button {
margin: 10px;
padding: 10px 20px;
font-size: 16px;
}
</style>
<title>弹弹球游戏</title>
</head>
<body>
<div id="message">哈哈!你真笨!</div>
<canvas id="gameCanvas" width="800" height="600"></canvas>
<button id="startButton">开始</button>
<button id="endButton">结束</button>
<script>
const canvas = document.getElementById('gameCanvas');
const ctx = canvas.getContext('2d');
const message = document.getElementById('message');
const startButton = document.getElementById('startButton');
const endButton = document.getElementById('endButton');
function randomSpeed() {
return (Math.random() * 4 + 2) * (Math.random() < 0.5 ? 1 : -1);
}
let ball = {
x: canvas.width / 2,
y: canvas.height / 2,
dx: randomSpeed(),
dy: randomSpeed(),
radius: 10,
color: '#00FFFF'
};
let paddle = {
width: 100,
height: 10,
x: canvas.width / 2 - 50,
color: '#FFFFFF',
speed: 10
};
let rightPressed = false;
let leftPressed = false;
let animationId;
let isRunning = false;
function drawBall() {
ctx.beginPath();
ctx.arc(ball.x, ball.y, ball.radius, 0, Math.PI * 2);
ctx.fillStyle = ball.color;
ctx.fill();
ctx.closePath();
}
function drawPaddle() {
ctx.beginPath();
ctx.rect(paddle.x, canvas.height - paddle.height, paddle.width, paddle.height);
ctx.fillStyle = paddle.color;
ctx.fill();
ctx.closePath();
}
function movePaddle() {
if (rightPressed && paddle.x < canvas.width - paddle.width) {
paddle.x += paddle.speed;
}
if (leftPressed && paddle.x > 0) {
paddle.x -= paddle.speed;
}
}
function changeBallColor() {
const colors = ['#FF5733', '#33FF57', '#3357FF', '#FF33A8', '#F3FF33'];
ball.color = colors[Math.floor(Math.random() * colors.length)];
}
function updateBall() {
ball.x += ball.dx;
ball.y += ball.dy;
if (ball.x + ball.radius > canvas.width || ball.x - ball.radius < 0) {
ball.dx = -ball.dx;
changeBallColor();
}
if (ball.y - ball.radius < 0) {
ball.dy = -ball.dy;
changeBallColor();
} else if (ball.y + ball.radius > canvas.height - paddle.height) {
if (ball.x > paddle.x && ball.x < paddle.x + paddle.width) {
ball.dy = -ball.dy;
changeBallColor();
} else {
message.style.display = 'block';
cancelAnimationFrame(animationId);
isRunning = false;
}
}
}
function draw() {
if (!isRunning) return;
ctx.clearRect(0, 0, canvas.width, canvas.height);
drawBall();
drawPaddle();
movePaddle();
updateBall();
animationId = requestAnimationFrame(draw);
}
function resetGame() {
ball.x = canvas.width / 2;
ball.y = canvas.height / 2;
ball.dx = randomSpeed();
ball.dy = randomSpeed();
message.style.display = 'none';
}
document.addEventListener('keydown', (e) => {
if (e.key === 'ArrowRight') {
rightPressed = true;
} else if (e.key === 'ArrowLeft') {
leftPressed = true;
}
});
document.addEventListener('keyup', (e) => {
if (e.key === 'ArrowRight') {
rightPressed = false;
} else if (e.key === 'ArrowLeft') {
leftPressed = false;
}
});
startButton.addEventListener('click', () => {
if (!isRunning) {
resetGame();
isRunning = true;
draw();
}
});
endButton.addEventListener('click', () => {
isRunning = false;
cancelAnimationFrame(animationId);
message.style.display = 'none';
});
</script>
</body>
</html>
代码解析
- HTML:定义了游戏的基本结构,包括画布、按钮和消息显示区。
- CSS:设置了页面样式,粉色背景,居中显示的按钮和消息。
- JavaScript:
- 初始化游戏元素和状态。
- 定义小球和挡板的属性和行为。
- 实现小球的随机速度和颜色变化。
- 处理键盘输入以控制挡板移动。
- 使用requestAnimationFrame实现动画循环。
运行界面:
学习收获
通过这个项目,初学者可以掌握:
- HTML5 Canvas的基本用法
- JavaScript中对象和方法的使用
- 动画循环和用户交互的实现
- 简单的碰撞检测和游戏逻辑
这不仅是一个娱乐项目,也是一个学习和实践编程的好机会。通过不断调整和优化代码,玩家可以深入理解游戏开发的基本概念,为未来更复杂的项目打下坚实基础。
感谢阅读!!!