【前端】利用JavaScript做打砖块小游戏

【写在前面:本篇文章介绍的是博主在学习前端的JavaScript时跟着教程,稍作改良的一个打砖块的小游戏,这个可能与大家见到的简易版打砖块不大一样,会稍微复杂一点点。写这篇文章有两个目的,一是作为学习记录,二是希望对大家有点帮助,对于不足之处,也希望各路大佬可以不吝赐教。本文为作者原创文章,文中所示的图片、代码皆来自网络或博主自制,仅做学习、记录使用,如果某些东西涉及侵权,请作者大大告知博主,可以对此进行补充说明。如有人私自引入商业使用构成侵权或违法犯罪,则博主概不负责。】

一、完成情况

在这里插入图片描述

二、简介

这个项目主要是为了练习JavaScript中的一些操作方法而设定的练习,但本身用Chrome写这种程序就是在折磨cup,懂得都懂。当时运行久了(敲代码+开Chrome看完成情况),电脑都差点卡死。所以也不敢继续完成下去了。

大体上的思路其实并不复杂,大家在很多地方都可以看到类似的代码。整个游戏是通过div块完成的,中间一个大的div作为中心方框,下面一个div作为打砖块的板子,然后一个div将角消掉作为小球,最后在游戏开始前随机那么60个白色div作为砖块,就可以开始打了。

其中需要实现的基本功能有三个。一是这个板子的移动,他应该可以有两种方式实现控制,键盘和鼠标,在这里我选择了用鼠标点击拖动的方式实现这一功能。第二个就是碰撞,其中包括了板子和小球的碰撞和小球和砖块的碰撞两种。第三是砖块的生成和消除。

由于一开始我是想直接仿照打砖块的小游戏做的(类似下图这种有奖励的,然后满屏都是球的这种,图片截自某信小程序),所以,在这个项目中未能完成的功能有:1)奖励方框的生成和掉落 2)小球分裂 3)胜负手判断(这个只是单纯没做完2333)
在这里插入图片描述

对于已完成的几个功能来讲:

- 板子移动

这里用到了一个简单的div移动的方法,由于用的是鼠标拖动的方法,所以这里只需要检测mouseup、mousedown、mousemove三个事件,这样一来,通过监测鼠标按下和鼠标抬起的事件来判断是否需要移动滑块,在读出鼠标移动的距离用于对板子移动距离进行计算。而对于板子的移动,则只需要在鼠标点下时计算其左上角的坐标,然后再通过鼠标移动时得到的移动数据,对板子的距左距离进行加减运算即可完成。

- 碰撞模块

这个模块相对来说就比较复杂了,倒不是代码有多难,而是由于在这个项目是在div块的基础上完成的,对于碰撞和反弹的判定可能会出现几个问题:

  1. 碰撞判定:碰撞判定其实还不算特别复杂,首先我们先来看看目标碰撞体和小球的示意图在这里插入图片描述从图中我们可以看到目标div和小球的div实际上都是一个方块,虽然我们用圆角的方式将小球做成了一个圆形,但其实际碰撞体积还是按方块来计算的,所以我们只需要按方块的体积来计算碰撞,就可以简单实现目标。首先我们得知道,在碰撞计算时,我们实际上是用的offsetLeftoffsetTop两种方法去获取div距离左侧和顶部的距离,然后利用数学的方式判断是否碰撞,大致上如下图所示在这里插入图片描述由于在本次设计中小球和所以我们单看关键点,就可以吧整个想象成一个二维坐标系和一堆坐标点,然后就会发现是这样的情况,当小球的关键点坐标落在绿色这个区域时,可以说小球与目标块之间产生了碰撞。这样,碰撞的问题就解决了

  2. 反弹问题:解决了碰撞的问题之后,反弹的问题相对来说就简单一点了,由于砖块是正方形,那么我们就可以画出一下几个区域在这里插入图片描述其中当“坐标”落在紫色区域时,我们可以认为小球在砖块的上面或者下面,此时我们反转小球纵向速度的正负值,就可以完成反弹。同理,当小球落在黄色区域时,可以认为小球在砖块的左侧或右侧,此时我们反转小球横向速度的正负值,就可以完成反弹。

- 砖块的生成

