【玩转IT】JS面向对象编程轻松实现打飞机游戏

论坛 期权论坛 期权     
东软睿道   2019-7-27 15:00   2769   0
1.游戏概述


目前很多常见的小游戏都是通过纯前端技术开发完成的,比如大家经常玩的2048、别踩白块等等。前端常见的技术HTML5、CSS3、JAVASCRIPT、ES6、以及canvas、WebGL等等都能够做出非常好玩的小游戏。


今天给大家分享一个前端小游戏“打飞机”,既简单又有趣,希望对你学习前端有所帮助。


下图是游戏运行效果图,实际运行效果更好哦,如下:




下面我们来详细的介绍一下这款有趣的打飞机游戏。该游戏运用了如下技术:HTML5、CSS3、JAVASCRIPT、ES6,本文采用JAVASCRIPT面向对象编程来实现这款打飞机游戏,让大家一起感受一下JS面向对象的强大。

实现这个小游戏,一共分为七步,如下:
1、 div跟随光标移动自己的飞机
2、 自己飞机发射子弹
3、 显示敌人飞机
4、 子弹和敌机的碰撞检测
5、 敌机与自己飞机的碰撞检测
6、 背景&爆炸效果
7、 游戏积分


1.1div跟随光标移动自己的飞机


1、 页面样式设置如下。

我们在页面上创建一个div.board,在其内部添加一个div.plane,这是我们自己的飞机。我们将.board定位为absolute,并将.plane也设定为absolute,这样,.plane就可以相对于.board的左上角进行移动。


有一点需要注意的是,飞机跟随光标移动时,光标会一会在board上,一会在plane上,导致坐标计算不准确。为了避免这种现象,我们在board内添加一个遮罩层,将其zindex设定为最高,这样,就能保证,光标始终在同一个元素上移动,光标的位置也相对稳定了。



/* 清除浏览器默认样式 */
* {
margin: 0;
padding: 0;
}
html,body {
width: 100%;
height: 100%;
overflow: hidden;
}
/* 设定游戏面板的大小和背景色 */
div.board {
width: 500px;
height: 100%;
background: #e1e1e1;
/* 游戏面板居中 */
position: absolute;
left: 50%;
top: 0;
transform: translateX(-50%)
}
div.plane {
position: absolute;
width: 66px;
height: 80px;
/* 指定自己飞机背景 */
background-image: url(./resource/own5.png);
/* 让飞机跟随光标移动时,光标在飞机中间 */
margin-left: -33px;
margin-top: -40px;
}
.board .mask {
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 100%;
z-index: 100;
background: black;
/*设定背景色,让我们先感觉它的存在*/
opacity: 0.5;
}





2、定义页面显示内容













3、 编写js代码,创建Hero对象,详细代码如下:



class Hero {
constructor() {
// ES6中的class,必须有一个constructor,否则,浏览器会添加默认的函数
this.el = document.querySelector('.board .plane')
this.board = document.querySelector('.board')
this.init()
}
init() {
this.board.onmousemove = (evt) => {
let x = evt.offsetX;
let y = evt.offsetY;
//箭头函数中,没有自己的this,若用到了this,会向其外层作用域查找
if (x < 33) {
x = 33
}
if (x > 500 - 33) {
x = 500 - 33
}
this.el.style.left = x + 'px';
this.el.style.top = y + 'px'
}
}
}
let me = new Hero()





通过上面的代码,我们实现了飞机跟着鼠标移动的效果,效果图如下:



1.2自己飞机发射子弹


页面新加入子弹的样式,详细代码如下:


/*>>>>>>>>>>>>>>>>>>>>>>>>>02_自己飞机发射子弹>>>>>>>>>>>>>>>>>*/
.bullet {
width: 15px;
height: 30px;
position: absolute;
background-image: url(./resource/bimg_bullet.png)}




1、 js代码修改如下:

1)、Hero的构造器添加自己飞机发射子弹的代码,详细代码如下:


