游戏效果图所示:
打砖块小游戏实现的步骤:
1.搭建整体的三个盒子: 最外面的大盒子,游戏屏幕盒子,游戏信息盒子
2.右侧信息的初始化(搭建好结构,以及样式)
3.游戏屏幕盒子中 未加JavaScript效果时的初始样子(砖块,小球,挡板)
4.给砖块设置随机颜色以及砖块的位置
5.键盘上的左右键控制挡板的左右移动
6.小球的运动效果:小球遇到挡板、盒子边沿、砖块的反弹效果
7.小球的运动过程中游戏信息的实时显示。
一、首先搭建游戏初始样子(未加JavaScript的样子)
效果图:
在这一块不再过多的解释代码了,大家应该都能看懂(不明白的地方欢迎提问)
重点解释一下,为什么90个砖块在这个时候只显示了一个?
在这里为什么不使用float:left,直接将所有的砖块都显示出来,,而是使用绝对定位的方式只显示了一个砖块呢? 原因是这样的: 在小球与砖块碰撞 “监听” 的时候,需要判断小球与砖块的位置之间的关系,这个位置关系是通过小球和砖块距离游戏屏幕盒子左边框线的长度来判断的,这个时候要用到这个left值,而这个left值是通过绝对定位的left来表明的,所以用绝对定位。 如果使用float:left,砖块距离左边的距离计算起来就不是很方便了。
(上面这里仅仅是个人的理解,有什么更好的方法欢迎补充~~~~)
此时的页面代码如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>打砖块</title>
<style>
*{
margin: 0;
padding: 0;
}
li{
list-style: none;
}
#game{
width: 1300px;
height: 630px;
border: 2px solid rgb(184, 224, 109);
margin: 20px auto;
}
#box{
width: 899px;
height: 510px;
border: 2px solid rgb(200,0,0);
margin: 20px 0 0 75px;
float: left;
position: relative;
}
#box ul{
height: 188px;
position: relative;
}
#box ul li{
width: 89px;
height: 20px;
position: absolute;
left: 0;
top: 0;
background: blue;
}
#box #bubble{
width: 20px;
height: 20px;
border-radius: 50%;
background-color: blueviolet;
position: absolute;
top: 474px;
left: 40px;
}
#box #board{
width: 100px;
height: 15px;
background-color: rgb(199, 142, 120);
position: absolute;
bottom: 1px;
left: 0;
}
#show{
width: 270px;
height: 510px;
float: right;
background-color: rgb(27, 25, 25);
margin: 20px 20px 0 30px;
color: rgb(56, 218, 56);
}
#show span{
display: block;
padding: 15px 0 15px 7px;
}
#show #info{
font-size: 20px;
font-weight: bold;
border-bottom: 2px solid rgb(56,218,56);
margin-top: 10px;
padding-bottom: 25px;
}
#show #time,#res,#times,#score{
border-bottom: 1px dashed rgb(56,218,56);
}
button{
width: 100px;
height: 40px;
margin: 40px 0 0 500px;
}
</style>
</head>
<body>
<div id="game">
<div id="box">
<!-- 砖块 -->
<ul>
<!-- 一共有90个li(因为有90个砖块),在这里不再全部显示了,注意!!!!!! -->
<li></li>
</ul>
<!-- 小球 -->
<div id="bubble"></div>
<div id="board"></div>
<!-- 挡板 -->
</div>
<div id="show">
<span id="info">游戏重要信息</span>
<span>当前时间:</span>
<span id="time">加载中···</span>
<span>游戏状态</span>
<span id="res">加载中···</span>
<span>挡板打球的次数</span>
<span id="times">加载中···</span>
<span>游戏得分</span>
<span id="score">加载中···</span>
</div>
<button id="start">开始游戏</button>
</div>
</body>
</html>
二、给砖块设置随机颜色以及砖块的位置
效果图:
准备工作:
$()函数:在canvas中,使用$来代表document.getElementById(id)是默认这样使用的,所以在这里写了这样一个函数;
rand()函数:在这里要实现随机生成砖块的颜色,所以可以设置一个随机函数,用来生成(0-255)之间的随机数,
function $(id){
return document.getElementById(id);
}
// 随机函数
function rand(min,max){
return Math.floor(Math.random()*(max-min+1))+min;
}
1.构造一个打砖块的 “函数”,每一个函数都是一个对象(可以类比为c语言中的一个类,类是以函数的形式定义的),
在打砖块 的 构造函数中,获取到所要用到的元素节点 , 以及设置小球的横纵向速度和砖块的left和top初始值,fx,fy,leftInit,topInit这四个值的用处下面就可以感受到。
// 打砖块 构造函数
function blockBreak(){
// 这里的$指的是获取getElementById的$函数
box = $("box");//容器
list = document.getElementsByTagName("li");//砖块
board = $("board");//挡板
ball = $("bubble");//小球
fx = 2;//横向速度
fy = -2;//纵向速度
leftInit = 0;//砖块left初始值
topInit = 0;//砖块的top初始值
}
2.(解释性)每一个函数对象都有一个子对象prototype对象。prototype表示该函数的原型,也表示一个类的成员的集合。
3.首先对打砖块函数进行初始化:
在初始化中,就是 1.设置砖块的随机颜色; 2.设置砖块的 位置
设置砖块的随机颜色:主要就是随机生成背景颜色rgb中的三个0~255之间的数;
设置砖块的位置:因为在设置游戏最初的样子的时候,设置了砖块的绝对定位,所以这里砖块的位置主要是通过砖块距离游戏屏幕盒子box的左边框以及上边框的长度,而这个长度可以直接通过left和top的偏移量来实现。
x轴的偏移量,一排中(几-1)个砖块 * 一个砖块的宽度
y轴的偏移量,topInit的值
通过计算,一排有10个砖块,leftInit初始值为0,i+1代表第一块转 判断(i+1)%10是否为0 ,为0 的时候,leftInit的值归0,topInit的值在上一行高度值的基础上加上砖块的高度,(多加一个1 是因为,设置一个1像素的边距)
4.在构造好打砖块的函数以及写好初始化函数后,通过new方法实例化一个对象, 然后进行初始化,设置好砖块的颜色和位置
// 初始化功能,摆放每一个砖块的位置
blockBreak.prototype.init = function(){
for(var i = 0 ; i < list.length ; i ++){
// 给每个砖块设置随机背景颜色
list[i].style.backgroundColor = "rgb("+rand(0,255)+","+rand(0,255)+","+rand(0,255)+")";
// 砖块位置
// 每一个砖块的 x值 = 一排中(几-1)个砖块 * 一个砖块的宽度
var x = leftInit * list[0].offsetWidth +i%10;
// leftInit值做一个加一操作
leftInit++;
var y =topInit;
list[i].style.left = x + "px";
list[i].style.top = y + "px";
// console.log(list[0].offsetHeight)
// 砖块的换行
if((i+1)%10 == 0){
// 换行之后,砖块距离左侧的值变为0 ,高度加上一个砖块的高度
leftInit = 0;
topInit += list[0].offsetHeight + 1;
}
}
}
三、键盘上的左右键控制挡板的左右移动
1.判断按下的是左键还是右键
2.是左键(键码值为37):则挡板往左移动40个像素,也就是挡板的左偏移减少40个像素; 对挡板是否移动到最左边进行判断, offsetLeft的含义是:选中的对象 的左侧 离它的左方 或者 父元素节点的距离,当这个距离为、小于等于0 的时候,设置left值为0 ,避免挡板从游戏屏幕盒子中出去。
3.是右键(键码值为39):则单板往右移动40个像素,也就是挡板的左偏移增加40个像素; 对挡板是否移动到最右边进行判断:(代码注释中有解释。)
// 键盘控制挡板运动
blockBreak.prototype.keydown = function(){
document.onkeydown = function(e){
var e = e || event;
// 如果按的是键盘左键
if( e.keyCode == 37){
// 则挡板往左移动15个像素,也就是距离左侧的距离减少15个像素
board.style.left = board.offsetLeft - 40 + "px";
// 当挡板距离上一层也就是游戏框的距离为0 时,挡板距离左侧的距离设置为0
if(board.offsetLeft <= 0){
board.style.left = 0;
}
}
// 如果按的是键盘右键
if( e.keyCode == 39){
// 则挡板往右侧移动15个像素,也就是距离左侧的距离加15个像素
board.style.left = board.offsetLeft + 40 + "px";
// 如果挡板距离左侧的距离 >= 上层盒子的宽度(盒子宽度有2个像素的边框) - 挡板的宽度的时候
if(board.offsetLeft >= (box.offsetWidth -4 - board.offsetWidth)){
board.style.left = box.offsetWidth - board.offsetWidth - 4 + "px";
}
}
}
}
四、小球的运动
变量定义:
小球的运动一定会用到定时器,timer
并且在小球运动的过程中要计算小球碰撞挡板以及小球打掉砖块得到的分数 times score
小球运动的函数:
小球运动过程中使用定时器,每过0.01秒,小球距离左侧的距离增加2个像素(fx),距离上侧的距离减少2个像素(fy),(这就类似于小球在运动)
小球运动的反弹:
1.遇到游戏屏幕盒子box的四个方向的反弹
2. 小球和挡板的碰撞反弹
3.小球和砖块的碰撞反弹
// 小球碰到挡板的次数
var times = 0;
// 小球打掉砖块得到的分数
var score = 0;
// 小球运动
// 小球的运动一定会用到定时器
blockBreak.prototype.move = function(){
var timer = null;
timer = setInterval(function(){
// 每0.01秒。小球距离左侧的距离增加一个像素,具体上侧的距离减少一个像素,这就类似于小球在运动
ball.style.left = ball.offsetLeft + fx + "px";
ball.style.top = ball.offsetTop + fy + "px"
// 小球四个方向的反弹
//上
//右
//下
//左
// 小球和挡板的碰撞反弹
// 小球和砖块的 碰撞反弹
},10)
4.1 小球四个方向的反弹:
小球四个方向的反弹分为上右下左四个方向,是用来防止小球小球飞出游戏屏幕盒子box的
其中 当小球碰到下边框的时候,游戏结束,
1.加一个文本节点用来显示游戏结束文字
2.清除定时器
3.res块的文字内容改为游戏结束
// 小球四个方向的反弹
// 上, 当小球距离上侧的距离小于0时(小球飞出边界时,设置小球每0.01秒,距离上侧的距离增加一个相素,也就是将原来的fy由-2改为2,由于每次都要反向,所以直接*-1即可)
if(ball.offsetTop <= 0){
fy *= -1;
}
// 右
if(ball.offsetLeft >= (box.offsetWidth - ball.offsetWidth)){
fx *= -1;
}
// 下
if(ball.offsetTop >= (box.offsetHeight - ball.offsetHeight - 5)){
// 游戏结束了
box.appendChild(document.createTextNode("GameOver!"));
clearInterval(timer);
$("res").innerHTML = "游戏结束";
}
// 左
if(ball.offsetLeft <= 0 ){
fx *= -1;
}
4.2 小球和挡板的碰撞反弹:
有三个判断条件:
1.小球的高度+小球距离上侧的高度 >= 挡板的高度
2.小球的宽度 +小球距离左侧的距离 >= 挡板距离左侧的距离
3.小球距离左侧的距离 < 挡板距离左侧的距离 + 挡板的宽度
当满足这三个条件的时候,小球的纵向运动方向改变,计算挡板打球的次数,并显示
// 小球和挡板的碰撞反弹
// 1.小球的高度+小球距离上侧的高度 >= 挡板的高度时
if(ball.offsetHeight + ball.offsetTop >= board.offsetTop){
// 2.小球的宽度 +小球距离左侧的距离 >= 挡板距离左侧的距离
// 3.小球距离左侧的距离 < 挡板距离左侧的距离 + 挡板的宽度
if(ball.offsetWidth + ball.offsetLeft >= board.offsetLeft ){
if(ball.offsetLeft < board.offsetLeft + board.offsetWidth){
fy *= -1;
times ++;
$("times").innerHTML = "挡板打球" + times +"次"
}
}
}
4.3 小球和砖块的碰撞反弹:
思想:以一个小球为基准,遍历所有砖块,找到满足条件的那个砖块,隐藏就好了
三个判断条件:
1.小球距离上侧的距离 <= 这个砖块距离上侧的距离 + 这个砖块的高度
2.小球距离左侧的距离 >= 这个砖块的距离左侧的距离
3.小球距离左侧的距离 <= 这个砖块距离左侧的距离 + 这个砖块的宽度
当满足这第三个条件的时候,小球的纵向运行改变方向,并且隐藏掉“打中”的砖块,计算得分并显示。
// 小球和砖块的 碰撞反弹
// 以一个小球为基准,遍历所有砖块,找到满足条件的那个砖块,隐藏就好了
for(var i = 0 ; i < list.length ; i++){
// 小球距离上侧的距离 <= 这个砖块距离上侧的距离 + 这个砖块的高度
// 2.小球距离左侧的距离 >= 这个砖块的距离左侧的距离
// 3.小球距离左侧的距离 <= 这个砖块距离左侧的距离 + 这个砖块的宽度
if(ball.offsetTop <= list[i].offsetTop +list[i].offsetHeight){
if(ball.offsetLeft >=list[i].offsetLeft ){
console.log(list[i].offsetLeft);
if(ball.offsetLeft <=list[i].offsetLeft + list[i].offsetWidth){
fy *= -1;
list[i].style.display = "none";
score ++;
$("score").innerHTML = "得分" + score;
}
}
}
}
五、开始游戏按钮:
点击开始游戏按钮开始游戏
点击之后,挡板可以移动,小球可以运动,并显示当前系统时间。
$("start").onclick = function(){
bb.keydown();
bb.move();
// 右侧信息实现系统时间的显示
setInterval(function(){
var now = new Date();
$("time").innerHTML = now.getFullYear() + "/" + (now.getMonth() + 1) + "/" + now.getDate();
},10)
$('res').innerHTML = "游戏加载完成了"
}
完整代码如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>打砖块</title>
<style>
*{
margin: 0;
padding: 0;
}
li{
list-style: none;
}
#game{
width: 1300px;
height: 630px;
border: 2px solid rgb(184, 224, 109);
margin: 20px auto;
}
#show{
width: 270px;
height: 510px;
float: right;
background-color: rgb(27, 25, 25);
margin: 20px 20px 0 30px;
color: rgb(56, 218, 56);
}
#show span{
display: block;
padding: 15px 0 15px 7px;
}
#show #info{
font-size: 20px;
font-weight: bold;
border-bottom: 2px solid rgb(56,218,56);
margin-top: 10px;
padding-bottom: 25px;
}
#show #time,#res,#times,#score{
border-bottom: 1px dashed rgb(56,218,56);
}
#box{
display: block;
width: 899px;
height: 510px;
border: 2px solid rgb(200,0,0);
margin: 20px 0 0 75px;
position: relative;
}
#box ul{
height: 188px;
position: relative;
}
#box ul li{
width: 89px;
height: 20px;
position: absolute;
left: 0;
top: 0;
background: blue;
}
#box #bubble{
width: 20px;
height: 20px;
border-radius: 50%;
background-color: blueviolet;
/* box-shadow: black ; */
position: absolute;
top: 474px;
left: 40px;
}
#box #board{
width: 100px;
height: 15px;
background-color: rgb(199, 142, 120);
position: absolute;
bottom: 1px;
left: 0;
}
button{
width: 100px;
height: 40px;
margin: 40px 0 0 500px;
}
</style>
</head>
<body>
<div id="game">
<!-- 游戏面板 -->
<div id="show">
<span id="info">游戏重要信息</span>
<span>当前时间:</span>
<span id="time">加载中···</span>
<span>游戏状态</span>
<span id="res">加载中···</span>
<span>挡板打球的次数</span>
<span id="times">加载中···</span>
<span>游戏得分</span>
<span id="score">加载中···</span>
</div>
<!-- 游戏区域 -->
<div id="box">
<!-- 挡板、小球、砖块 -->
<ul>
<li></li> 一共90个li <li></li>
</ul>
<div id="bubble"></div>
<div id="board"></div>
</div>
<button id="start">开始游戏</button>
</div>
</body>
<script>
function $(id){
return document.getElementById(id);
}
// 随机函数
function rand(min,max){
return Math.floor(Math.random()*(max-min+1))+min;
}
// 打砖块 构造函数
function blockBreak(){
// 这里的$指的是获取getElementById的$函数
box = $("box");//容器
list = document.getElementsByTagName("li");//砖块
board = $("board");//挡板
ball = $("bubble");//小球
fx = 2;//横向速度
fy = -2;//纵向速度
leftInit = 0;//砖块left初始值
topInit = 0;//砖块的top初始值
}
// 初始化功能,摆放每一个砖块的位置
blockBreak.prototype.init = function(){
for(var i = 0 ; i < list.length ; i ++){
// 给每个砖块设置随机背景颜色
list[i].style.backgroundColor = "rgb("+rand(0,255)+","+rand(0,255)+","+rand(0,255)+")";
// 砖块位置
// 每一个砖块的 x值 = 一排中(几-1)个砖块 * 一个砖块的宽度
var x = leftInit * list[0].offsetWidth +i%10;
// leftInit值做一个加一操作
leftInit++;
var y =topInit;
list[i].style.left = x + "px";
list[i].style.top = y + "px";
// console.log(list[0].offsetHeight)
// 砖块的换行
if((i+1)%10 == 0){
// 换行之后,砖块距离左侧的值变为0 ,高度加上一个砖块的高度
leftInit = 0;
topInit += list[0].offsetHeight + 1;
}
}
}
// 键盘控制挡板运动
blockBreak.prototype.keydown = function(){
document.onkeydown = function(e){
var e = e || event;
// 如果按的是键盘左键
if( e.keyCode == 37){
// 则挡板往左移动15个像素,也就是距离左侧的距离减少15个像素
board.style.left = board.offsetLeft - 40 + "px";
// 当挡板距离上一层也就是游戏框的距离为0 时,挡板距离左侧的距离设置为0
if(board.offsetLeft <= 0){
board.style.left = 0;
}
}
// 如果按的是键盘右键
if( e.keyCode == 39){
// 则挡板往右侧移动15个像素,也就是距离左侧的距离加15个像素
board.style.left = board.offsetLeft + 40 + "px";
// 如果挡板距离左侧的距离 >= 上层盒子的宽度(盒子宽度有2个像素的边框) - 挡板的宽度的时候
if(board.offsetLeft >= (box.offsetWidth -4 - board.offsetWidth)){
board.style.left = box.offsetWidth - board.offsetWidth - 4 + "px";
}
}
}
}
// 小球碰到挡板的次数
var times = 0;
// 小球打掉砖块得到的分数
var score = 0;
// 小球运动
// 小球的运动一定会用到定时器
blockBreak.prototype.move = function(){
var timer = null;
timer = setInterval(function(){
// 每0.01秒。小球距离左侧的距离增加一个像素,具体上侧的距离减少一个像素,这就类似于小球在运动
ball.style.left = ball.offsetLeft + fx + "px";
ball.style.top = ball.offsetTop + fy + "px"
// 小球四个方向的反弹
// 上, 当小球距离上侧的距离小于0时(小球飞出边界时,设置小球每0.01秒,距离上侧的距离增加一个相素,也就是将原来的fy由-2改为2,由于每次都要反向,所以直接*-1即可)
if(ball.offsetTop <= 0){
fy *= -1;
}
// 右
if(ball.offsetLeft >= (box.offsetWidth - ball.offsetWidth)){
fx *= -1;
}
// 下
if(ball.offsetTop >= (box.offsetHeight - ball.offsetHeight - 5)){
// 游戏结束了
box.appendChild(document.createTextNode("GameOver!"));
clearInterval(timer);
$("res").innerHTML = "游戏结束";
}
// 左
if(ball.offsetLeft <= 0 ){
fx *= -1;
}
// 小球和挡板的碰撞反弹
// 1.小球的高度+小球距离上侧的高度 >= 挡板的高度时
if(ball.offsetHeight + ball.offsetTop >= board.offsetTop){
// 2.小球的宽度 +小球距离左侧的距离 >= 挡板距离左侧的距离
// 3.小球距离左侧的距离 < 挡板距离左侧的距离 + 挡板的宽度
if(ball.offsetWidth + ball.offsetLeft >= board.offsetLeft ){
if(ball.offsetLeft < board.offsetLeft + board.offsetWidth){
fy *= -1;
times ++;
$("times").innerHTML = "挡板打球" + times +"次"
}
}
}
// 小球和砖块的 碰撞反弹
// 以一个小球为基准,遍历所有砖块,找到满足条件的那个砖块,隐藏就好了
for(var i = 0 ; i < list.length ; i++){
// 小球距离上侧的距离 <= 这个砖块距离上侧的距离 + 这个砖块的高度
// 2.小球距离左侧的距离 >= 这个砖块的距离左侧的距离
// 3.小球距离左侧的距离 <= 这个砖块距离左侧的距离 + 这个砖块的宽度
if(ball.offsetTop <= list[i].offsetTop +list[i].offsetHeight){
if(ball.offsetLeft >=list[i].offsetLeft ){
console.log(list[i].offsetLeft);
if(ball.offsetLeft <=list[i].offsetLeft + list[i].offsetWidth){
fy *= -1;
list[i].style.display = "none";
score ++;
$("score").innerHTML = "得分" + score;
}
}
}
}
},10)
}
var bb = new blockBreak();
bb.init();
$("start").onclick = function(){
bb.keydown();
bb.move();
// 右侧信息实现系统时间的显示
setInterval(function(){
var now = new Date();
$("time").innerHTML = now.getFullYear() + "/" + (now.getMonth() + 1) + "/" + now.getDate();
},10)
$('res').innerHTML = "游戏加载完成了"
}
</script>
</html>