1. 从零开始构建HTML+JS+CSS飞机游戏
最近在整理前端教学案例时,发现很多初学者对游戏开发既向往又畏惧。其实用基础的HTML+JS+CSS三件套就能做出可玩的游戏,今天我就带大家实现一个完整的飞机射击游戏。这个项目特别适合刚学完前端基础想练手的同学,全程只需200行左右代码,但包含了游戏开发的核心逻辑。
我选择飞机游戏作为案例,是因为它的游戏元素简单(玩家飞机、敌机、子弹),碰撞检测直观(矩形碰撞),同时又能覆盖前端游戏开发的主要技术点。下面这个成品支持键盘控制移动、自动射击、敌机生成、分数计算等基础功能,在Chrome和Firefox等现代浏览器上都能流畅运行。
提示:建议先准备好代码编辑器(VSCode/Sublime等)和浏览器开发者工具(F12),我们将边写代码边讲解原理。
2. 游戏架构设计与核心文件结构
2.1 HTML骨架搭建
游戏的主入口文件是index.html,结构非常简洁:
html复制<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>简易飞机大战</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<div id="game-container">
<div id="player"></div>
<div id="score">分数: 0</div>
</div>
<script src="game.js"></script>
</body>
</html>
关键点说明:
- 使用标准HTML5文档声明
- 设置视口适配移动端(虽然本游戏主要针对桌面浏览器)
- 游戏元素全部用div实现,通过CSS控制样式
- 分离的CSS和JS文件便于维护
2.2 CSS样式设计
在style.css中,我们采用相对单位实现响应式布局:
css复制body {
margin: 0;
overflow: hidden;
background: #222;
}
#game-container {
position: relative;
width: 100vw;
height: 100vh;
background: linear-gradient(to bottom, #1a1a2e, #16213e);
}
#player {
position: absolute;
width: 50px;
height: 50px;
background: url('player.png') center/contain no-repeat;
bottom: 20px;
left: 50%;
transform: translateX(-50%);
}
.enemy {
position: absolute;
width: 40px;
height: 40px;
background: url('enemy.png') center/contain no-repeat;
}
.bullet {
position: absolute;
width: 5px;
height: 15px;
background: #ffcc00;
border-radius: 2px;
}
#score {
position: absolute;
top: 10px;
right: 20px;
color: white;
font-family: Arial, sans-serif;
font-size: 24px;
text-shadow: 0 0 5px black;
}
样式设计技巧:
- 使用vw/vh单位实现全屏游戏
- 背景采用CSS渐变而非图片,减少资源加载
- 游戏元素使用绝对定位方便JS控制位置
- 为分数添加文字阴影提升可读性
2.3 JavaScript游戏逻辑
game.js是核心文件,我们采用面向过程的方式组织代码:
javascript复制// 游戏状态
const state = {
playerX: 0,
playerSpeed: 8,
bullets: [],
enemies: [],
score: 0,
gameOver: false
};
// 初始化游戏
function init() {
const player = document.getElementById('player');
state.playerX = window.innerWidth / 2 - 25;
player.style.left = `${state.playerX}px`;
document.addEventListener('keydown', handleKeyDown);
document.addEventListener('keyup', handleKeyUp);
// 游戏主循环
setInterval(gameLoop, 1000/60);
setInterval(spawnEnemy, 2000);
}
// 键盘控制
function handleKeyDown(e) {
if (e.key === 'ArrowLeft') {
state.moveLeft = true;
} else if (e.key === 'ArrowRight') {
state.moveRight = true;
}
}
// 游戏主逻辑
function gameLoop() {
if (state.gameOver) return;
updatePlayer();
updateBullets();
updateEnemies();
checkCollisions();
}
3. 核心游戏机制实现
3.1 玩家控制与移动
玩家飞机的移动通过监听键盘事件实现:
javascript复制let keys = {};
function handleKeyDown(e) {
keys[e.key] = true;
}
function handleKeyUp(e) {
keys[e.key] = false;
}
function updatePlayer() {
const player = document.getElementById('player');
const gameWidth = window.innerWidth;
const playerWidth = 50;
if (keys['ArrowLeft'] && state.playerX > 0) {
state.playerX -= state.playerSpeed;
}
if (keys['ArrowRight'] && state.playerX < gameWidth - playerWidth) {
state.playerX += state.playerSpeed;
}
player.style.left = `${state.playerX}px`;
// 自动射击
if (Date.now() - (state.lastShot || 0) > 300) {
shoot();
state.lastShot = Date.now();
}
}
这里有几个优化点:
- 使用keys对象存储按键状态,避免连续按键的卡顿
- 边界检测防止飞机移出屏幕
- 节流控制射击频率(每300ms一发)
3.2 子弹系统实现
子弹需要管理创建、移动和销毁的全生命周期:
javascript复制function shoot() {
const bullet = document.createElement('div');
bullet.className = 'bullet';
bullet.style.left = `${state.playerX + 22}px`;
bullet.style.bottom = '70px';
document.getElementById('game-container').appendChild(bullet);
state.bullets.push({
element: bullet,
x: state.playerX + 22,
y: window.innerHeight - 70,
speed: 10
});
}
function updateBullets() {
state.bullets.forEach((bullet, index) => {
bullet.y -= bullet.speed;
bullet.element.style.bottom = `${bullet.y}px`;
// 移除超出屏幕的子弹
if (bullet.y < 0) {
bullet.element.remove();
state.bullets.splice(index, 1);
}
});
}
子弹管理注意事项:
- 子弹位置要基于玩家当前位置计算
- 使用数组存储所有子弹实例
- 及时移除超出屏幕的子弹释放内存
3.3 敌机生成与移动
敌机系统需要随机生成并向下移动:
javascript复制function spawnEnemy() {
if (state.gameOver) return;
const enemy = document.createElement('div');
enemy.className = 'enemy';
const x = Math.random() * (window.innerWidth - 40);
enemy.style.left = `${x}px`;
enemy.style.top = '-40px';
document.getElementById('game-container').appendChild(enemy);
state.enemies.push({
element: enemy,
x: x,
y: -40,
speed: 3 + Math.random() * 2
});
}
function updateEnemies() {
state.enemies.forEach((enemy, index) => {
enemy.y += enemy.speed;
enemy.element.style.top = `${enemy.y}px`;
// 移除超出屏幕的敌机
if (enemy.y > window.innerHeight) {
enemy.element.remove();
state.enemies.splice(index, 1);
}
});
}
敌机设计要点:
- 随机生成位置和速度增加游戏变化
- 使用负数初始位置实现"飞入"效果
- 同样需要管理敌机实例的生命周期
4. 碰撞检测与游戏逻辑
4.1 矩形碰撞检测
实现子弹与敌机的碰撞检测:
javascript复制function checkCollisions() {
// 子弹与敌机碰撞
state.bullets.forEach((bullet, bIndex) => {
state.enemies.forEach((enemy, eIndex) => {
if (isColliding(bullet, enemy)) {
// 移除子弹和敌机
bullet.element.remove();
enemy.element.remove();
state.bullets.splice(bIndex, 1);
state.enemies.splice(eIndex, 1);
// 增加分数
state.score += 10;
document.getElementById('score').textContent = `分数: ${state.score}`;
}
});
});
// 玩家与敌机碰撞
const player = document.getElementById('player');
state.enemies.forEach((enemy, index) => {
if (isColliding({
x: state.playerX,
y: window.innerHeight - 70,
width: 50,
height: 50
}, enemy)) {
gameOver();
}
});
}
function isColliding(obj1, obj2) {
return obj1.x < obj2.x + 40 &&
obj1.x + (obj1.width || 5) > obj2.x &&
obj1.y < obj2.y + 40 &&
obj1.y + (obj1.height || 15) > obj2.y;
}
碰撞检测优化:
- 使用简化的矩形碰撞检测
- 为不同元素设置合适的碰撞体积
- 碰撞后立即更新游戏状态
4.2 游戏结束逻辑
实现游戏结束的判断与处理:
javascript复制function gameOver() {
state.gameOver = true;
const gameOverText = document.createElement('div');
gameOverText.textContent = '游戏结束! 最终分数: ' + state.score;
gameOverText.style.position = 'absolute';
gameOverText.style.top = '50%';
gameOverText.style.left = '50%';
gameOverText.style.transform = 'translate(-50%, -50%)';
gameOverText.style.color = 'white';
gameOverText.style.fontSize = '36px';
gameOverText.style.fontFamily = 'Arial, sans-serif';
gameOverText.style.textShadow = '0 0 10px red';
document.getElementById('game-container').appendChild(gameOverText);
// 禁用控制
document.removeEventListener('keydown', handleKeyDown);
document.removeEventListener('keyup', handleKeyUp);
}
游戏结束处理要点:
- 显示醒目的结束提示
- 停止所有游戏逻辑
- 移除事件监听防止误操作
5. 性能优化与扩展建议
5.1 游戏性能优化
对于这类小游戏,仍有优化空间:
javascript复制// 使用requestAnimationFrame替代setInterval
function startGameLoop() {
function loop() {
if (!state.gameOver) {
gameLoop();
requestAnimationFrame(loop);
}
}
requestAnimationFrame(loop);
}
// 对象池优化
const bulletPool = [];
const enemyPool = [];
function getBullet() {
if (bulletPool.length > 0) {
return bulletPool.pop();
}
const bullet = document.createElement('div');
bullet.className = 'bullet';
return bullet;
}
function recycleBullet(bullet) {
bullet.style.display = 'none';
bulletPool.push(bullet);
}
优化建议:
- requestAnimationFrame提供更流畅的动画
- 对象池减少DOM操作开销
- 节流事件处理减少计算量
5.2 游戏功能扩展
如果想进一步完善游戏:
javascript复制// 添加音效
function playSound(type) {
const audio = new Audio();
audio.src = type === 'shoot' ? 'shoot.wav' : 'explosion.wav';
audio.volume = 0.3;
audio.play();
}
// 添加关卡系统
let level = 1;
function checkLevelUp() {
if (state.score >= level * 100) {
level++;
state.playerSpeed += 1;
// 提高敌机生成频率
clearInterval(state.spawnInterval);
state.spawnInterval = setInterval(spawnEnemy, 2000 / level);
}
}
扩展方向:
- 添加音效和背景音乐
- 实现关卡难度递增
- 加入多种敌机和武器类型
- 添加开始菜单和暂停功能
这个飞机游戏虽然简单,但完整展示了前端游戏开发的核心技术链。我在实际教学中发现,学生通过实现这个项目,能深刻理解事件驱动编程、游戏循环、碰撞检测等关键概念。建议初学者在完成基础版本后,尝试自己添加新功能,比如敌机发射子弹、能量系统等,这对提升编程能力很有帮助。