constructor() {
// ES6中的class,必须有一个constructor,否则,浏览器会添加默认的函数
this.el = document.querySelector('.board .plane')
this.board = document.querySelector('.board')
this.init()
/*>>>>>>>>>>>>>>>>>>>>>>>>>02_自己飞机发射子弹>>>>>>>>>>>>>>>>>*/
this.bulletFactory()
this.fire()
/**/
fire() {
this.fireTimer = setInterval(() => {
let bullets = document.querySelectorAll('.bullet');
for (let bullet of bullets) {
let current = bullet.offsetTop;
if (current < -100) {
bullet.remove()
} else {
bullet.style.top = current - 30 + 'px'
}
}
}, 50)
}
bulletFactory() {
this.factoryTimer = setInterval(() => {
let bulletCount = document.querySelectorAll('.bullet').length;
if (bulletCount > 6) return;
let bullet = document.createElement('div');
bullet.classList.add('bullet');
bullet.style.top = this.el.offsetTop + 'px';
bullet.style.left = this.el.offsetLeft + 33 - 7 + 'px';
this.board.appendChild(bullet)
}, 300)
}
destory() {
clearInterval(this.factoryTimer)
clearInterval(this.fireTimer)
this.el.remove()
let bullets = document.querySelectorAll('.bullet')
for(let bullet of bullets) {
bullet.remove()
}
}


}




通过上面的代码,我们实现了自己飞机发射子弹的效果,效果图如下:



1.3显示敌人飞机


1、 页面样式添加敌机样式.Enemy


/*>>>>>>>>>>>>>>>>>>>>>>>>>03_敌人飞机>>>>>>>>>>>>>>>>>*/
.enemy {
width: 90px;
height: 81px;
position: absolute;
background-image: url(./resource/xiaozhong.png)
}




2、 增加敌机的对象Enemy

在Enemy的构造函数中,我们创建了一个节点,并将节点放置在body上。我们为每个飞机添加了一个blood属性,默认值为1.在飞机的beated()方法内,我们将blood自减1,当blood为0时,我们将飞机销毁。这样,我们就可以在后期创建boss飞机时,将blood修改为更大的值,保证飞机可以抵挡更多次的子弹。


因为敌机非常多,所以我们不能为每个敌机都设定一个间隔函数来实现敌机向画面下方移动。如果这样,会导致页面中setInterval过多而使页面卡顿。我们将fly定义在Enemy这个类本身上,在fly函数中,我们设定一个setInterval,并在其内部,控制所有的敌机向下方移动。


同理,Enemy上定义了factory,也是基于这个考虑。我们每间隔1秒钟创建一个飞机。
并保证飞机的数目不大于10。详细代码如下:


/*>>>>>>>>>>>>>>>>>>>>>>>>>03_敌人飞机>>>>>>>>>>>>>>>>>*/
class Enemy {
constructor() {
this.init()
this.constructor.fly()
this.bulletFactory()
// this.fire()
}
init() {
this.el = document.createElement('div')
//将类实例与DOM节点进行关联,方便调用函数
this.el.instance = this;
this.el.classList.add('enemy')
this.board = document.querySelector('.board')
this.el.style.left = Math.floor(Math.random() * (this.board.offsetWidth - 90))
this.el.style.top = '-100px'
this.board.appendChild(this.el)
this.blood = 1
}
bulletFactory() {
}
beated() {
this.blood--;
if (this.blood === 0) {
this.constructor.destory(this);
}
}
destory() {
this.el.remove();
}
}
Enemy.fly = function () {
//相当于类的静态方法,使所有的敌机都共享使用一个setInterval来控制飞行。
//可以节省浏览器系统资源。下面的Enemy.factory同理
if (this.flyStart)
return
this.enemyCount = this.enemyCount || 0;
this.flyStart = true
this.flyTimer = setInterval(() => {
let enemies = document.querySelectorAll('.enemy');
for (let enemy of enemies) {
let current = enemy.offsetTop
if (current > window.innerHeight) {
enemy.remove()
this.enemyCount--;
return
}
enemy.style.top = current + 3 + 'px';
}
}, 16)
}
Enemy.factory = function () {
if (this.factoryStart)
return;
this.factoryStart = true;
this.factoryTimer = setInterval(() => {
if (this.enemyCount > 10)
return

new this()
this.enemyCount++;
}, 1000)
}
Enemy.destory = function (enemy) {
this.enemyCount--;
enemy.destory();
}
Enemy.loose = function () {
clearInterval(this.factoryTimer)
clearInterval(this.flyTimer)
}
Enemy.factory();




