原生JS经典射击游戏-小蜜蜂

安稳与你 提交于 2019-12-07 17:43:22

经典射击游戏-小蜜蜂做之前的大概思路看下面这个图,这个游戏我打算用单例模式来写也就是平常我们说的单体模式。

源码链接
在线玩经典射击游戏-小蜜蜂


经典射击游戏-小蜜蜂

小蜜蜂


单体是一个用来划分命名空间并将一批相关的属性和方法组织在一起的对象,如果他可以被实例化,那么他只能被实例化一次。 单体模式是javascript里面最基本但是也是最有用的也是最常用的模式之一。

单体模式的特点:

  1. 可以用来划分命名空间,从而清除全局变量所带来的危险,也就是该类只有一个实例
  2. 该类自行创建该实例,即在该类内部创建自身的实例对象,向整个系统公开这个实例接口。
  3. 利用分支技术来封装浏览器直接的差异
  4. 可以把代码组织的更为一体,便于阅读和维护。

单体模式的思路是: 一个类能返回一个对象的引用(并且永远是同一个)和一个获得该实例的方法(静态方法,通常使用getInstance名称)。那么当我们调用这个方法时,如果类持有的引用不为空就返回该引用,否者就创建该类的实例,并且将实例引用赋值给该类保持的那个引用再返回。同时将该类的构造函数定义为私有方法,避免其他函数使用该构造函数来实例化对象,只通过该类的静态方法来得到该类的唯一实例。 用一句话来说,单例模式的核心是确保只有一个实例,并提供全局访问。


布局代码

<!doctype html>
<html lang="en">
 <head>
  <meta charset="UTF-8">
  <title>经典射击游戏-小蜜蜂</title>
  <link rel="stylesheet" href="css/index.css" />
  <script type="text/javascript" src="js/index.js" charset="UTF-8"></script>
 </head>
 <body>
    <div id="containt">
        <div id="goBtn">游戏开始</div>
    </div>
 </body>
</html>

CSS

body,ul,li,span{
    margin:0;
    padding:0;
}
html,body {
    width:100%;
    height:100%;
    overflow:hidden;
}
body {
    background:#f1f1f1;
}
li{
    list-style:none;
}
.score {
    color:white;
}
#containt{
    width:900px;
    height:600px;
    overflow:hidden;
    background:black;
    margin:10px auto;
    position:relative;
}
#goBtn{ 
    color:white;
    font-size:20px;
    cursor:pointer;
    border:1px #FFFFFF solid;
    width:100px;
    height:40px;
    line-height:40px;
    text-align:center;
    position:absolute;
    top:50%;
    left:50%;
    margin-top:-20px;
    margin-left:-50px;
}
.enemy1 {/* 一级怪的样式 */
    width:25px;
    height:25px;
    background:url(../images/aliensheet.png);
    background-position:left bottom;
    overflow:hidden;
    float:left;
}
.enemy2 {/* 二级怪的样式 */
    width:25px;
    height:25px;
    background:url(../images/aliensheet.png);
    background-position:left 50px;
    overflow:hidden;
    float:left;
}
.enemy3 {/* 三级怪的样式 */
    width:25px;
    height:25px;
    background:url(../images/aliensheet.png);
    background-position:left 72px;
    overflow:hidden;
    float:left;
}
.enemy4 {/* 四级怪的样式 */
    width:25px;
    height:25px;
    background:url(../images/aliensheet.png);
    background-position:left top;
    overflow:hidden;
    float:left;
}
.air { /* 飞机的样式 */
    width:32px;
    height:25px;
    background:url(../images/playersheet.png);
    background-position:left 0px;
    overflow:hidden;
    position:absolute;
    left:0px;
    top:0px;
}
.bullet { /* 子弹的样式 */
    width:4px;
    height:16px;
    background:url(../images/lasers.gif);
    background-position:left 0px;
    overflow:hidden;
    position:absolute;
}
#list {
    position:relative;
}
#list:after {
    content:'';
    display:block;
    clear:both;
}

javascript

