用JS实现飞机大战小游戏,效果图如下:
说明:飞机产生子弹的速度并没有如此之快,为了录制gif图,缩短了时间;
思路分析
整体的思路用的是单例模式,使用的方法有构造函数,面向对象,继承等。
一、创建地图,使用的是图片,封装类的方法
二、创建用户飞机,使用封装类的方法
1.在创建用户飞机之前需要创建一个飞机类,之后创建用户飞机和敌机就可以使用继承
2.飞机用图片表示,故在构造飞机创建方法是需创建dom元素img;
3. 另外需获取设置用户飞机的left,top值,根据鼠标移动事件改变用户飞机的left top值,让其跟随鼠标移动,鼠标移动事件中x,y值的获取与之前图片放大镜中覆盖层的鼠标移动事件一致,都需获取鼠标坐标减去偏移量以及自身宽高的一半,而且还要判断飞机移动的上下左右的临界值
this.mymove = function (x, y) {
if (this._plane) {
//计算坐标问题
this.left = x - this.map.offsetLeft - 40;
this.top = y - this.map.offsetTop - 40;
this.x = this.left <= 0 ? 0 : this.left >= 320 ? 320 : this.left;
this.y = this.top <= -10 ? -10 : this.top >= 540 ? 540 : this.top;
this._plane.style.left = this.x + "px";
this._plane.style.top = this.y + "px";
}
}
4.最后需要将地图挂载在飞机上。
5.注意:鼠标移动事件是创建给跟随对象的上一元素的,即这里创建给地图,给地图添加监听鼠标事件。
Map._map.addEventListener("mousemove", function () {
if (Map.mousemove) {
//鼠标移动的时候控制飞机的移动
var x = event.pageX || event.clientX;
var y = event.pageY || event.clientY;
myPlane.mymove(x, y);
}
})
三、创建用户飞机的子弹
1.创建子弹的类,由于子弹都是一组属性相同的数据,所以将子弹实例化对象存储在数组对象中,每创建一颗子弹就追加在数组中
2.子弹同样用一个png图片表示,其创建方法与用户飞机的创建方法一致,只是需要注意的是子弹首发应该显示在飞机中间的上方.(子弹坐标 x=飞机left+飞机自身的一半-子弹自身一半, y=飞机top-飞机自身的一半)
img.style.left = (parseInt(x) +40-15) + "px"; //x,y 传入参数的值,即是飞机的left值和top值
img.style.top = (parseInt(y) - 10) + "px";
3.创建子弹移动的方法,子弹移动只需要循环改变top值,使子弹的top值不停减小,在子弹飞出地图后,需将子弹移除,在移除子弹时,不仅需移除创建方法中的子弹,还需移除其实例化对象;利用存储实例化子弹数组的截取方法实现(splice方法)
4.在创建子弹和移动子弹都需要控制速度;
5.由于子弹是动态变化时创建的,所以使用定时器调用子弹类
window.requestAnimate = (function () {
return window.requestAnimationFrame ||
window.webkitRequestAnimationFrame ||
window.mozRequestAnimationFrame ||
function (callback) {
window.setTimeout(callback, 5000);
}
})();
(function animaloop() {
stop = requestAnimate(animaloop);
//造子弹的方法
myPlane.showBullet.createBullet(myPlane.x, myPlane.y);
//子弹移动
myPlane.showBullet.bulletmove();
})();
6.实例化子弹对象,并将子弹挂载在飞机上,成为飞机的一个属性
Bullet=new bullet();
myplane.prototype.showBullet = Bullet;
四、创建敌机
1.敌机也是一组属性类似的数据,故定义数组来存储;
2.同样需要创建敌机类,并继承飞机类,敌机用图片表示,本案例随机创建10种敌机,大小也随机产生
3.构造敌机的下落方法与子弹原理一致,即改变每个敌机的top属性值
4.子弹击打敌机,需要将敌机挂载到子弹上,判断子弹的left和top值是否在敌机的范围内,若在敌机移除,子弹移除。
if ( left>= eleft && left <= eleft + ewidth && top <= etop + eheight - 20) {
//left--子弹 eleft--敌机
this.enemyArray[index].remove();
this.enemyArray.splice(index, 1);
this._bullet[i][k].remove();
this._bullet[i].splice(k, 1);
}
5.当敌机飞出地图,需移除创建方法的敌机还有其实例化对象,同样用实例化存储敌机的数组截取的方法实现;
6.在创建敌机和移动敌机也需要控制速度;
五、游戏结束
设置用户飞机的血量,在敌机未被子弹击中而移除时,血量逐渐减少,直到血量<=0,游戏结束并清除所有定时器;
全部代码
TML+CSS
<!DOCTYPE html>
<html>
<head lang="en">
<meta charset="UTF-8">
<title></title>
<style>
*{
margin: 0;
padding: 0;
}
.map{
width: 400px;
height: 600px;
margin: 0 auto;
position: relative;
border: 1px solid silver;
background-size: 400px 600px;
background-repeat: no-repeat;
cursor: none;
}
.my{
position: absolute;
width: 80px;
height: 80px;
}
.bullet{
position: absolute;
width: 30px;
height: 30px;
}
.enemy{
position: absolute;
z-index: 5;
}
.score{
position: absolute;
top:5px;
left: 0;
width: 400px;
text-align: center;
color: white;
font-size: 25px;
font-family: cursive;
}
.blod{
position: absolute;
right: -10px;
bottom: 0;
border: 1px solid white;
box-sizing: border-box;
width: 10px;
height: 600px;
background-color: #00df00;
}
.sb{
position: absolute;
right: 0px;
bottom: 0;
width: 8px;
background-color: red;
transition: all 0.5s linear;
}
</style>
</head>
<body>
<script src="js/index.js"></script>
</body>
</html>
JavaScript
/**
* Created by Administrator on 2019/2/16.
*/
//创建变量,接收对象
var Map;
var myPlane; //我的飞机
var Enemy; //敌机
var Bullet; //子弹
var game = (function () {
//创建地图
function map() {
this._map = null;
this.bgimage = "url(./img/background_1.gif)";
this.score = 0;
this.span = null;
this.blod = 600;
this.blodele = null;
this.mousemove = true;
this.createMap = function () {
if (this._map == null) {
this._map = document.createElement("div");
this._map.className = "map";
this._map.style.backgroundImage = this.bgimage;
}
};
this.createSpan = function () {
this.span = document.createElement("span");
this.span.innerHTML = "Score:" + this.score;
this.span.className = "score";
this._map.appendChild(this.span);
};
this.createMyBlod = function () {
this.blodele = document.createElement("div");
this.blodele.className = "blod";
var s = document.createElement("div");
s.className = "sb";
s.style.height = this.blod + "px";
this.blodele.appendChild(s);
this._map.appendChild(this.blodele);
}
}
//创建飞机类
function plane(img, c, l, t) { //使用多继承
this.bgimage = img;
this.classname = c;
this.left = l;
this.top = t;
}
//创建我的飞机
function myplane(img, c, l, t) {
this._plane = null;
plane.apply(this, [img, c, l, t]); //构造继承
this.x = null;
this.y = null;
this.createPlane = function () {
if (this._plane == null) {
this._plane = document.createElement("img");
this._plane.className = this.classname;
this._plane.src = this.bgimage;
}
this._plane.style.left = this.left + "px";
this._plane.style.top = this.top + "px";
this.x = this.left;
this.y = this.top;
this.map.appendChild(this._plane);
};
this.mymove = function (x, y) {
//计算坐标问题
if (this._plane) {
this.left = x - this.map.offsetLeft - 40;
this.top = y - this.map.offsetTop - 40;
this.x = this.left <= 0 ? 0 : this.left >= 320 ? 320 : this.left;
this.y = this.top <= -10 ? -10 : this.top >= 540 ? 540 : this.top;
this._plane.style.left = this.x + "px";
this._plane.style.top = this.y + "px";
}
}
}
//创建敌机
function enemy() {
plane.apply(this);
this.bgimage = ["ep_1.png", "ep_2.png", "ep_3.png", "ep_4.png", "ep_5.png", "ep_6.png", "ep_7.png", "ep_8.png", "ep_9.png", "ep_10.png"];
this.width = null;
this.random = 0;
this.enemyArray = [];
this.enemyHz = 40;
this.speed = 5;
this.enemymoveHz = 2;
this.createEnemy = function () {
this.enemyHz--;
if (this.enemyHz < 0) {
this.enemyHz = 40;
//随机产生飞机
this.random = Math.floor(Math.random() * this.bgimage.length);
var enemy = document.createElement("img");
enemy.src = "img/" + this.bgimage [this.random];
enemy.className = "enemy";
//setAttribute() 方法创建或改变某个新属性。 添加的属性名,属性值
enemy.setAttribute("data-blod", Math.random() * 4 + 2); //敌机的血量,打几次消失
enemy.setAttribute("data-score", Math.random() * 200 + 100); //不同的敌机消失加不同的分数
this.width = (Math.random() * 30 + 70);
enemy.style.width = this.width + "px";
enemy.style.height = this.width + "px";
enemy.style.top = 0 + "px";
this.left = ( Math.random() * 400);
enemy.style.left = (this.left - this.width < 0 ? 0 : this.left - this.width) + "px";
this.enemyArray.push(enemy);
this.map._map.appendChild(enemy);
}
};
this.enemymove = function () {
this.enemymoveHz--;
if (this.enemymoveHz < 0) {
this.enemymoveHz = 2;
for (var i = 0; i < this.enemyArray.length; i++) {
var top = parseInt(this.enemyArray[i].style.top);
top += this.speed;
this.enemyArray[i].style.top = top + "px";
if (top > 600- (parseInt(this.enemyArray[i].style.width)) ) {
this.enemyArray[i].remove();
this.enemyArray.splice(i, 1);
this.map.blod -= 100;
this.map.blodele.children[0].style.height = this.map.blod + "px";
if (this.map.blod <= 0) {
window.cancelAnimationFrame(stop); //画面停止
this.map.mousemove = false;
}
}
}
}
}
}
//创建子弹
function bullet() {
this._bullet = [];
this.num = 1;
this.width = null;
this.className = "bullet";
this.src = "img/myb_1.png";
this.createbulletHz = 8;
this.bulletmoveHz = 1;
this.speed = 4;
this.createBullet = function (x, y) {
this.createbulletHz--;
if (this.createbulletHz <= 0) {
this.createbulletHz = 8;
var but = [];
for (var i = 0; i < this.num; i++) {
var img = document.createElement("img");
img.className = this.className;
img.src = this.src;
if (this.num == 1) {
img.style.left = (parseInt(x) + 26) + "px";
img.style.top = (parseInt(y) - 10) + "px";
}
if (this.num == 2) {
img.style.left = (parseInt(x) + 4 + (i * 45)) + "px";
img.style.top = (parseInt(y) - 10) + "px";
}
if (this.num == 3) {
img.style.left = (parseInt(x) + 4 + (i * 22)) + "px";
img.style.top = (parseInt(y) - 10) + "px";
}
img.style.width = this.width + "px";
this.map._map.appendChild(img);
but.push(img);
}
this._bullet.push(but);
}
};
this.bulletmove = function () {
this.bulletmoveHz--;
if (this.bulletmoveHz <= 0) {
this.bulletmoveHz = 1;
for (var i = 0; i < this._bullet.length; i++) {
for (var k = 0; k < this._bullet[i].length; k++) {
if(this._bullet[i][k]){
var top = parseInt(this._bullet[i][k].style.top);
top -= this.speed;
this._bullet[i][k].style.top = top + "px";
if (top <= 0) {
if (this.num == 1) {
this._bullet[i][k].remove();
this._bullet.splice(i, 1);
}
else if (this.num == 2) {
this._bullet[i][k].remove();
this._bullet[i].splice(k, 1);
}
else {
this._bullet[i][k].remove();
this._bullet[i].splice(k, 1);
}
}
}
}
}
//子弹在移动的过程当中 寻找敌机
for (var i = 0; i < this._bullet.length; i++) {
for (var k = 0; k < this._bullet[i].length; k++) {
if (this._bullet[i][k]) {
var left = parseInt(this._bullet[i][k].style.left);
var top = parseInt(this._bullet[i][k].style.top);
var width = parseInt(this._bullet[i][k].style.width);
for (var index in this.enemyArray) {
//敌机的左上 宽高
var eleft = parseInt(this.enemyArray[index].style.left);
var etop = parseInt(this.enemyArray[index].style.top);
var ewidth = parseInt(this.enemyArray[index].style.width);
var eheight = ewidth;
//判断是否撞击
if (left >= eleft && left <= eleft + ewidth && top <= etop + eheight - 20) {//避免没有碰到就打到飞机
var blod = this.enemyArray[index].getAttribute("data-blod");
blod--;
this.enemyArray[index].setAttribute("data-blod", blod);
if (blod <= 0) {
var score = this.enemyArray[index].getAttribute("data-score");
this.map.score += parseInt(score);
this.map.span.innerHTML = "Score:" + this.map.score;
this.enemyArray[index].remove();
this.enemyArray.splice(index, 1);
}
this._bullet[i][k].remove();
this._bullet[i].splice(k, 1);
}
}
}
}
}
}
}
}
//实例化场景
function createScene() {
//实例化地图
Map = new map();
Map.createMap();
Map.createSpan();
Map.createMyBlod();
//给地图添加监听事件
Map._map.addEventListener("mousemove", function () {
//鼠标移动的时候控制飞机的移动
var x = event.pageX || event.clientX;
var y = event.pageY || event.clientY;
myPlane.mymove(x, y);
});
//实例化我的飞机 同时传参
//挂载飞机到地图上
//挂载地图到子弹上
bullet.prototype.map = Map;
//挂载地图到敌机上
enemy.prototype.map = Map;
myplane.prototype.map = Map._map; //原型链追加
//实例化子弹并直接挂载到飞机上 成为飞机的一个属性
Bullet=new bullet();
myplane.prototype.showBullet =Bullet;
myPlane = new myplane("img/my_2.png", "my", "160", "500");
myPlane.createPlane();
//实例化敌机
Enemy = new enemy();
//敌机挂载在子弹上
bullet.prototype.enemyArray = Enemy.enemyArray;
return Map._map;
}
return {
getmap: createScene() //获得该实例的方法
}
//单例模式的思路是:一个类能返回一个对象的引用(并且永远是同一个)和一个获得该实例的方法(静态方法,通常使用 getInstance 名称)。
})
();
//添加到页面
document.body.appendChild(game.getmap);
window.requestAnimate = (function () {
return window.requestAnimationFrame ||
window.webkitRequestAnimationFrame ||
window.mozRequestAnimationFrame ||
function (callback) {
window.setTimeout(callback, 5000);
}
})();
(function animaloop() {
requestAnimate(animaloop);
//造子弹的方法 反复调用就可出现多个子弹
myPlane.showBullet.createBullet(myPlane.x, myPlane.y);
//子弹移动
myPlane.showBullet.bulletmove(); //竖直向上
//创建敌机
Enemy.createEnemy();
//敌机移动
Enemy.enemymove();
if (Map.score < 5000) {
Bullet.num = 1;
}
else if (Map.score < 10000) {
Bullet.num = 2;
}
else {
Bullet.num = 3;
}
})();