这里可以有两种方法生成砖块的div,第一种是笨办法,也是简单的方法,直接在html中一个个写上去就好了,但是这样做那么程序就失去意义了,工作量太大。所以我们采用第二种方法,先用createElement依序创建出足够多的砖块,然后再将生成的div变成浮动的形式,最后固定div的“坐标”。这样我们就可以轻松完成对砖块的创建。

三、总结

对这个小练习来说,有以下几个问题:

  1. 最大的问题还是这样做游戏真不推荐,如果把它做完,相当于直接点开了个@echo off ; start cmd;0%;的bat文件,大概能把cpu都给烧了吧。
  2. 在砖块的碰撞端上还是有一定的问题,问题主要出在小球的速度上,由于程序中是判断小球关键点“坐标”与我们目标位置的关系,一旦进入目标位置,则相应速度方向反向。那么就会出现两个问题:一是板子与小球碰撞的问题,由于板子并不是正方形的块,没办法写对角线的判别式(由于是像素点判定,所以如果强行写判别式,可能出现被遗漏的像素,从而造成bug),所以板子的碰撞反弹只能反转纵向速度。二是如果小球随机到的速度过快,会出现在第一次检测时,“坐标”在判定区内,速度反向,但是第二次检测时,小球来不及飞出来,还在判定区内,于是在同一方向上继续反向,造成在边界上鬼畜的情况。如果说第一个问题还无伤大雅的话,第二个问题我们除了能控制小球速度之外,我想不出更好的办法去解决这个bug,可能是我才疏学浅了,如果有好的办法,请各位大神不吝赐教。

总的来说,虽然有点耗cpu,代码也有点小问题,但是能在没用框架的情况下亲手做出这样的游戏,还是蛮有成就感的,知足了。

四、完整代码