window.onload = function() {
    var oBtn = document.getElementById("goBtn");
    oBtn.onclick = function() {
        this.style.display = "none";
        Game.init("containt");
    }
}
var Game = {
    //敌人数据
    oEnemy : {
        /**
        *   style 敌人样式
        *   blood 敌人的血量
        *   speed 敌人的移动速度
        *   score 敌人的积分
        */
        oEnemy1 : {style:"enemy1",blood:1,speed:2,score:1},
        oEnemy2 : {style:"enemy2",blood:2,speed:3,score:2},
        oEnemy3 : {style:"enemy3",blood:3,speed:4,score:3},
        oEnemy4 : {style:"enemy4",blood:4,speed:5,score:4}
    },
    gk : [
        //第一关
        {
            aMap : [
                'oEnemy2','oEnemy2','oEnemy2','oEnemy2','oEnemy2','oEnemy2','oEnemy2','oEnemy2','oEnemy2','oEnemy2',
                'oEnemy2','oEnemy2','oEnemy2','oEnemy2','oEnemy2','oEnemy2','oEnemy2','oEnemy2','oEnemy2','oEnemy2',
                'oEnemy2','oEnemy2','oEnemy2','oEnemy2','oEnemy2','oEnemy2','oEnemy2','oEnemy2','oEnemy2','oEnemy2',
                'oEnemy1','oEnemy1','oEnemy1','oEnemy1','oEnemy1','oEnemy1','oEnemy1','oEnemy1','oEnemy1','oEnemy1',
                'oEnemy1','oEnemy1','oEnemy1','oEnemy1','oEnemy1','oEnemy1','oEnemy1','oEnemy1','oEnemy1','oEnemy1',
                'oEnemy1','oEnemy1','oEnemy1','oEnemy1','oEnemy1','oEnemy1','oEnemy1','oEnemy1','oEnemy1','oEnemy1'
            ],
            cloNum : 10,
            iSpeedX : 3,
            iSpeedY : 2,
            timer : 2000,
        },
        //第二关
        {
            aMap : [
                'oEnemy3','oEnemy3','oEnemy3','oEnemy3','oEnemy3','oEnemy3','oEnemy3','oEnemy3','oEnemy3','oEnemy3',
                'oEnemy2','oEnemy2','oEnemy2','oEnemy2','oEnemy2','oEnemy2','oEnemy2','oEnemy2','oEnemy2','oEnemy2',
                'oEnemy2','oEnemy2','oEnemy2','oEnemy2','oEnemy2','oEnemy2','oEnemy2','oEnemy2','oEnemy2','oEnemy2',
                'oEnemy1','oEnemy1','oEnemy1','oEnemy1','oEnemy1','oEnemy1','oEnemy1','oEnemy1','oEnemy1','oEnemy1',
                'oEnemy1','oEnemy1','oEnemy1','oEnemy1','oEnemy1','oEnemy1','oEnemy1','oEnemy1','oEnemy1','oEnemy1',
                'oEnemy1','oEnemy1','oEnemy1','oEnemy1','oEnemy1','oEnemy1','oEnemy1','oEnemy1','oEnemy1','oEnemy1'
            ],
            cloNum : 10,
            iSpeedX : 4,
            iSpeedY : 2,
            timer : 3000,
        },
        //第三关
        {
            aMap : [
                'oEnemy3','oEnemy3','oEnemy3','oEnemy3','oEnemy3','oEnemy3','oEnemy3','oEnemy3','oEnemy3','oEnemy3','oEnemy3','oEnemy3','oEnemy3',
                'oEnemy2','oEnemy2','oEnemy2','oEnemy2','oEnemy2','oEnemy2','oEnemy2','oEnemy2','oEnemy2','oEnemy2','oEnemy2','oEnemy2','oEnemy2',
                'oEnemy2','oEnemy2','oEnemy2','oEnemy2','oEnemy2','oEnemy2','oEnemy2','oEnemy2','oEnemy2','oEnemy2','oEnemy2','oEnemy2','oEnemy2',
                'oEnemy1','oEnemy1','oEnemy1','oEnemy1','oEnemy1','oEnemy1','oEnemy1','oEnemy1','oEnemy1','oEnemy1','oEnemy1','oEnemy1','oEnemy1',
                'oEnemy1','oEnemy1','oEnemy1','oEnemy1','oEnemy1','oEnemy1','oEnemy1','oEnemy1','oEnemy1','oEnemy1','oEnemy1','oEnemy1','oEnemy1',
                'oEnemy1','oEnemy1','oEnemy1','oEnemy1','oEnemy1','oEnemy1','oEnemy1','oEnemy1','oEnemy1','oEnemy1','oEnemy1','oEnemy1','oEnemy1'
            ],
            cloNum : 13,
            iSpeedX : 5,
            iSpeedY : 2,
            timer : 3000,
        },
        //第四关
        {
            aMap : [
                'oEnemy4','oEnemy4','oEnemy4','oEnemy4','oEnemy4','oEnemy4','oEnemy4','oEnemy4','oEnemy4','oEnemy4','oEnemy4','oEnemy4','oEnemy4',
                'oEnemy3','oEnemy3','oEnemy3','oEnemy3','oEnemy3','oEnemy3','oEnemy3','oEnemy3','oEnemy3','oEnemy3','oEnemy3','oEnemy3','oEnemy3',
                'oEnemy3','oEnemy3','oEnemy3','oEnemy3','oEnemy3','oEnemy3','oEnemy3','oEnemy3','oEnemy3','oEnemy3','oEnemy3','oEnemy3','oEnemy3',
                'oEnemy1','oEnemy1','oEnemy1','oEnemy1','oEnemy1','oEnemy1','oEnemy1','oEnemy1','oEnemy1','oEnemy1','oEnemy1','oEnemy1','oEnemy1',
                'oEnemy1','oEnemy1','oEnemy1','oEnemy1','oEnemy1','oEnemy1','oEnemy1','oEnemy1','oEnemy1','oEnemy1','oEnemy1','oEnemy1','oEnemy1',
                'oEnemy1','oEnemy1','oEnemy1','oEnemy1','oEnemy1','oEnemy1','oEnemy1','oEnemy1','oEnemy1','oEnemy1','oEnemy1','oEnemy1','oEnemy1'
            ],
            cloNum : 13,
            iSpeedX : 5,
            iSpeedY : 2,
            timer : 3000,
        },
        //第五关
        {
            aMap : [
                'oEnemy4','oEnemy4','oEnemy4','oEnemy4','oEnemy4','oEnemy4','oEnemy4','oEnemy4','oEnemy4','oEnemy4','oEnemy4','oEnemy4','oEnemy4',
                'oEnemy4','oEnemy4','oEnemy4','oEnemy4','oEnemy4','oEnemy4','oEnemy4','oEnemy4','oEnemy4','oEnemy4','oEnemy4','oEnemy4','oEnemy4',
                'oEnemy3','oEnemy3','oEnemy3','oEnemy3','oEnemy3','oEnemy3','oEnemy3','oEnemy3','oEnemy3','oEnemy3','oEnemy3','oEnemy3','oEnemy3',
                'oEnemy1','oEnemy1','oEnemy1','oEnemy1','oEnemy1','oEnemy1','oEnemy1','oEnemy1','oEnemy1','oEnemy1','oEnemy1','oEnemy1','oEnemy1',
                'oEnemy1','oEnemy1','oEnemy1','oEnemy1','oEnemy1','oEnemy1','oEnemy1','oEnemy1','oEnemy1','oEnemy1','oEnemy1','oEnemy1','oEnemy1',
                'oEnemy1','oEnemy1','oEnemy1','oEnemy1','oEnemy1','oEnemy1','oEnemy1','oEnemy1','oEnemy1','oEnemy1','oEnemy1','oEnemy1','oEnemy1'
            ],
            cloNum : 13,
            iSpeedX : 5,
            iSpeedY : 2,
            timer : 3000,
        },
        //第六关
        {
            aMap : [
                'oEnemy4','oEnemy4','oEnemy4','oEnemy4','oEnemy4','oEnemy4','oEnemy4','oEnemy4','oEnemy4','oEnemy4','oEnemy4','oEnemy4','oEnemy4','oEnemy4','oEnemy4','oEnemy4','oEnemy4','oEnemy4',
                'oEnemy4','oEnemy4','oEnemy4','oEnemy4','oEnemy4','oEnemy4','oEnemy4','oEnemy4','oEnemy4','oEnemy4','oEnemy4','oEnemy4','oEnemy4','oEnemy4','oEnemy4','oEnemy4','oEnemy4','oEnemy4',
                'oEnemy3','oEnemy3','oEnemy3','oEnemy3','oEnemy3','oEnemy3','oEnemy3','oEnemy3','oEnemy3','oEnemy3','oEnemy3','oEnemy3','oEnemy3','oEnemy3','oEnemy3','oEnemy3','oEnemy3','oEnemy3',
                'oEnemy1','oEnemy1','oEnemy1','oEnemy1','oEnemy1','oEnemy1','oEnemy1','oEnemy1','oEnemy1','oEnemy1','oEnemy1','oEnemy1','oEnemy1','oEnemy1','oEnemy1','oEnemy1','oEnemy1','oEnemy1',
                'oEnemy1','oEnemy1','oEnemy1','oEnemy1','oEnemy1','oEnemy1','oEnemy1','oEnemy1','oEnemy1','oEnemy1','oEnemy1','oEnemy1','oEnemy1','oEnemy1','oEnemy1','oEnemy1','oEnemy1','oEnemy1',
                'oEnemy1','oEnemy1','oEnemy1','oEnemy1','oEnemy1','oEnemy1','oEnemy1','oEnemy1','oEnemy1','oEnemy1','oEnemy1','oEnemy1','oEnemy1','oEnemy1','oEnemy1','oEnemy1','oEnemy1','oEnemy1',
            ],
            cloNum : 18,
            iSpeedX : 5,
            iSpeedY : 2,
            timer : 3000,
        }
    ],  
    //通关数
    gs : {
        gs : 0,
    },
    air : {
        style : "air", //飞机样式
        bulletStyle : "bullet" //子弹样式
    },
    //初始化
    init : function(id) {
        this.oParent = document.getElementById(id);
        this.createScore();
        this.createEemey(this.gs.gs);
        this.createAir()
    },
    //创建积分
    createScore : function() {
        var oSco = document.createElement("div");
        oSco.className = "score";
        oSco.innerHTML = "积分:<span>0</span>分";
        this.oParent.appendChild(oSco);
        this.oNum = oSco.getElementsByTagName("span")[0];
    },
    //创建敌人
    createEemey : function(iNow) {
        //如果敌人整体的ul还存在就把它删掉
        if(this.list) {
            clearInterval(this.list.timer); // 如果敌人整体的ul还存在就应该删掉前把定时器关掉
            this.oParent.removeChild(this.list); //删除敌人
        }
        var oGk = this.gk[iNow]; //获取关卡数据
        //创建ul并且加到containt里面去
        var oUl = document.createElement("ul");
        oUl.id="list";
        oUl.style.width = oGk.cloNum*25 + "px"; //给ul宽度,也就是控制每一行显示的个数
        this.list = oUl;
        this.oParent.appendChild(oUl); // 追加
        oUl.style.left = (this.oParent.offsetWidth - oUl.offsetWidth)/2 + "px"; //默认让敌人在中间出发
        document.title = "经典射击游戏-小蜜蜂第" + (this.gs.gs + 1) +"关";
        //创建li
        for(var i = 0; i < oGk.aMap.length; i++) {
            var oLi = document.createElement("li");
            //给敌人上装备比如:样式,血量,积分。把敌人的装备转成他们自身自定义属性
            oLi.className = this.oEnemy[oGk.aMap[i]].style;
            oLi.blood = this.oEnemy[oGk.aMap[i]].blood;
            oLi.speed = this.oEnemy[oGk.aMap[i]].speed;
            oLi.score = this.oEnemy[oGk.aMap[i]].score;
            oLi.zIndex1 = 1;
            oUl.appendChild(oLi);//追加
        }
        this.aLi = oUl.getElementsByTagName("li");// 向整个系统公开这个实例接口。

        //布局转换
        var arr = [];
        for(var i = 0; i < this.aLi.length; i++) {
            arr.push({l : this.aLi[i].offsetLeft,t : this.aLi[i].offsetTop}); //把宽高存到json里面,方便提取
        }
        // 加定位前把left和top值先加上去
        for(var i = 0; i < this.aLi.length; i++) {
            this.aLi[i].style.left = arr[i].l + "px";
            this.aLi[i].style.top = arr[i].t + "px";
        }
        //加定位
        for(var i = 0; i < this.aLi.length; i++) {
            this.aLi[i].style.position = "absolute";
        }
        this.runEemey(oGk); //敌人开始移动
    },
    //敌人移动
    runEemey : function(oGk) {
        var This = this; //转this指向,这没什么好说的
        var l = 0;
        var R = this.oParent.offsetWidth - this.list.offsetWidth; // 敌人右移动最大的left
        this.list.timer = setInterval(function(){
            if(This.list.offsetLeft < l) {
                oGk.iSpeedX*=-1; //转方向移动
                This.list.style.top = This.list.offsetTop + oGk.iSpeedY + "px"; //敌人向y轴移动一点点
            }
            else if(This.list.offsetLeft>R)
            {
                oGk.iSpeedX*=-1;//转方向移动
                This.list.style.top = This.list.offsetTop + oGk.iSpeedY + "px";//敌人向y轴移动一点点
            }
            This.list.style.left = This.list.offsetLeft + oGk.iSpeedX + "px"; //敌人向水平方向移动
        },200);
        //在这开始单兵做战
        setInterval(function(){
            This.oneMove();
        },oGk.timer)
    },
    //单兵做战 敌人追随
    oneMove : function() {
        var This = this;
        var nowLi = this.aLi[Math.floor(Math.random()*this.aLi.length-1)]; // 随机选一个敌人出来作战,厉害了我的哥
        nowLi.style.zIndex = nowLi.zIndex1++; // 敌人每次出来都是正前方出来
        nowLi.timer = setInterval(function(){
            //蜜蜂和飞机中心点的水平距离
            var x = (This.oAir.offsetLeft + This.oAir.offsetWidth/2) - (nowLi.offsetLeft + This.list.offsetLeft + nowLi.offsetWidth/2); //X轴的距离
            var y = (This.oAir.offsetTop + This.oAir.offsetHeight/2) - (nowLi.offsetTop + This.list.offsetTop + nowLi.offsetHeight/2); //Y的距离
            var z = Math.sqrt(x*x+y*y); //勾股定理,我就不多说了
            var oIsX=nowLi.speed*x/z; 
            var oIsY=nowLi.speed*y/z;
            nowLi.style.left = nowLi.offsetLeft + oIsX + "px"; //X轴追起来了,哈哈
            nowLi.style.top = nowLi.offsetTop + oIsY + "px"; //Y轴追起来了,哈哈
            //检测敌人是否碰到飞机,碰到就结束游戏
            if(This.pz(This.oAir,nowLi)){
                alert('游戏结束');
                window.location.reload(); //从新加载
            }
        },30);
    },
    // 创建飞机
    createAir : function() {
        var oAir = document.createElement("div");
        oAir.className = this.air.style;
        this.oAir = oAir;
        this.oParent.appendChild(oAir); // 追加
        //把飞机初始化位置调到中间
        oAir.style.left = (this.oParent.offsetWidth - oAir.offsetWidth)/2 + "px";
        oAir.style.top = (this.oParent.offsetHeight - oAir.offsetHeight-10) + "px";
        this.bindAir();
    },
    //操作飞机
    bindAir : function() {
        var timer = null;
        var iNum = 0;
        var This = this;
        document.onkeydown = function() {
            document.onkeydown = function(ev) {
                var ev = ev || event;
                if(!timer) {
                    timer = setInterval(run,30);
                }
                if(ev.keyCode == 37 ) { //按左方向的时候干的事
                    iNum = 1;
                } else if (ev.keyCode == 39) {  //按右方向的时候干的事
                    iNum = 2;
                } else if( ev.keyCode == 38) {//按上方向的时候干的事
                    iNum = 3;
                } else if( ev.keyCode == 40) {//按下方向的时候干的事
                    iNum = 4;
                }
            }
            document.onkeyup = function(ev) {
                var ev = ev || event;
                clearInterval(timer);
                timer = null;
                iNum = 0;
                if(ev.keyCode == 32) {
                    This.createBullet();
                }
            }
            function run() {
                if(iNum == 1) {
                    //禁止飞机从左边出去
                    if(This.oAir.offsetLeft<=0) {
                        This.oAir.style.left=0 + "px";
                    }
                    This.oAir.style.left = This.oAir.offsetLeft - 6 + "px";
                } else if (iNum == 2) {
                    //禁止飞机从右边出去
                    if(This.oParent.offsetWidth - This.oAir.offsetWidth <= This.oAir.offsetLeft) {
                        This.oAir.style.left = This.oParent.offsetWidth - This.oAir.offsetWidth + "px";
                    }
                    This.oAir.style.left = This.oAir.offsetLeft + 6 + "px";
                } else if (iNum == 3) {
                    //禁止飞机从上边出去
                    if(This.oAir.offsetTop <= 0 ) {
                        This.oAir.style.top = 10+"px";
                    }
                    This.oAir.style.top = This.oAir.offsetTop - 6 + "px";
                } else if (iNum == 4) {
                    //禁止飞机从下边出去
                    if(This.oAir.offsetTop >= This.oParent.offsetHeight - This.oAir.offsetHeight-10 ) {
                        This.oAir.style.top = This.oParent.offsetHeight - This.oAir.offsetHeight-10+"px";
                    }
                    This.oAir.style.top = This.oAir.offsetTop + 6 + "px";
                }
            }
        }
    },
    // 创建子弹
    createBullet : function() {
        //创建div
        var oBullet = document.createElement("div");
        oBullet.className = this.air.bulletStyle;
        this.oParent.appendChild(oBullet);
        //把子弹定位到飞机的正前方
        oBullet.style.left = this.oAir.offsetLeft + this.oAir.offsetWidth/2 + "px";
        oBullet.style.top = this.oAir.offsetTop - oBullet.offsetHeight + "px";
        //创建后就开始运动
        this.runBullet(oBullet);
    },
    //子弹运动
    runBullet : function(obj) {
        var This = this;
        obj.timer = setInterval(function(){
            if(obj.offsetTop < -11 ) {//当子弹飞出游戏窗就销毁该子弹,谨防子弹一直飞,导致子弹过多,页面元素过多影响性能
                clearInterval(obj.timer);
                This.oParent.removeChild(obj);
            } else {
                obj.style.top = obj.offsetTop - 5 + "px"; //子弹的速度
            }
            for(var i = 0; i < This.aLi.length; i++) {
                if(This.pz(obj,This.aLi[i])) { //如果碰撞了就为true否则为false
                    // 如果敌人血量为0了,就把他干掉
                    if(This.aLi[i].blood == 1 ) { 
                        clearInterval(This.aLi[i].timer);
                        This.oNum.innerHTML = parseInt(This.oNum.innerHTML) + This.aLi[i].score;
                        This.list.removeChild(This.aLi[i]);
                    } else { //或者血量减1
                        This.aLi[i].blood--;
                    }
                    clearInterval(obj.timer); //子弹碰到了敌人就把定时器关掉
                    This.oParent.removeChild(obj); //子弹碰到了敌人就把子弹干掉
                }
            }
            if(!This.aLi.length) {
                //检测过了最后一关,就通关了
                if(This.gk.length == This.gs.gs) {
                    alert("恭喜你,你通关了!");
                    window.location.reload();
                }
                This.gs.gs++; 
                This.createEemey(This.gs.gs);
            }
        },30);
    },
    //碰撞检测
    pz : function(obj1,obj2) {
        var L1 = obj1.offsetLeft;
        var R1 = obj1.offsetLeft + obj1.offsetWidth;
        var T1 = obj1.offsetTop;
        var B1 = obj1.offsetTop + obj1.offsetHeight;

        //因为小蜜蜂li是装在ul里面的,所以计算小蜜蜂的top、left值是相对于ul的,所以要加上ul的top、left值才算小蜜蜂的值
        var L2 = obj2.offsetLeft + this.list.offsetLeft;
        var R2 = obj2.offsetLeft + obj2.offsetWidth + this.list.offsetLeft;
        var T2 = obj2.offsetTop + this.list.offsetTop;
        var B2 = obj2.offsetTop +obj2.offsetHeight + this.list.offsetTop;

        if(R1<L2 || L1 > R2 || T1 < T2 || T1 >  B2) {
            //这里是没有碰撞
            return false;
        } else {
            //碰撞了返回true
            return true;
        }
    }
}

最新源码点击这里源码链接

代码要是有看不懂的地方,在码云给我留言,我会第一时间回复你们。

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!