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

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

这里写目录标题

  • 一、完成情况
  • 二、简介
    • - 板子移动
    • - 碰撞模块
    • - 砖块的生成
  • 三、总结
  • 四、完整代码

一、完成情况
二、简介 这个项目主要是为了练习JavaScript中的一些操作方法而设定的练习,但本身用Chrome写这种程序就是在折磨cup,懂得都懂。当时运行久了(敲代码+开Chrome看完成情况),电脑都差点卡死。所以也不敢继续完成下去了。
大体上的思路其实并不复杂,大家在很多地方都可以看到类似的代码。整个游戏是通过div块完成的,中间一个大的div作为中心方框,下面一个div作为打砖块的板子,然后一个div将角消掉作为小球,最后在游戏开始前随机那么60个白色div作为砖块,就可以开始打了。
其中需要实现的基本功能有三个。一是这个板子的移动,他应该可以有两种方式实现控制,键盘和鼠标,在这里我选择了用鼠标点击拖动的方式实现这一功能。第二个就是碰撞,其中包括了板子和小球的碰撞和小球和砖块的碰撞两种。第三是砖块的生成和消除。
由于一开始我是想直接仿照打砖块的小游戏做的(类似下图这种有奖励的,然后满屏都是球的这种,图片截自某信小程序),所以,在这个项目中未能完成的功能有:1)奖励方框的生成和掉落 2)小球分裂 3)胜负手判断(这个只是单纯没做完2333)
前端|【前端】利用JavaScript做打砖块小游戏
文章图片

对于已完成的几个功能来讲:
- 板子移动 这里用到了一个简单的div移动的方法,由于用的是鼠标拖动的方法,所以这里只需要检测mouseup、mousedown、mousemove三个事件,这样一来,通过监测鼠标按下和鼠标抬起的事件来判断是否需要移动滑块,在读出鼠标移动的距离用于对板子移动距离进行计算。而对于板子的移动,则只需要在鼠标点下时计算其左上角的坐标,然后再通过鼠标移动时得到的移动数据,对板子的距左距离进行加减运算即可完成。
- 碰撞模块 这个模块相对来说就比较复杂了,倒不是代码有多难,而是由于在这个项目是在div块的基础上完成的,对于碰撞和反弹的判定可能会出现几个问题:
  1. 碰撞判定:碰撞判定其实还不算特别复杂,首先我们先来看看目标碰撞体和小球的示意图前端|【前端】利用JavaScript做打砖块小游戏
    文章图片
    从图中我们可以看到目标div和小球的div实际上都是一个方块,虽然我们用圆角的方式将小球做成了一个圆形,但其实际碰撞体积还是按方块来计算的,所以我们只需要按方块的体积来计算碰撞,就可以简单实现目标。首先我们得知道,在碰撞计算时,我们实际上是用的offsetLeftoffsetTop两种方法去获取div距离左侧和顶部的距离,然后利用数学的方式判断是否碰撞,大致上如下图所示前端|【前端】利用JavaScript做打砖块小游戏
    文章图片
    由于在本次设计中小球和所以我们单看关键点,就可以吧整个想象成一个二维坐标系和一堆坐标点,然后就会发现是这样的情况,当小球的关键点坐标落在绿色这个区域时,可以说小球与目标块之间产生了碰撞。这样,碰撞的问题就解决了
  2. 【前端|【前端】利用JavaScript做打砖块小游戏】反弹问题:解决了碰撞的问题之后,反弹的问题相对来说就简单一点了,由于砖块是正方形,那么我们就可以画出一下几个区域前端|【前端】利用JavaScript做打砖块小游戏
    文章图片
    其中当“坐标”落在紫色区域时,我们可以认为小球在砖块的上面或者下面,此时我们反转小球纵向速度的正负值,就可以完成反弹。同理,当小球落在黄色区域时,可以认为小球在砖块的左侧或右侧,此时我们反转小球横向速度的正负值,就可以完成反弹。
- 砖块的生成 这里可以有两种方法生成砖块的div,第一种是笨办法,也是简单的方法,直接在html中一个个写上去就好了,但是这样做那么程序就失去意义了,工作量太大。所以我们采用第二种方法,先用createElement依序创建出足够多的砖块,然后再将生成的div变成浮动的形式,最后固定div的“坐标”。这样我们就可以轻松完成对砖块的创建。
三、总结 对这个小练习来说,有以下几个问题:
  1. 最大的问题还是这样做游戏真不推荐,如果把它做完,相当于直接点开了个@echo off ; start cmd;0%;的bat文件,大概能把cpu都给烧了吧。
  2. 在砖块的碰撞端上还是有一定的问题,问题主要出在小球的速度上,由于程序中是判断小球关键点“坐标”与我们目标位置的关系,一旦进入目标位置,则相应速度方向反向。那么就会出现两个问题:一是板子与小球碰撞的问题,由于板子并不是正方形的块,没办法写对角线的判别式(由于是像素点判定,所以如果强行写判别式,可能出现被遗漏的像素,从而造成bug),所以板子的碰撞反弹只能反转纵向速度。二是如果小球随机到的速度过快,会出现在第一次检测时,“坐标”在判定区内,速度反向,但是第二次检测时,小球来不及飞出来,还在判定区内,于是在同一方向上继续反向,造成在边界上鬼畜的情况。如果说第一个问题还无伤大雅的话,第二个问题我们除了能控制小球速度之外,我想不出更好的办法去解决这个bug,可能是我才疏学浅了,如果有好的办法,请各位大神不吝赐教。
总的来说,虽然有点耗cpu,代码也有点小问题,但是能在没用框架的情况下亲手做出这样的游戏,还是蛮有成就感的,知足了。
四、完整代码 编译器:vs code
浏览器:Chrome
闲的蛋疼 - 锐客网 > //做一些简单的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; } // > 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(){}

    推荐阅读