通过上面的代码,我们实现了地图上有自己飞机发射子弹及敌机的效果,效果图如下:



1.4子弹和敌机的碰撞检测


1、 添加对象Game,实现子弹与敌机的碰撞检测。


碰撞检测的思路为,分别求出两个元素中心点水平、垂直方向的距离。


当水平方向的距离小于两个元素宽度和的一半,且垂直方向距离小于高度和的一半,我们即判定两个元素相撞。


接着,我们选取了页面上所有的飞机元素,和所有的子弹元素。我们遍历飞机元素,首先跟所有的子弹进行碰撞检测,如果碰撞,则将子弹与飞机销毁。如果没撞上,再判断敌机跟我方飞机是否相撞,如果相撞,则游戏结束。详细代码如下:


/*>>>>>>>>>>>>>>>>>>>>>>>>>04_子弹和敌机的碰撞检测>>>>>>>>>>>>>>>>>*/
class Game {
constructor() {
this.judgeTimer = null
this.board = document.querySelector('.board');
}
start() {
this.hero = new Hero()
Enemy.factory()
this.judge()
}
judge() {
clearInterval(this.judgeTimer)
this.judgeTimer = setInterval(() => {
//取到所有子弹,与所有的敌机做碰撞检测,如果撞上则调用飞机的beated方法。
let enemies = document.querySelectorAll('.enemy')
let bullets = document.querySelectorAll('.bullet')
for (let enemy of enemies) {
for (let bullet of bullets) {
if (this.checkCollision(bullet, enemy)) {
bullet.remove()
enemy.instance.beated()
enemy = null;
break;
}
}
}
}, 50)
}
checkCollision(el1, el2) {
let center1 = {
x: el1.offsetLeft + el1.offsetWidth / 2,
y: el1.offsetTop + el1.offsetHeight / 2
}
let center2 = {
x: el2.offsetLeft + el2.offsetWidth / 2,
y: el2.offsetTop + el2.offsetHeight / 2
}
//两元素中心点水平方向距离(绝对值)
let distX = Math.abs(center1.x - center2.x)
//两元素中心点垂直方向距离(绝对值)
let distY = Math.abs(center1.y - center2.y)
//中心点水平方向距离小于宽度和的一半 且
//中心点垂直方向距离小于高度和的一半 撞上
if (distX < (el1.offsetWidth + el2.offsetWidth) / 2 &&
distY < (el2.offsetHeight + el2.offsetHeight) / 2) {
return true;    //撞上了
}
return false;   //没撞上
}
}
let game = new Game();
game.start();




1.5敌机与自己飞机的碰撞检测


1、 修改judge()函数,添加检测敌机与自己的碰撞的代码,详细代码如下:


