|
|
马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。
您需要 登录 才可以下载或查看,没有账号?立即注册
x
引言
HTML5作为新一代的网页标准,为网页游戏开发提供了强大的支持。通过HTML5的Canvas API、Audio API等技术,开发者可以创建出功能丰富、体验流畅的网页游戏。坦克大战作为经典的电子游戏,是学习游戏开发的绝佳项目。本教程将全面解析如何使用HTML5技术从零开始开发一个完整的坦克大战游戏,帮助读者掌握游戏编程的核心技术。
HTML5游戏开发基础
HTML5游戏开发概述
HTML5游戏开发主要依赖于以下几个核心技术:
1. Canvas API:用于绘制图形、动画和游戏场景。
2. Audio API:处理游戏音效和背景音乐。
3. JavaScript:实现游戏逻辑和交互。
4. RequestAnimationFrame:创建流畅的游戏循环。
与传统Flash游戏相比,HTML5游戏无需插件,跨平台兼容性好,可以直接在浏览器中运行。
Canvas基础
Canvas是HTML5中最重要的游戏开发元素,它提供了一个可以通过JavaScript绘制的位图画布。
- <!DOCTYPE html>
- <html>
- <head>
- <title>坦克大战游戏</title>
- <style>
- body {
- margin: 0;
- padding: 0;
- display: flex;
- justify-content: center;
- align-items: center;
- height: 100vh;
- background-color: #333;
- }
- #gameCanvas {
- border: 2px solid #fff;
- background-color: #000;
- }
- </style>
- </head>
- <body>
- <canvas id="gameCanvas" width="800" height="600"></canvas>
- <script src="game.js"></script>
- </body>
- </html>
复制代码
在JavaScript中获取Canvas上下文:
- // 获取Canvas元素和上下文
- const canvas = document.getElementById('gameCanvas');
- const ctx = canvas.getContext('2d');
- // 设置Canvas尺寸
- canvas.width = 800;
- canvas.height = 600;
复制代码
游戏循环
游戏循环是游戏开发的核心概念,它负责更新游戏状态和渲染游戏画面。
- // 游戏循环
- function gameLoop() {
- update(); // 更新游戏状态
- render(); // 渲染游戏画面
- requestAnimationFrame(gameLoop); // 请求下一帧
- }
- // 启动游戏循环
- gameLoop();
复制代码
游戏设计与规划
游戏需求分析
在开发坦克大战游戏前,我们需要明确游戏的核心功能和需求:
1. 玩家控制:玩家可以控制坦克移动和发射子弹。
2. 敌方AI:敌方坦克具有基本的移动和攻击AI。
3. 地图设计:包含障碍物、墙壁等元素。
4. 碰撞检测:坦克与墙壁、子弹与坦克之间的碰撞。
5. 游戏状态:开始、进行中、暂停、结束等状态管理。
6. 计分系统:记录玩家得分。
7. 关卡设计:多个难度递增的关卡。
游戏架构设计
采用面向对象的方式设计游戏架构:
1. Game类:游戏主控制器,管理游戏状态和资源。
2. Tank类:坦克基类,包含玩家坦克和敌方坦克的公共属性和方法。
3. PlayerTank类:玩家坦克类,继承自Tank类。
4. EnemyTank类:敌方坦克类,继承自Tank类。
5. Bullet类:子弹类,处理子弹的移动和碰撞。
6. Map类:地图类,管理游戏地图和障碍物。
7. Explosion类:爆炸效果类,处理坦克被击中后的爆炸动画。
游戏开发环境搭建
项目结构
一个良好的项目结构有助于代码的组织和维护:
- tank-war/
- ├── index.html # 游戏主页面
- ├── css/
- │ └── style.css # 游戏样式
- ├── js/
- │ ├── game.js # 游戏主逻辑
- │ ├── objects/ # 游戏对象
- │ │ ├── tank.js # 坦克类
- │ │ ├── bullet.js # 子弹类
- │ │ ├── map.js # 地图类
- │ │ └── explosion.js # 爆炸效果类
- │ ├── utils/ # 工具函数
- │ │ ├── collision.js # 碰撞检测
- │ │ └── helpers.js # 辅助函数
- │ └── resources/ # 资源加载
- │ └── loader.js # 资源加载器
- └── assets/ # 游戏资源
- ├── images/ # 图片资源
- └── audio/ # 音频资源
复制代码
资源加载
游戏资源包括图片、音频等,需要预加载以确保游戏正常运行:
- // 资源加载器
- class ResourceLoader {
- constructor() {
- this.images = {};
- this.audios = {};
- this.loadedCount = 0;
- this.totalCount = 0;
- }
- // 加载图片
- loadImage(key, src) {
- this.totalCount++;
- const img = new Image();
- img.src = src;
- img.onload = () => {
- this.images[key] = img;
- this.loadedCount++;
- this.checkAllLoaded();
- };
- }
- // 加载音频
- loadAudio(key, src) {
- this.totalCount++;
- const audio = new Audio();
- audio.src = src;
- audio.oncanplaythrough = () => {
- this.audios[key] = audio;
- this.loadedCount++;
- this.checkAllLoaded();
- };
- }
- // 检查所有资源是否加载完成
- checkAllLoaded() {
- if (this.loadedCount === this.totalCount) {
- this.onLoadComplete();
- }
- }
- // 资源加载完成回调
- onLoadComplete() {
- console.log('所有资源加载完成');
- }
- }
- // 使用资源加载器
- const loader = new ResourceLoader();
- loader.loadImage('playerTank', 'assets/images/player-tank.png');
- loader.loadImage('enemyTank', 'assets/images/enemy-tank.png');
- loader.loadImage('bullet', 'assets/images/bullet.png');
- loader.loadImage('wall', 'assets/images/wall.png');
- loader.loadAudio('shoot', 'assets/audio/shoot.wav');
- loader.loadAudio('explosion', 'assets/audio/explosion.wav');
复制代码
核心功能实现
游戏画布与渲染
游戏渲染是将游戏对象绘制到Canvas上的过程:
- // 游戏渲染器
- class Renderer {
- constructor(canvas, ctx) {
- this.canvas = canvas;
- this.ctx = ctx;
- }
- // 清空画布
- clear() {
- this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
- }
- // 绘制图像
- drawImage(image, x, y, width, height) {
- this.ctx.drawImage(image, x, y, width, height);
- }
- // 绘制矩形
- drawRect(x, y, width, height, color) {
- this.ctx.fillStyle = color;
- this.ctx.fillRect(x, y, width, height);
- }
- // 绘制文本
- drawText(text, x, y, color, font) {
- this.ctx.fillStyle = color;
- this.ctx.font = font || '16px Arial';
- this.ctx.fillText(text, x, y);
- }
- }
- // 使用渲染器
- const renderer = new Renderer(canvas, ctx);
复制代码
坦克对象与控制
坦克是游戏的核心对象,需要实现移动、旋转和射击等功能:
- // 坦克基类
- class Tank {
- constructor(x, y, speed, direction, color) {
- this.x = x;
- this.y = y;
- this.width = 40;
- this.height = 40;
- this.speed = speed;
- this.direction = direction; // 0: 上, 1: 右, 2: 下, 3: 左
- this.color = color;
- this.isAlive = true;
- this.cooldown = 0; // 射击冷却时间
- }
- // 移动坦克
- move() {
- switch(this.direction) {
- case 0: // 上
- this.y -= this.speed;
- break;
- case 1: // 右
- this.x += this.speed;
- break;
- case 2: // 下
- this.y += this.speed;
- break;
- case 3: // 左
- this.x -= this.speed;
- break;
- }
-
- // 边界检查
- this.x = Math.max(0, Math.min(canvas.width - this.width, this.x));
- this.y = Math.max(0, Math.min(canvas.height - this.height, this.y));
- }
- // 改变方向
- setDirection(direction) {
- this.direction = direction;
- }
- // 射击
- shoot() {
- if (this.cooldown <= 0) {
- this.cooldown = 20; // 设置冷却时间
- // 创建子弹
- let bulletX, bulletY;
- switch(this.direction) {
- case 0: // 上
- bulletX = this.x + this.width / 2 - 5;
- bulletY = this.y - 10;
- break;
- case 1: // 右
- bulletX = this.x + this.width;
- bulletY = this.y + this.height / 2 - 5;
- break;
- case 2: // 下
- bulletX = this.x + this.width / 2 - 5;
- bulletY = this.y + this.height;
- break;
- case 3: // 左
- bulletX = this.x - 10;
- bulletY = this.y + this.height / 2 - 5;
- break;
- }
- return new Bullet(bulletX, bulletY, this.direction, this);
- }
- return null;
- }
- // 更新坦克状态
- update() {
- if (this.cooldown > 0) {
- this.cooldown--;
- }
- }
- // 绘制坦克
- render(ctx) {
- if (!this.isAlive) return;
-
- ctx.save();
- ctx.translate(this.x + this.width / 2, this.y + this.height / 2);
- ctx.rotate(this.direction * Math.PI / 2);
-
- // 绘制坦克主体
- ctx.fillStyle = this.color;
- ctx.fillRect(-this.width / 2, -this.height / 2, this.width, this.height);
-
- // 绘制坦克炮管
- ctx.fillStyle = '#333';
- ctx.fillRect(-3, -this.height / 2 - 10, 6, 10);
-
- ctx.restore();
- }
- }
- // 玩家坦克类
- class PlayerTank extends Tank {
- constructor(x, y) {
- super(x, y, 3, 0, '#00F'); // 蓝色玩家坦克
- this.lives = 3; // 玩家生命值
- }
- // 处理键盘输入
- handleInput(keys) {
- if (keys['ArrowUp']) {
- this.setDirection(0);
- this.move();
- }
- if (keys['ArrowRight']) {
- this.setDirection(1);
- this.move();
- }
- if (keys['ArrowDown']) {
- this.setDirection(2);
- this.move();
- }
- if (keys['ArrowLeft']) {
- this.setDirection(3);
- this.move();
- }
- }
- }
- // 敌方坦克类
- class EnemyTank extends Tank {
- constructor(x, y) {
- super(x, y, 1, Math.floor(Math.random() * 4), '#F00'); // 红色敌方坦克
- this.aiTimer = 0;
- this.aiDirectionChangeInterval = 60; // AI改变方向的间隔
- }
- // 简单的AI行为
- updateAI() {
- this.aiTimer++;
-
- // 随机改变方向
- if (this.aiTimer >= this.aiDirectionChangeInterval) {
- this.setDirection(Math.floor(Math.random() * 4));
- this.aiTimer = 0;
- this.aiDirectionChangeInterval = 30 + Math.random() * 90;
- }
-
- // 随机射击
- if (Math.random() < 0.02) {
- return this.shoot();
- }
-
- return null;
- }
- // 更新敌方坦克
- update() {
- super.update();
- this.move();
- return this.updateAI();
- }
- }
复制代码
地图设计
地图是游戏场景的基础,包含各种障碍物和地形:
- // 地图类
- class Map {
- constructor(width, height, cellSize) {
- this.width = width;
- this.height = height;
- this.cellSize = cellSize;
- this.grid = [];
- this.walls = [];
-
- // 初始化地图网格
- this.initGrid();
- }
- // 初始化地图网格
- initGrid() {
- for (let y = 0; y < this.height; y++) {
- this.grid[y] = [];
- for (let x = 0; x < this.width; x++) {
- this.grid[y][x] = 0; // 0表示空地
- }
- }
- }
- // 添加墙壁
- addWall(x, y, width, height) {
- for (let row = y; row < y + height; row++) {
- for (let col = x; col < x + width; col++) {
- if (row >= 0 && row < this.height && col >= 0 && col < this.width) {
- this.grid[row][col] = 1; // 1表示墙壁
- this.walls.push({
- x: col * this.cellSize,
- y: row * this.cellSize,
- width: this.cellSize,
- height: this.cellSize
- });
- }
- }
- }
- }
- // 创建默认地图
- createDefaultMap() {
- // 添加边界墙
- for (let x = 0; x < this.width; x++) {
- this.addWall(x, 0, 1, 1); // 上边界
- this.addWall(x, this.height - 1, 1, 1); // 下边界
- }
- for (let y = 0; y < this.height; y++) {
- this.addWall(0, y, 1, 1); // 左边界
- this.addWall(this.width - 1, y, 1, 1); // 右边界
- }
-
- // 添加一些随机障碍物
- this.addWall(5, 5, 3, 2);
- this.addWall(10, 8, 2, 3);
- this.addWall(15, 5, 3, 2);
- this.addWall(20, 10, 2, 2);
- }
- // 检查位置是否可通行
- isPassable(x, y, width, height) {
- // 转换为网格坐标
- const leftCol = Math.floor(x / this.cellSize);
- const rightCol = Math.floor((x + width - 1) / this.cellSize);
- const topRow = Math.floor(y / this.cellSize);
- const bottomRow = Math.floor((y + height - 1) / this.cellSize);
-
- // 检查所有相关网格
- for (let row = topRow; row <= bottomRow; row++) {
- for (let col = leftCol; col <= rightCol; col++) {
- if (row < 0 || row >= this.height || col < 0 || col >= this.width || this.grid[row][col] !== 0) {
- return false;
- }
- }
- }
- return true;
- }
- // 渲染地图
- render(ctx) {
- ctx.fillStyle = '#888';
- for (const wall of this.walls) {
- ctx.fillRect(wall.x, wall.y, wall.width, wall.height);
- }
- }
- }
复制代码
碰撞检测
碰撞检测是游戏开发中的重要技术,用于判断游戏对象之间的交互:
- // 碰撞检测工具
- class CollisionDetector {
- // 矩形碰撞检测
- static rectCollision(rect1, rect2) {
- return rect1.x < rect2.x + rect2.width &&
- rect1.x + rect1.width > rect2.x &&
- rect1.y < rect2.y + rect2.height &&
- rect1.y + rect1.height > rect2.y;
- }
- // 圆形碰撞检测
- static circleCollision(circle1, circle2) {
- const dx = circle1.x - circle2.x;
- const dy = circle1.y - circle2.y;
- const distance = Math.sqrt(dx * dx + dy * dy);
- return distance < circle1.radius + circle2.radius;
- }
- // 坦克与墙壁碰撞检测
- static tankWallCollision(tank, map) {
- // 检查坦克当前位置是否与墙壁碰撞
- return !map.isPassable(tank.x, tank.y, tank.width, tank.height);
- }
- // 子弹与墙壁碰撞检测
- static bulletWallCollision(bullet, map) {
- // 获取子弹的边界框
- const bulletRect = {
- x: bullet.x,
- y: bullet.y,
- width: bullet.width,
- height: bullet.height
- };
-
- // 检查子弹是否与任何墙壁碰撞
- for (const wall of map.walls) {
- if (CollisionDetector.rectCollision(bulletRect, wall)) {
- return true;
- }
- }
- return false;
- }
- // 子弹与坦克碰撞检测
- static bulletTankCollision(bullet, tank) {
- if (!tank.isAlive || bullet.owner === tank) {
- return false;
- }
-
- const bulletRect = {
- x: bullet.x,
- y: bullet.y,
- width: bullet.width,
- height: bullet.height
- };
-
- const tankRect = {
- x: tank.x,
- y: tank.y,
- width: tank.width,
- height: tank.height
- };
-
- return CollisionDetector.rectCollision(bulletRect, tankRect);
- }
- }
复制代码
子弹系统
子弹是坦克攻击的主要方式,需要实现子弹的移动和碰撞处理:
- // 子弹类
- class Bullet {
- constructor(x, y, direction, owner) {
- this.x = x;
- this.y = y;
- this.width = 10;
- this.height = 10;
- this.speed = 5;
- this.direction = direction; // 0: 上, 1: 右, 2: 下, 3: 左
- this.owner = owner; // 发射子弹的坦克
- this.isAlive = true;
- }
- // 移动子弹
- move() {
- switch(this.direction) {
- case 0: // 上
- this.y -= this.speed;
- break;
- case 1: // 右
- this.x += this.speed;
- break;
- case 2: // 下
- this.y += this.speed;
- break;
- case 3: // 左
- this.x -= this.speed;
- break;
- }
-
- // 检查是否超出边界
- if (this.x < 0 || this.x > canvas.width || this.y < 0 || this.y > canvas.height) {
- this.isAlive = false;
- }
- }
- // 更新子弹状态
- update(map, tanks) {
- if (!this.isAlive) return;
-
- this.move();
-
- // 检查与墙壁的碰撞
- if (CollisionDetector.bulletWallCollision(this, map)) {
- this.isAlive = false;
- return;
- }
-
- // 检查与坦克的碰撞
- for (const tank of tanks) {
- if (CollisionDetector.bulletTankCollision(this, tank)) {
- this.isAlive = false;
- tank.isAlive = false;
-
- // 创建爆炸效果
- const explosion = new Explosion(tank.x + tank.width / 2, tank.y + tank.height / 2);
- game.addExplosion(explosion);
-
- // 播放爆炸音效
- if (game.resources.audios['explosion']) {
- game.resources.audios['explosion'].currentTime = 0;
- game.resources.audios['explosion'].play();
- }
-
- return;
- }
- }
- }
- // 绘制子弹
- render(ctx) {
- if (!this.isAlive) return;
-
- ctx.fillStyle = '#FF0';
- ctx.fillRect(this.x, this.y, this.width, this.height);
- }
- }
复制代码
敌方AI
敌方AI是游戏趣味性的关键,需要实现基本的移动和攻击逻辑:
- // 敌方AI管理器
- class EnemyAI {
- constructor() {
- this.difficulty = 1; // AI难度级别
- }
- // 更新所有敌方坦克的AI
- update(enemyTanks, playerTank, map) {
- const bullets = [];
-
- for (const tank of enemyTanks) {
- if (!tank.isAlive) continue;
-
- // 根据难度调整AI行为
- switch(this.difficulty) {
- case 1: // 简单难度
- bullets.push(this.simpleAI(tank, map));
- break;
- case 2: // 中等难度
- bullets.push(this.mediumAI(tank, playerTank, map));
- break;
- case 3: // 困难难度
- bullets.push(this.hardAI(tank, playerTank, map));
- break;
- }
- }
-
- return bullets.filter(bullet => bullet !== null);
- }
- // 简单AI - 随机移动和射击
- simpleAI(tank, map) {
- // 随机改变方向
- if (Math.random() < 0.02) {
- const oldDirection = tank.direction;
- let newDirection;
- do {
- newDirection = Math.floor(Math.random() * 4);
- } while (newDirection === oldDirection);
-
- tank.setDirection(newDirection);
- }
-
- // 尝试移动
- const oldX = tank.x;
- const oldY = tank.y;
- tank.move();
-
- // 如果移动后与墙壁碰撞,则恢复位置并改变方向
- if (CollisionDetector.tankWallCollision(tank, map)) {
- tank.x = oldX;
- tank.y = oldY;
- tank.setDirection(Math.floor(Math.random() * 4));
- }
-
- // 随机射击
- if (Math.random() < 0.01) {
- return tank.shoot();
- }
-
- return null;
- }
- // 中等AI - 尝试朝向玩家
- mediumAI(tank, playerTank, map) {
- // 计算与玩家的距离
- const dx = playerTank.x - tank.x;
- const dy = playerTank.y - tank.y;
- const distance = Math.sqrt(dx * dx + dy * dy);
-
- // 如果距离较近,有一定概率朝向玩家
- if (distance < 300 && Math.random() < 0.05) {
- if (Math.abs(dx) > Math.abs(dy)) {
- tank.setDirection(dx > 0 ? 1 : 3); // 右或左
- } else {
- tank.setDirection(dy > 0 ? 2 : 0); // 下或上
- }
- } else {
- // 否则随机移动
- if (Math.random() < 0.02) {
- tank.setDirection(Math.floor(Math.random() * 4));
- }
- }
-
- // 尝试移动
- const oldX = tank.x;
- const oldY = tank.y;
- tank.move();
-
- // 如果移动后与墙壁碰撞,则恢复位置并改变方向
- if (CollisionDetector.tankWallCollision(tank, map)) {
- tank.x = oldX;
- tank.y = oldY;
- tank.setDirection(Math.floor(Math.random() * 4));
- }
-
- // 如果朝向玩家且距离适中,增加射击概率
- let shootChance = 0.01;
- if (distance < 400) {
- shootChance = 0.03;
- }
-
- if (Math.random() < shootChance) {
- return tank.shoot();
- }
-
- return null;
- }
- // 困难AI - 更智能的追踪和射击
- hardAI(tank, playerTank, map) {
- // 计算与玩家的距离和方向
- const dx = playerTank.x - tank.x;
- const dy = playerTank.y - tank.y;
- const distance = Math.sqrt(dx * dx + dy * dy);
-
- // 确定最佳方向
- let bestDirection = tank.direction;
- if (Math.random() < 0.1) { // 10%概率重新计算最佳方向
- if (Math.abs(dx) > Math.abs(dy)) {
- bestDirection = dx > 0 ? 1 : 3; // 右或左
- } else {
- bestDirection = dy > 0 ? 2 : 0; // 下或上
- }
- tank.setDirection(bestDirection);
- }
-
- // 尝试移动
- const oldX = tank.x;
- const oldY = tank.y;
- tank.move();
-
- // 如果移动后与墙壁碰撞,则恢复位置并尝试其他方向
- if (CollisionDetector.tankWallCollision(tank, map)) {
- tank.x = oldX;
- tank.y = oldY;
-
- // 尝试其他方向
- for (let dir = 0; dir < 4; dir++) {
- if (dir !== tank.direction) {
- tank.setDirection(dir);
- tank.move();
- if (!CollisionDetector.tankWallCollision(tank, map)) {
- break;
- }
- tank.x = oldX;
- tank.y = oldY;
- }
- }
- }
-
- // 智能射击
- let shootChance = 0.02;
- if (distance < 500) {
- // 如果朝向玩家,增加射击概率
- const isFacingPlayer =
- (tank.direction === 0 && dy < 0) || // 上
- (tank.direction === 1 && dx > 0) || // 右
- (tank.direction === 2 && dy > 0) || // 下
- (tank.direction === 3 && dx < 0); // 左
-
- if (isFacingPlayer) {
- shootChance = 0.05;
- }
- }
-
- if (Math.random() < shootChance) {
- return tank.shoot();
- }
-
- return null;
- }
- // 设置AI难度
- setDifficulty(level) {
- this.difficulty = Math.max(1, Math.min(3, level));
- }
- }
复制代码
爆炸效果
爆炸效果增强了游戏的视觉体验,使游戏更加生动:
- // 爆炸效果类
- class Explosion {
- constructor(x, y) {
- this.x = x;
- this.y = y;
- this.radius = 5;
- this.maxRadius = 30;
- this.growthRate = 2;
- this.isAlive = true;
- this.opacity = 1;
- }
- // 更新爆炸效果
- update() {
- this.radius += this.growthRate;
- this.opacity = 1 - (this.radius / this.maxRadius);
-
- if (this.radius >= this.maxRadius) {
- this.isAlive = false;
- }
- }
- // 绘制爆炸效果
- render(ctx) {
- if (!this.isAlive) return;
-
- ctx.save();
- ctx.globalAlpha = this.opacity;
-
- // 绘制外圈
- ctx.beginPath();
- ctx.arc(this.x, this.y, this.radius, 0, Math.PI * 2);
- ctx.fillStyle = '#F90';
- ctx.fill();
-
- // 绘制内圈
- ctx.beginPath();
- ctx.arc(this.x, this.y, this.radius * 0.6, 0, Math.PI * 2);
- ctx.fillStyle = '#FF0';
- ctx.fill();
-
- // 绘制中心
- ctx.beginPath();
- ctx.arc(this.x, this.y, this.radius * 0.3, 0, Math.PI * 2);
- ctx.fillStyle = '#FFF';
- ctx.fill();
-
- ctx.restore();
- }
- }
复制代码
游戏主控制器
游戏主控制器负责整合所有游戏组件,管理游戏状态和流程:
- // 游戏主控制器
- class Game {
- constructor() {
- this.canvas = document.getElementById('gameCanvas');
- this.ctx = this.canvas.getContext('2d');
- this.renderer = new Renderer(this.canvas, this.ctx);
-
- // 游戏状态
- this.gameState = 'loading'; // loading, menu, playing, paused, gameOver
- this.score = 0;
- this.level = 1;
- this.lives = 3;
-
- // 游戏对象
- this.playerTank = null;
- this.enemyTanks = [];
- this.bullets = [];
- this.explosions = [];
- this.map = null;
-
- // 游戏系统
- this.enemyAI = new EnemyAI();
- this.keys = {}; // 键盘输入状态
- this.resourceLoader = new ResourceLoader();
-
- // 初始化游戏
- this.init();
- }
- // 初始化游戏
- init() {
- // 设置资源加载完成回调
- this.resourceLoader.onLoadComplete = () => {
- this.gameState = 'menu';
- this.setupEventListeners();
- this.gameLoop();
- };
-
- // 加载游戏资源
- this.loadResources();
- }
- // 加载游戏资源
- loadResources() {
- // 加载图片资源
- this.resourceLoader.loadImage('playerTank', 'assets/images/player-tank.png');
- this.resourceLoader.loadImage('enemyTank', 'assets/images/enemy-tank.png');
- this.resourceLoader.loadImage('bullet', 'assets/images/bullet.png');
- this.resourceLoader.loadImage('wall', 'assets/images/wall.png');
-
- // 加载音频资源
- this.resourceLoader.loadAudio('shoot', 'assets/audio/shoot.wav');
- this.resourceLoader.loadAudio('explosion', 'assets/audio/explosion.wav');
- this.resourceLoader.loadAudio('background', 'assets/audio/background.mp3');
- }
- // 设置事件监听器
- setupEventListeners() {
- // 键盘事件
- window.addEventListener('keydown', (e) => {
- this.keys[e.key] = true;
-
- // 空格键射击
- if (e.key === ' ' && this.gameState === 'playing' && this.playerTank && this.playerTank.isAlive) {
- const bullet = this.playerTank.shoot();
- if (bullet) {
- this.bullets.push(bullet);
-
- // 播放射击音效
- if (this.resourceLoader.audios['shoot']) {
- this.resourceLoader.audios['shoot'].currentTime = 0;
- this.resourceLoader.audios['shoot'].play();
- }
- }
- }
-
- // P键暂停/继续
- if (e.key === 'p' || e.key === 'P') {
- if (this.gameState === 'playing') {
- this.gameState = 'paused';
- } else if (this.gameState === 'paused') {
- this.gameState = 'playing';
- }
- }
-
- // Enter键开始游戏
- if (e.key === 'Enter') {
- if (this.gameState === 'menu' || this.gameState === 'gameOver') {
- this.startGame();
- }
- }
- });
-
- window.addEventListener('keyup', (e) => {
- this.keys[e.key] = false;
- });
- }
- // 开始游戏
- startGame() {
- this.gameState = 'playing';
- this.score = 0;
- this.level = 1;
- this.lives = 3;
-
- // 创建地图
- this.map = new Map(25, 19, 40);
- this.map.createDefaultMap();
-
- // 创建玩家坦克
- this.playerTank = new PlayerTank(400, 500);
-
- // 创建敌方坦克
- this.enemyTanks = [];
- this.spawnEnemyTanks(5);
-
- // 清空子弹和爆炸效果
- this.bullets = [];
- this.explosions = [];
-
- // 设置AI难度
- this.enemyAI.setDifficulty(1);
-
- // 播放背景音乐
- if (this.resourceLoader.audios['background']) {
- this.resourceLoader.audios['background'].loop = true;
- this.resourceLoader.audios['background'].play();
- }
- }
- // 生成敌方坦克
- spawnEnemyTanks(count) {
- for (let i = 0; i < count; i++) {
- let x, y;
- let validPosition = false;
- let attempts = 0;
-
- // 尝试找到有效的位置
- while (!validPosition && attempts < 50) {
- x = Math.floor(Math.random() * 20) * 40 + 40;
- y = Math.floor(Math.random() * 10) * 40 + 40;
-
- // 检查位置是否有效(不与墙壁重叠)
- if (this.map.isPassable(x, y, 40, 40)) {
- validPosition = true;
- }
- attempts++;
- }
-
- if (validPosition) {
- this.enemyTanks.push(new EnemyTank(x, y));
- }
- }
- }
- // 添加爆炸效果
- addExplosion(explosion) {
- this.explosions.push(explosion);
- }
- // 更新游戏状态
- update() {
- if (this.gameState !== 'playing') return;
-
- // 更新玩家坦克
- if (this.playerTank && this.playerTank.isAlive) {
- this.playerTank.handleInput(this.keys);
- this.playerTank.update();
-
- // 检查玩家坦克与墙壁的碰撞
- if (CollisionDetector.tankWallCollision(this.playerTank, this.map)) {
- // 简单处理:回退位置
- switch(this.playerTank.direction) {
- case 0: // 上
- this.playerTank.y += this.playerTank.speed;
- break;
- case 1: // 右
- this.playerTank.x -= this.playerTank.speed;
- break;
- case 2: // 下
- this.playerTank.y -= this.playerTank.speed;
- break;
- case 3: // 左
- this.playerTank.x += this.playerTank.speed;
- break;
- }
- }
- }
-
- // 更新敌方坦克和AI
- const aiBullets = this.enemyAI.update(this.enemyTanks, this.playerTank, this.map);
- this.bullets.push(...aiBullets);
-
- // 更新子弹
- for (let i = this.bullets.length - 1; i >= 0; i--) {
- const bullet = this.bullets[i];
- bullet.update(this.map, [this.playerTank, ...this.enemyTanks]);
-
- if (!bullet.isAlive) {
- this.bullets.splice(i, 1);
- }
- }
-
- // 更新爆炸效果
- for (let i = this.explosions.length - 1; i >= 0; i--) {
- const explosion = this.explosions[i];
- explosion.update();
-
- if (!explosion.isAlive) {
- this.explosions.splice(i, 1);
- }
- }
-
- // 检查游戏状态
- this.checkGameState();
- }
- // 检查游戏状态
- checkGameState() {
- // 检查玩家是否死亡
- if (this.playerTank && !this.playerTank.isAlive) {
- this.lives--;
- if (this.lives <= 0) {
- this.gameState = 'gameOver';
- } else {
- // 重生玩家坦克
- this.playerTank = new PlayerTank(400, 500);
- }
- }
-
- // 检查是否所有敌人都被消灭
- const aliveEnemies = this.enemyTanks.filter(tank => tank.isAlive);
- if (aliveEnemies.length === 0) {
- // 进入下一关
- this.level++;
- this.enemyAI.setDifficulty(Math.min(3, Math.floor(this.level / 3) + 1));
- this.spawnEnemyTanks(5 + this.level * 2);
-
- // 增加玩家生命值
- if (this.level % 2 === 0) {
- this.lives++;
- }
- }
-
- // 更新分数
- this.score = (this.level - 1) * 1000 + (5 - aliveEnemies.length) * 100;
- }
- // 渲染游戏
- render() {
- // 清空画布
- this.renderer.clear();
-
- // 根据游戏状态渲染不同内容
- switch(this.gameState) {
- case 'loading':
- this.renderLoading();
- break;
- case 'menu':
- this.renderMenu();
- break;
- case 'playing':
- this.renderGame();
- break;
- case 'paused':
- this.renderGame();
- this.renderPaused();
- break;
- case 'gameOver':
- this.renderGameOver();
- break;
- }
- }
- // 渲染加载画面
- renderLoading() {
- this.renderer.drawText('Loading...', canvas.width / 2 - 50, canvas.height / 2, '#FFF', '24px Arial');
- }
- // 渲染菜单
- renderMenu() {
- this.renderer.drawText('TANK BATTLE', canvas.width / 2 - 100, canvas.height / 2 - 50, '#FFF', '36px Arial');
- this.renderer.drawText('Press ENTER to Start', canvas.width / 2 - 120, canvas.height / 2 + 20, '#FFF', '20px Arial');
- this.renderer.drawText('Arrow Keys to Move, SPACE to Shoot', canvas.width / 2 - 180, canvas.height / 2 + 60, '#FFF', '16px Arial');
- this.renderer.drawText('P to Pause', canvas.width / 2 - 50, canvas.height / 2 + 90, '#FFF', '16px Arial');
- }
- // 渲染游戏画面
- renderGame() {
- // 渲染地图
- if (this.map) {
- this.map.render(this.ctx);
- }
-
- // 渲染玩家坦克
- if (this.playerTank) {
- this.playerTank.render(this.ctx);
- }
-
- // 渲染敌方坦克
- for (const tank of this.enemyTanks) {
- tank.render(this.ctx);
- }
-
- // 渲染子弹
- for (const bullet of this.bullets) {
- bullet.render(this.ctx);
- }
-
- // 渲染爆炸效果
- for (const explosion of this.explosions) {
- explosion.render(this.ctx);
- }
-
- // 渲染UI
- this.renderUI();
- }
- // 渲染UI
- renderUI() {
- // 分数
- this.renderer.drawText(`Score: ${this.score}`, 10, 30, '#FFF', '20px Arial');
-
- // 关卡
- this.renderer.drawText(`Level: ${this.level}`, 10, 60, '#FFF', '20px Arial');
-
- // 生命值
- this.renderer.drawText(`Lives: ${this.lives}`, 10, 90, '#FFF', '20px Arial');
- }
- // 渲染暂停画面
- renderPaused() {
- this.renderer.drawText('PAUSED', canvas.width / 2 - 60, canvas.height / 2, '#FFF', '36px Arial');
- this.renderer.drawText('Press P to Resume', canvas.width / 2 - 100, canvas.height / 2 + 40, '#FFF', '20px Arial');
- }
- // 渲染游戏结束画面
- renderGameOver() {
- this.renderer.drawText('GAME OVER', canvas.width / 2 - 100, canvas.height / 2 - 50, '#F00', '36px Arial');
- this.renderer.drawText(`Final Score: ${this.score}`, canvas.width / 2 - 90, canvas.height / 2, '#FFF', '24px Arial');
- this.renderer.drawText('Press ENTER to Restart', canvas.width / 2 - 120, canvas.height / 2 + 50, '#FFF', '20px Arial');
- }
- // 游戏主循环
- gameLoop() {
- this.update();
- this.render();
- requestAnimationFrame(() => this.gameLoop());
- }
- }
- // 启动游戏
- const game = new Game();
复制代码
游戏优化与高级技巧
性能优化
游戏性能优化对于流畅的游戏体验至关重要:
- // 对象池 - 减少垃圾回收
- class ObjectPool {
- constructor(createFn, resetFn, initialSize = 10) {
- this.createFn = createFn;
- this.resetFn = resetFn;
- this.pool = [];
- this.activeObjects = new Set();
-
- // 预创建对象
- for (let i = 0; i < initialSize; i++) {
- this.pool.push(this.createFn());
- }
- }
- // 获取对象
- get() {
- let obj;
- if (this.pool.length > 0) {
- obj = this.pool.pop();
- } else {
- obj = this.createFn();
- }
- this.activeObjects.add(obj);
- return obj;
- }
- // 释放对象
- release(obj) {
- if (this.activeObjects.has(obj)) {
- this.activeObjects.delete(obj);
- this.resetFn(obj);
- this.pool.push(obj);
- }
- }
- // 释放所有活动对象
- releaseAll() {
- for (const obj of this.activeObjects) {
- this.resetFn(obj);
- this.pool.push(obj);
- }
- this.activeObjects.clear();
- }
- }
- // 使用对象池管理子弹
- const bulletPool = new ObjectPool(
- // 创建函数
- () => new Bullet(0, 0, 0, null),
- // 重置函数
- (bullet) => {
- bullet.x = 0;
- bullet.y = 0;
- bullet.direction = 0;
- bullet.owner = null;
- bullet.isAlive = true;
- },
- // 初始大小
- 20
- );
- // 修改Game类中的子弹创建和释放逻辑
- class Game {
- // ... 其他代码 ...
-
- // 创建子弹
- createBullet(x, y, direction, owner) {
- const bullet = bulletPool.get();
- bullet.x = x;
- bullet.y = y;
- bullet.direction = direction;
- bullet.owner = owner;
- bullet.isAlive = true;
- return bullet;
- }
-
- // 释放子弹
- releaseBullet(bullet) {
- bulletPool.release(bullet);
- }
-
- // ... 其他代码 ...
- }
复制代码
视觉效果增强
增强游戏的视觉效果可以使游戏更加吸引人:
- // 粒子系统 - 用于创建更丰富的视觉效果
- class Particle {
- constructor(x, y, vx, vy, color, size, life) {
- this.x = x;
- this.y = y;
- this.vx = vx;
- this.vy = vy;
- this.color = color;
- this.size = size;
- this.life = life;
- this.maxLife = life;
- }
- update() {
- this.x += this.vx;
- this.y += this.vy;
- this.life--;
-
- // 添加重力效果
- this.vy += 0.1;
- }
- render(ctx) {
- const alpha = this.life / this.maxLife;
- ctx.save();
- ctx.globalAlpha = alpha;
- ctx.fillStyle = this.color;
- ctx.beginPath();
- ctx.arc(this.x, this.y, this.size, 0, Math.PI * 2);
- ctx.fill();
- ctx.restore();
- }
- }
- // 粒子发射器
- class ParticleEmitter {
- constructor(x, y) {
- this.x = x;
- this.y = y;
- this.particles = [];
- }
- // 发射爆炸粒子
- emitExplosion(count = 30) {
- for (let i = 0; i < count; i++) {
- const angle = Math.random() * Math.PI * 2;
- const speed = 1 + Math.random() * 3;
- const vx = Math.cos(angle) * speed;
- const vy = Math.sin(angle) * speed;
- const color = `hsl(${Math.random() * 60}, 100%, 50%)`; // 黄色到红色
- const size = 2 + Math.random() * 3;
- const life = 20 + Math.random() * 30;
-
- this.particles.push(new Particle(this.x, this.y, vx, vy, color, size, life));
- }
- }
- // 发射烟雾粒子
- emitSmoke(count = 10) {
- for (let i = 0; i < count; i++) {
- const angle = Math.random() * Math.PI * 2;
- const speed = 0.5 + Math.random() * 1;
- const vx = Math.cos(angle) * speed;
- const vy = Math.sin(angle) * speed - 1; // 向上飘
- const color = `rgba(100, 100, 100, ${0.3 + Math.random() * 0.3})`;
- const size = 5 + Math.random() * 10;
- const life = 30 + Math.random() * 50;
-
- this.particles.push(new Particle(this.x, this.y, vx, vy, color, size, life));
- }
- }
- update() {
- for (let i = this.particles.length - 1; i >= 0; i--) {
- const particle = this.particles[i];
- particle.update();
-
- if (particle.life <= 0) {
- this.particles.splice(i, 1);
- }
- }
- }
- render(ctx) {
- for (const particle of this.particles) {
- particle.render(ctx);
- }
- }
- }
- // 修改Explosion类,使用粒子系统
- class Explosion {
- constructor(x, y) {
- this.x = x;
- this.y = y;
- this.emitter = new ParticleEmitter(x, y);
- this.emitter.emitExplosion();
- this.isAlive = true;
- this.timer = 60; // 爆炸持续时间
- }
- update() {
- this.emitter.update();
- this.timer--;
-
- if (this.timer <= 0) {
- this.isAlive = false;
- }
- }
- render(ctx) {
- this.emitter.render(ctx);
- }
- }
复制代码
音效增强
音效是游戏体验的重要组成部分,适当的音效可以大大增强游戏的沉浸感:
存档系统
存档系统允许玩家保存和加载游戏进度,增强游戏的可玩性:
总结与扩展
通过本教程,我们全面学习了如何使用HTML5技术开发一个坦克大战游戏。我们从基础概念开始,逐步实现了游戏的核心功能,包括坦克控制、地图设计、碰撞检测、敌方AI等。同时,我们还探讨了一些高级技巧,如性能优化、视觉效果增强、音效管理和存档系统。
扩展建议
如果你想要进一步完善这个坦克大战游戏,可以考虑以下扩展方向:
1. 多人对战:添加网络功能,实现玩家之间的对战。
2. 更多关卡:设计更多不同风格的关卡,增加游戏的可玩性。
3. 道具系统:添加各种道具,如加速、护盾、增强火力等。
4. 更多敌人类型:设计不同类型的敌方坦克,具有不同的行为模式和特点。
5. 成就系统:添加成就系统,鼓励玩家探索游戏的各个方面。
6. 移动端适配:优化游戏,使其能够在移动设备上流畅运行。
学习资源推荐
想要深入学习HTML5游戏开发,以下资源可能会有所帮助:
1. MDN Web Docs:提供详细的HTML5、Canvas和Web API文档。
2. 《HTML5 Game Development Insights》:一本深入探讨HTML5游戏开发的书籍。
3. Phaser.js:一个流行的HTML5游戏框架,可以简化游戏开发过程。
4. Stack Overflow:解决开发中遇到的具体问题。
5. GitHub:查找开源的HTML5游戏项目,学习他人的代码和实现方式。
通过不断学习和实践,你将能够掌握HTML5游戏开发的核心技术,并创造出属于自己的精彩游戏。祝你在游戏开发的道路上取得成功! |
|