编译器:vs code
浏览器:Chrome

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>闲的蛋疼</title>
        <style>
        	//做一些简单的css样式,美化网页
            #div1{width: 600px; height: 600px; border: 1px solid black; border-bottom: dashed 1px black; position: relative ; margin: 100px auto;}
            #ball1{width: 6px; height: 6px; background-color: red; border-radius: 50%; position:absolute; bottom: 30px; left: 300px;}
            #bat{width: 100px; height: 20px; background-color:grey; opacity: 1; position:absolute; bottom: -15px; left: 250px;}
            #brick div{width: 19px; height: 19px; border: 0.5px solid black ; float: left; }
            body{height: 100%; width: 100%; background-image: url('E:/vs code/源代码/html/img_2323.jpg'); background-attachment: fixed; position: relative;font-family: Arial; background-position: center 0;}
            #rewardBrick {position: absolute;}
            #rewardBrick div{width: 14px; height: 14px; background-color: orange; opacity: 1; position:absolute; left: 3px; top: 3px;}
        </style>
        
        //<script src="jquery-3.4.1.min.js"></script>
        <script>
            window.onload = function(){
                var oDiv = document.getElementById('div1');
                var oBall = document.getElementById('ball1');
                var oBat = document.getElementById('bat');
                var oBrick = document.getElementById('brick');
                var aBricks = oBrick.getElementsByTagName('div');
                var rBrick = document.getElementById('rewardBrick');
                var rBrick1 = rBrick.getElementsByTagName('div');
                var rBrick2 = document.getElementsByClassName('nb');

                dragX(oBat);
                creatBrick(360);

                //随机生成一个的速度
                var speedX = 1 + Math.random()*3;/* parseInt(Math.random()*3) +10; */ 
                var speedY = -1 - Math.random()*3;

                //触壁反弹函数
                setInterval(function(){
                    oBall.style.left = oBall.offsetLeft + speedX + 'px';
                    oBall.style.top = oBall.offsetTop + speedY + 'px';

                    if(oBall.offsetLeft >= 594 || oBall.offsetLeft <= 0){
                        speedX *= -1;
                    }

                    if(oBall.offsetTop >= 594 || oBall.offsetTop <= 0){
                        speedY *= -1;
                    }

                    //砖块反弹和板子反弹
                    if(knock(oBall,oBat)){
                        speedY *= -1;
                    }

                    for(var i = 0; i < aBricks.length; i++){
                        if(knock(oBall,aBricks[i])){
                            
                            if(knockPositionJudge_Brick(oBall,aBricks[i]) == 'surface'){
                                speedY *= -1;
                            }else if(knockPositionJudge_Brick(oBall,aBricks[i]) == 'side'){
                                speedX *= -1;
                            }
                        
                            var left2 = aBricks[i].offsetLeft;
                            var top2 = aBricks[i].offsetTop;
                            oBrick.removeChild(aBricks[i]);
                            // reward(aBricks[i],left2,top2);
                            return top2;
                            break;
                        }
                    }

                    //奖励模块移动
                    // var bricks_1 = $(".nb");
                    // bricks_1.style.backgroundColor = 'blue';
                    // if(rBrick1.length > 0){
                    //     document.getElementsByClassName('nb');
                    // }

                   
                },10);  //10ms检测一次

            }

            //拍子拖拽函数
            function dragX(node){
                node.onmousedown = function(ev){
                    var e = ev ||window.event;
                    var offsetX = e.clientX - node.offsetLeft;

                    document.onmousemove = function(ev){
                        var e = ev ||window.event;
                        var l = e.clientX - offsetX;
                        if(l <= 0){
                            l = 0;
                        }
                        if(l >= 500){
                            l = 500;
                        }
                        node.style.left = l +'px';
                    }
                }

                document.onmouseup = function(){
                    document.onmousemove = null;
                }
            }

            //创建砖块的函数
            function creatBrick(n){
                var oBrick = document.getElementById('brick');
                for(var i = 0; i < n; i++){
                    var node = document.createElement('div');
                    node.style.backgroundColor = 'rgba(255,255,255,0.7)';
                    oBrick.appendChild(node);
                }

                //文档流转换
                var aBricks = oBrick.getElementsByTagName('div');
                for(var i = 0; i < aBricks.length; i++){
                    aBricks[i].style.left = aBricks[i].offsetLeft +'px';
                    aBricks[i].style.top = aBricks[i].offsetTop +'px';
                }

                for(var i = 0; i < aBricks.length; i++){
                    aBricks[i].style.position = 'absolute';
                }
            }

            //碰撞处理的函数
            function knock(node1,node2){
                var l1 = node1.offsetLeft;
                var r1 = node1.offsetLeft + node1.offsetWidth;
                var t1 = node1.offsetTop;
                var b1 = node1.offsetTop + node1.offsetHeight;

                var l2 = node2.offsetLeft;
                var r2 = node2.offsetLeft + node2.offsetWidth;
                var t2 = node2.offsetTop ;
                var b2 = node2.offsetTop + node2.offsetHeight;

                if(t2 >= b1 || b2 <= t1 || l2 >= r1 || r2 <= l1){
                    return false;
                }else{
                    return true;
                }
            }

            function knockPositionJudge_Brick(node1,node2){
                var x1 = node1.offsetLeft - node2.offsetLeft;
                var y1 = node1.offsetTop - node2.offsetTop;
               
                if(y1 >= -6 && y1 - x1 <= 0 && x1 >= -6 && x1 <= 7|| y1 >= -6 && y1 + x1 <= 14 && x1 >= 7 && x1 <= 20|| y1 + x1 >= 14 && y1 <= 20 && x1 >= -6 && x1 <= 7 || y1 - x1 >= 0 && y1 <= 20 && x1 >= 7 && x1 <= 20 ){
                    return 'surface';
                }else {
                    return 'side';
                }
            }

            //随机掉落奖励箱子的创建
            // function reward(aBricks,leftPoint,topPoint){

            //     if(Math.random() <= 1){
            //     var rBrick = document.getElementById('rewardBrick');
            //     var newBrick = document.createElement('div');
                
            //     newBrick.className = 'nb';
            //     newBrick.style.left = leftPoint + 3 + 'px';
            //     newBrick.style.top = topPoint +3 + 'px';

            //     rBrick.appendChild(newBrick);
            //     newBrick.style.position = 'absulute';
            //     newBrick.style.display = 'inline-block';
                
            //     topPosition = topPoint + 3;
            //     return topPosition;

            //     }
            // }

            //小球分裂函数
            function divideBall(){

            }

            //小球消失函数
            function removeBall(){

            }

            //胜负手判断函数
            function result(){

            }

        </script>
    </head>
    <body>
        <div id="div1">
            <div id="ball1"></div>
            <div id="bat"></div>
            <div id="brick"></div>
            <div id="rewardBrick"></div>
        </div>
        
    </body>
</html>
  • 1
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值