judge() {
clearInterval(this.judgeTimer)
this.judgeTimer = setInterval(() => {
//取到所有子弹,与所有的敌机做碰撞检测,如果撞上则调用飞机的beated方法。
let enemies = document.querySelectorAll('.enemy')
let bullets = document.querySelectorAll('.bullet')
for (let enemy of enemies) {
for (let bullet of bullets) {
if (this.checkCollision(bullet, enemy)) {
bullet.remove()
enemy.instance.beated()
enemy = null;
break;
}
}
/*>>>>>>>>>>>>>>>>>>>>>>>>>05_检测敌机与自己的碰撞>>>>>>>>>>>>>>>>>*/
if (enemy !== null) {
//检测跟自己的碰撞
if (this.checkCollision(enemy, this.hero.el)) {
this.gameOver()
}
}
/**/
gameOver() {
console.log('game over!!!!')
clearInterval(this.judgeTimer)
Enemy.loose()
this.hero.destory()
}




1.6背景&爆炸效果


1、 修改页面样式,为dib.board选择器添加背景&爆炸效果


我们设定一个背景图片,并为其设定一个垂直向下平移的动画。这样,我们的飞机看起来就像是向上飞行一样,详细代码如下:


/* 设定游戏面板的大小和背景色 */
div.board {
width: 500px;
height: 100%;
background: #e1e1e1;
/* 游戏面板居中 */
position: absolute;
left: 50%;
top: 0;
translateX(-50%); -webkit-transform: >translateX(-50%); -moz-transform: >translateX(-50%); -o-transform: >translateX(-50%);
/*>>>>>>>>>>>>>>>>>>>>>>>>>06_背景&爆炸效果>>>>>>>>>>>>>>>>>*/
background-image: url('./resource/bg2.jpg');
background-size: 100% 600px;
animation: bg 8s linear infinite;
/**/
/* background: black; */
/*设定背景色,让我们先感觉它的存在*/
/* opacity: 0.5; */
/**/
.enemy.boom {
background-image: url(./resource/xzfjbz.png);
background-size: contain;
}

@keyframes bg {
from {
background-position: 0 0;
}
to {
background-position: 0 600px;
}
}




4、 添加爆炸音效







5、 这里我们设定一下遮罩层的lineHeight属性为可视区宽度,使得我们提示用户的信息,是垂直居中的。


/*>>>>>>>>>>>>>>>>>>>>>>>>>06_背景&爆炸效果>>>>>>>>>>>>>>>>>*/
document.querySelector('.mask').style.lineHeight = window.innerHeight + 'px';




6、 修改敌机对象的init()和destroy()
Init中,我们取到上面添加的audio标签,在destrory中,当一个飞机被销毁时,我们播放boom的音乐,详细代码如下:


init() {
this.el = document.createElement('div')
//将类实例与DOM节点进行关联,方便调用函数
this.el.instance = this;
this.el.classList.add('enemy')
this.board = document.querySelector('.board')
this.el.style.left = Math.floor(Math.random() * (this.board.offsetWidth - 90))
this.el.style.top = '-100px'
this.board.appendChild(this.el)
this.blood = 1
/*>>>>>>>>>>>>>>>>>>>>>>>>>06_背景&爆炸效果>>>>>>>>>>>>>>>>>*/
this.audio = document.querySelector('audio')
/**/
this.audio.load()
this.audio.play()
this.el.classList.add('boom');
setTimeout(() => {
this.el.remove()
}, 200)




7、 游戏结束后,页面显示效果


gameOver() {
console.log('game over!!!!')
clearInterval(this.judgeTimer)
Enemy.loose()
this.hero.destory()
/*>>>>>>>>>>>>>>>>>>>>>>>>>06_背景&爆炸效果>>>>>>>>>>>>>>>>>*/
let mask = document.querySelector('.mask')
mask.innerHTML = '游戏结束'
mask.style.opacity = 1;
/**/
window.addEventListener('click', function () {
let game = new Game();
game.start();
document.querySelector('.mask').style.opacity = 0;
}, {
once: true
})




通过上面的代码,我们实现了背景&爆炸的效果,效果图如下:





1.7统计积分


1、添加页面显示积分的样式


/*>>>>>>>>>>>>>>>>>>>>>>>>>07_积分>>>>>>>>>>>>>>>>>*/
.score {
width: 100px;
height: 70px;
line-height: 50px;
text-align: center;
background-image: url(./resource/fenshu.png);
color: white;
}




2、 页面添加一个div,用于显示积分计数




0




3、 修改对象的destroy(),添加积分计数功能


destory() {
/*>>>>>>>>>>>>>>>>>>>>>>>>>06_背景&爆炸效果>>>>>>>>>>>>>>>>>*/
this.audio.load()
this.audio.play()
this.el.classList.add('boom');
/*>>>>>>>>>>>>>>>>>>>>>>>>>07_积分>>>>>>>>>>>>>>>>>>>>>>>>>*/
let score = document.querySelector('.score').innerHTML;
document.querySelector('.score').innerHTML = ++score;
/*
分享到 :
0 人收藏
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

积分:5
帖子:1
精华:0
期权论坛 期权论坛
发布
内容

下载期权论坛手机APP