基于JS与Canvas的飞机大战游戏实现详解
本文详细解析如何使用JavaScript结合Canvas API开发一款经典的飞机大战游戏。内容涵盖Canvas画布初始化、游戏状态机设计(包括初始化、入场、战斗、暂停及结束状态)、背景与角色对象构建,以及碰撞检测逻辑和定时器刷新机制。通过分步代码示例,帮助开发者掌握2D网页游戏的核心实现思路。
本文分享了一个使用JavaScript和Canvas技术实现飞机大战游戏的完整代码示例,供读者参考学习。
Canvas画布初始化与状态定义
首先,需要创建一个Canvas绘图区域,并将其宽高设置为480px和852px,并通过CSS样式使其在页面中水平居中显示。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
canvas {
position: absolute;
left: 0;
right: 0;
margin: auto;
border: #000 solid 1px;
}
</style>
</head>
<body>
<canvas width="480" height="852"></canvas>
</body>
</html>接着,通过JavaScript获取对应的Canvas元素,并获取其2D绘图上下文对象cex。同时,定义一个全局变量state用于表示当前的游戏状态。
该state变量用于管理游戏流程,具体定义如下:state=0表示游戏初始化阶段;state=1表示我方飞机入场动画阶段;state=2表示正式战斗阶段;state=3表示游戏暂停阶段;state=4表示游戏结束阶段。
var canvas = document.getElementsByTagName("canvas")[0];
var cex = canvas.getContext('2d');
var state = 0; //状态游戏资源与对象构建
随后,加载背景图片,根据图片实际尺寸确定背景的宽高等属性数据,并编写相应的构造函数或方法,最终实例化出一个背景图片对象。
//背景图片
var bg = new Image()
bg.src = 'img/background.png'
// 背景数据对象
var bakgobj = {
img: bg,
width: 480,
height: 852,
}
// 背景函数
function By(params) {
this.width = params.width;
this.height = params.height;
this.img = params.img;
this.x = 0;
this.y = 0;
this.y1 = -this.height;
// 背景绘制
this.paint = function () {
cex.drawImage(this.img, this.x, this.y);
cex.drawImage(this.img, this.x, this.y1)
}
// 背景运动
this.sprot = function () {
this.y += 3;
this.y1 += 3;
if (this.y >= this.height) {
this.y = -this.height;
}
if (this.y1 >= this.height) {
this.y1 = -this.height;
}
}
}
// 背景对象
var bakg = new By(bakgobj);按照类似思路,分别声明并加载Logo图片和暂停状态所需的图片资源。
// logo图片
var logo = new Image();
logo.src = 'img/start.png'
// 暂停图片
var pause = new Image();
pause.src = 'img/game_pause_nor.png';使用相同的对象声明思路,构建入场阶段所需的飞机对象,处理入场动画逻辑。
// 入场阶段
var gamearr = ['img/game_loading1.png', 'img/game_loading2.png', 'img/game_loading3.png',
'img/game_loading4.png'
];
// 入场图片对象
var gameArr = [];
for (var i = 0; i < gamearr.length; i++) {
gameArr[i] = new Image();
gameArr[i].src = gamearr[i];
}
// 入场飞机数据
var gameobj = {
img: gameArr,
width: 186,
height: 38,
length: gameArr.length
}
// 入场飞机函数
function Game(params) {
this.imgs = params.img;
this.width = params.width;
this.height = params.height;
this.length = params.length;
this.index = 0; //入场顺序图片下标
this.thim = 0;
this.paint = function () {
cex.drawImage(this.imgs[this.index], 0, bakg.height - this.height);
}
this.sprot = function () {
this.thim++;
if (this.thim % 3 == 0) {
this.index++;
}
if (this.index == this.length) {
state = 2;
}
}
}
// 入场飞机对象
var game = new Game(gameobj);接着声明我方飞机的对象,包含其图片资源、尺寸及运动逻辑等数据。
// 飞机图片
var heroarr = ['img/hero1.png', 'img/hero2.png']
// 飞机图片对象
var heroArr = [];
for (var i = 0; i < heroarr.length; i++) {
heroArr[i] = new Image();
heroArr[i].src = heroarr[i];
}
// 飞机数据
var heroobj = {
img: heroArr,
width: 99,
height: 124,
length: heroArr.length,
full:4, //生命
invinc_:50, //无敌时间
maga:500, //子弹数量
}
// 飞机函数
function Hero(params) {
this.imgs = params.img;
this.width = params.width;
this.height = params.height;
this.length = params.length;
this.x = (bakgobj.width - this.width) / 2;
this.y = bakgobj.height - this.height;
this.index = 0;
this.maga=params.maga;
this.full=params.full; //飞机生命
this.invinc=0; //初始无敌时间
this.invinc_=params.invinc_;
this.frac=0; //飞机分数;
this.cou = 0; //控制子弹发射速度
this.ene = 0; //控制敌机出现频率
this.paint = function () {
if((this.invinc>0 && this.invinc%2==0)||this.invinc<=0){
cex.drawImage(this.imgs[this.index], this.x, this.y)
}
}
this.sprot = function () {
this.index++;
if (this.index == 2) {
this.index = 0;
}
}
// 增加射出子弹
this.bullk = function () {
this.cou++;
// 子弹发射速度
// if (this.cou % 5 == 0) {
bullsec.push(new Bull(bullobj))
// }
}
// 增加敌机
this.enemysos = function () {
if (this.ene % 20 == 0) {
var rand = Math.random();
if (rand < 0.5) {
enemy.push(new Enemy(enemy1obj))
} else if (rand < 0.8) {
enemy.push(new Enemy(enemy2obj))
} else {
enemy.push(new Enemy3(enemy3obj))
}
}
this.ene++;
}
}
var hero = new Hero(heroobj);定义子弹对象及其数组,用于管理游戏中发射的子弹实例。
// 子弹图像
var bullimg = new Image();
bullimg.src = 'img/bullet1.png';
// 子弹数据
var bullobj = {
img: bullimg,
width: 9,
height: 21,
}
// 子弹函数
function Bull(params) {
this.img = params.img;
this.width = params.width;
this.height = params.height;
this.x = hero.x + hero.width / 2 - this.width / 2;
this.y = hero.y - this.height;
this.paint = function () {
cex.drawImage(this.img, this.x, this.y)
}
this.sprot = function () {
this.y -= 20; //子弹飞行速度
}
}
var bull = new Bull(bullobj);
// 界面上的子弹数组;
var bullsec = [];
function bull_paint() {
for (var i = 0; i < bullsec.length; i++) {
bullsec[i].paint();
}
}
function bull_sprot() {
for (var i = 0; i < bullsec.length; i++) {
bullsec[i].sprot();
}
}声明敌机对象,分为小、中、大三种类型,并建立相应的数组和管理方法,用于生成和控制敌方单位。
// 敌机--小
var enemy1arr = ['img/enemy1.png', 'img/enemy1_down1.png', 'img/enemy1_down2.png', 'img/enemy1_down3.png',
'img/enemy1_down4.png'
]
var enemy1Arr = [];
for (var i = 0; i < enemy1arr.length; i++) {
enemy1Arr[i] = new Image();
enemy1Arr[i].src = enemy1arr[i];
}
//敌机—-小 数据
var enemy1obj = {
img: enemy1Arr,
width: 57,
height: 51,
length: enemy1Arr.length,
frac:3,
full:1,
}
// 敌机--中
var enemy2arr = ['img/enemy2.png', 'img/enemy2_down1.png', 'img/enemy2_down2.png', 'img/enemy2_down3.png',
'img/enemy2_down4.png'
]
var enemy2Arr = [];
for (var i = 0; i < enemy2arr.length; i++) {
enemy2Arr[i] = new Image();
enemy2Arr[i].src = enemy2arr[i];
}
//敌机--中 数据
var enemy2obj = {
img: enemy2Arr,
width: 69,
height: 95,
length: enemy2Arr.length,
frac:5,
full:2,
}
// 敌机--小、中 函数
function Enemy(params) {
this.imgs = params.img;
this.width = params.width;
this.height = params.height;
this.length = params.length;
this.frac=params.frac;
this.index = 0;
this.buff=Math.random<0.05?true:false; //随机带buff
this.ext=false;//敌机是否被击落
this.full = params.full; //敌机生命值
this.x = Math.random() * (bakg.width - this.width);
this.y = -this.height;
this.paint = function () {
cex.drawImage(this.imgs[this.index], this.x, this.y);
}
this.sprot = function () {
this.y += 5;
if (this.full <= 0) {
this.index++;
}
}
}
// 敌机--大
var enemy3arr = ['img/enemy3_n1.png', 'img/enemy3_n2.png', 'img/enemy3_hit.png', 'img/enemy3_down1.png',
'img/enemy3_down2.png', 'img/enemy3_down3.png', 'img/enemy3_down4.png', 'img/enemy3_down5.png',
'img/enemy3_down6.png'
]
var enemy3Arr = [];
for (var i = 0; i < enemy3arr.length; i++) {
enemy3Arr[i] = new Image();
enemy3Arr[i].src = enemy3arr[i];
}
//敌机--大 数据
var enemy3obj = {
img: enemy3Arr,
width: 169,
height: 258,
length: enemy3Arr.length,
frac:10,
full:4,
}
// 敌机--大 函数
function Enemy3(params) {
this.imgs = params.img;
this.width = params.width;
this.height = params.height;
this.length = params.length;
this.frac=params.frac;
this.index = 0;
this.thim = 0;
this.buff=Math.random<0.2?true:false; //随机带buff
this.ext=false;//敌机是否被击落
this.full = params.full;
this.full_=Math.floor(this.full/2);//战损
this.x = Math.random() * (bakg.width - this.width);
this.y = -this.height;
this.paint = function () {
cex.drawImage(this.imgs[this.index], this.x, this.y);
}
this.sprot = function () {
this.y += 5;
if (this.full <= 0) {
this.index++;
}else if(this.full>0&&this.full<=this.full_){
this.index=2;
}else if (this.thim % 5 == 0) {
this.index++;
if (this.index == 2) {
this.index = 0;
}
}
this.thim++;
}
}
//敌机数组
var enemy = [];
// 敌机绘制
function enemy_paint() {
for (var i = 0; i < enemy.length; i++) {
enemy[i].paint();
}
}
// 敌机移动
function enemy_sprot() {
for (var i = 0; i < enemy.length; i++) {
enemy[i].sprot();
}
}
// 敌机爆炸后删除
function enemy_del(){
for(var i=0;i<enemy.length;i++){
if(enemy[i].index==enemy[i].length){
hero.frac+=enemy[i].frac;
enemy.splice(i,1);
i--;
}
}
}核心逻辑与交互实现
创建一个专门的函数用于判断游戏内各元素之间的碰撞情况,这是游戏逻辑的核心部分。
// 检测敌机是否产生碰撞
function enemy_bull_hero() {
hero.invinc--;
for (var i = 0; i < enemy.length; i++) {
if (hero.invinc<=0&&hero.y <= enemy[i].y + enemy[i].height&&hero.y>enemy[i].y-hero.height) {
if (hero.x > enemy[i].x - hero.width && hero.x < enemy[i].x + enemy[i].width) {
// 飞机与敌机碰撞;
hero.full--;
hero.invinc=hero.invinc_;
if(hero.full==0){
state = 4;
}
}
}
for (var n = 0; n < bullsec.length; n++) {
if (!enemy[i].ext&&bullsec[n].y <= enemy[i].y + enemy[i].height&&bullsec[n].y>enemy[i].y-bullsec[n].height) {
if (bullsec[n].x > enemy[i].x - bullsec[n].width && bullsec[n].x < enemy[i].x + enemy[i]
.width) {
// 敌机与子弹碰撞;
bullsec.splice(n, 1);
n--;
enemy[i].full--;
if(enemy[i].full<=0){
enemy[i].ext=true;
}
}
}
}
}
}分别为Canvas元素绑定相应的事件监听器,如鼠标或触摸事件,以响应用户操作。
//点击画布从状态0切换到状态1;
canvas.onclick = function () {
if (state == 0) {
state = 1;
}
}
// 飞机跟随鼠标移动
canvas.onmousemove = function (e) {
if (state == 3) {
state = 2;
}
if (state == 2) {
var x = e.offsetX;
var y = e.offsetY;
hero.x = x - hero.width / 2;
hero.y = y - hero.height / 2;
}
}
// 鼠标移出暂停
canvas.onmouseout = function () {
if (state == 2) {
state = 3;
}
}
// 弹夹子弹发射
document.onkeydown =function(event){
if(state==2){
if(event.keyCode==32&&hero.maga>0){
hero.bullk() //增加界面射出子弹
hero.maga--;
}
}
};最后,在定时器中持续调用绘图函数,实时更新画面内容,确保游戏流畅运行。
setInterval(function () {
bakg.paint()
bakg.sprot()
cex.font='40px 微软雅黑';
cex.fillText('生命:'+hero.full,330,40);
cex.fillText('分数:'+hero.frac,0,40);
cex.fillText('子弹:'+hero.maga,0,80);
if (state == 0) { //初始化
cex.drawImage(logo, 40, 0);
}
if (state == 1) { //出场动画
game.paint();
game.sprot();
}
if (state == 2) { //战斗状态
hero.paint()
hero.sprot()
bull_paint()
bull_sprot()
hero.enemysos() //增加敌机数量
enemy_paint()
enemy_sprot()
enemy_bull_hero() //碰撞检测
enemy_del();
}
if (state == 3) { //暂停状态
cex.drawImage(pause, 210, 375)
hero.paint()
bull_paint()
enemy_paint()
}
if (state == 4) { //游戏结束状态
cex.fillText('菜',bakg.width/2-30,bakg.height/2-90);
}
}, 30)以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持本站。
JavaScript零基础入门:从环境搭建到运行第一段代码
2026-11-05 15:29:58
HTML链接中javascript:void(0)的作用及现代替代方案
2026-11-05 10:07:47
在线调试 JS 时怎么引入 jQuery 或 Vue 等第三方库
2026-11-05 08:53:56
原生JavaScript实现简易颜色选择器原理与代码
2026-11-03 23:22:19
JS原生表单序列化:FormData与URLSearchParams用法详解
2026-11-03 23:13:21
