7.制作开始与结束面板🌻
8.得分统计🌺
我们先来看看接下来我们要做的效果:🙋🙋🙋
有需要源码和素材的同学,在文章末尾有链接。
1.适配设备💨
PC端下背景320px*568px(游戏背景图片大小),移动端下占满窗口
新建一个public.js文件,这个文件放一些我们公共的方法,下面我们先定义一个isPhone方法来判断是否是移动端设备
function isPhone() {
var arr = [“iPhone”,“iPad”,“Android”]
var is = false;
for(var i = 0;i<arr.length;i++) {
if(navigator.userAgent.indexOf(arr[i])!=-1) {
is = true;
}
}
return is;
}
在isPhone方法里我们定义了一个数组arr用来存储移动端的设备名,UserAgent是HTTP请求中的用户标识,一般发送一个能够代表客户端类型的字符串,indexOf() 方法可返回某个指定的字符串值在字符串中首次出现的位置,如果要检索的字符串值没有出现,则该方法返回 -1
我们默认是PC端,如果indexOf不返回-1说明与数组中的元素匹配,代表是移动端设备,那么我们的isPhone方法就返回true。
这个判断移动端的方法大家可以保存下来,以后很多的项目我们也用的到
因为我们规定移动端下背景图片要占满屏幕,所以需要一个if语句进行判断,如果isPhone返回的是true,说明当前在移动端,我们需要修改背景图片的宽高:
sw和sh是在在外面定义的全局变量,默认情况下sw=320,sh=568,因为在后面我们还会用到sw,sh,所以如果设备是移动端的话,需要对它们进行重新赋值。
if (isPhone()) {
var bg = document.querySelector(‘.contain’);
sw = document.documentElement.clientWidth + ‘px’;
sh = document.documentElement.clientHeight + ‘px’;
bg.style.width = sw;
bg.style.height = sh;
}
document.documentElement.clientWidth 就是当前设备的屏幕宽度,注意加符号
我们可以在chrome浏览器下模拟移动端不同设备下是否占满全屏,每次换完设备时要刷新页面:
这样的话我们适配设备的效果就完成了,成功做到了可以在移动端下占满全屏,下面就开始制作我们的flappybird游戏吧!
2.背景滚动💨
在下面的代码中bg是我们获取的最外层装背景图片的盒子,背景图片是在x轴平铺的,所以我们只需要一个定时器不断调用背景移动函数就行,背景移动函数里我们每次调用背景的位置就向左移动5像素
var timer = setInterval(function(){
bgMove();
},30)
function bgMove() {
var bg = document.querySelector(‘.contain’);
bgDis -= 5;
bg.style.backgroundPosition = ${bgDis}px 0
;
}
在我们制作的这个游戏中,不论是背景移动还是待会要做的管道的移动,小鸟的移动,最后封装的函数都需要在这个定时器里调用,这样才会有我们看到的那种动画一样的效果。
3.管道的创建与移动💨
在实现管道的移动之前我们先需要创建管道,因为我们要让生成的管道高度不一致,所以需要先写一个随机数函数,我们就在public.js里完成:
function rand(min, max) {
return Math.round(Math.random() * (max-min) + min);
}
我们先整理一下创建管道的思路:
-
先写管道的样式,html与css部分的代码在文章最后都有,大家在看解析的时候要先看一眼html结构和css样式。
-
规定上下管道间隔120px,通过定义好的rand随机函数实现上管道高度随机,背景图片高度减去间隔减去上管道高度就是下管道高度,这里下管道高度不要给随机。
-
通过insertAdjacentHTML将生成管道的代码添加到ul里
因为管道也是不断生成的,我们需要在timer定时器里调用管道移动函数pipeMove():
var timer = setInterval(function(){
bgMove();
pipeMove();
},30)
我们在外面定义这个pipeMove方法,在pipeMove里完成管道的创建与移动
function pipeMove() {
//1.创建管道
createPipe();
//2.管道移动
}
下面先来根据在开头写的思路来完善管道创建函数createPipe:
function createPipe() {
var pipeheight = rand(100,300);
var ul = document.querySelector(‘ul’);
var str = <li class="top" style="height:${pipeheight+'px'};left:${sw+'px'}"><div></div></li><li class="bottom" style="height:${sh-pipeheight-120+'px'};left:${sw+'px'}"><div></div></li>
;
ul.insertAdjacentHTML(‘beforeend’,str);
}
运行代码看一看管道有没有被创建出来:
很明显管道数量太多了啊,因为定时器每隔三十毫秒就会调用管道创建函数,所以管道生成的就非常多,我们定义一些全局变量进行限制:
var space = 100; //创建管道的间隔
var count = 0; //管道的计数
修改一下createPipe函数,当计数达到创建管道的间隔100时才执行下面创建管道的代码,否则不执行,这样就对生成管道的数量进行了限制
function createPipe() {
count ++;
if (count != space) {
return ;
}
count = 0;
var pipeheight = rand(100,300);
var ul = document.querySelector(‘ul’);
var str = <li class="top" able="0" style="height:${pipeheight+'px'};left:${sw+'px'}"><div></div></li><li class="bottom" style="height:${sh-pipeheight-120+'px'};left:${sw+'px'}"><div></div></li>
;
ul.insertAdjacentHTML(‘beforeend’,str);
}
现在管道可以在背景的右面不断的生成,这样管道的创建就全部完成了,下面在pipeMove方法里继续完善管道移动的部分:
function pipeMove() {
//1.创建管道
createPipe();
//2.管道移动
var li = document.querySelectorAll(‘li’);
li.forEach(function(value,index,arr){
arr[index].style.left = arr[index].offsetLeft-2+‘px’;
})
}
我们先获取创建的所有管道,然后通过foreach循环每次调用都让管道左移两像素,管道就能成功移动起来了。
注意:直接通过obj.style.left和obj.style.top可以获取位置,但是有局限性,这种获取的方法只能获取到行内样式的left和top的属性值,不能获取到style标签和link 外部引用的left和top属性值。所以这里用offsetleft获取
然后我们再给管道加一个边界,让他超出背景时就在ul里删除这个元素:
li.forEach(function(value,index,arr){
arr[index].style.left = arr[index].offsetLeft-5+‘px’;
if(arr[index].offsetLeft<=-62) {
ul.removeChild(arr[index]);
}
})
我们运行代码看一看效果:
这样管道的创建与运动就基本上完成了,下面我们开始小鸟的操作。
4.小鸟操作💨
首先我们先把小鸟的html结构和css样式搭建好,下一步就是让小鸟“动起来”。
同样我们需要在定时器中调用小鸟移动函数birdMov
var timer = setInterval(function(){
bgMove();
pipeMove();
birdMove();
},30)
因为游戏开始的时候小鸟要向下掉落所以先写一个小鸟的移动函数:
function birdMove() {
var bird = document.querySelector(‘#bird’);
bird.style.top = bird.offsetTop +5 + ‘px’;
}
这样的话,小鸟就实现了一直匀速下落的效果,但是游戏中我们的小鸟并不是匀速的所以我们还需要定义一个全局变量speed,初始化它的值为0,来控制小鸟的速度。
因为我们在游戏中单击屏幕时小鸟会向上飞而且向上飞的速度和向下的速度不相同,所以我们在全局声明一个isDown变量,来判断小鸟是否向下飞。默认isDown = true,因为小鸟不操作的话一定是向下飞的。
function birdMove() {
if(isDown) {
speed += 0.4;
speed = speed > 8 ? 8 : speed;
}
else{
speed += 0.7;
}
var bird = document.querySelector(‘.bird’);
bird.style.top = bird.offsetTop +speed + ‘px’;
}
如果不点击屏幕每隔三十毫秒小鸟的速度就增加0.4,然后用一个三元表达式,如果速度达到8那么就是小鸟的极限速度就不再增加了,最后我们把原来的5这个固定值换成speed就实现了小鸟速度的动态变化
接下来我们要实现的是在单击背景的任一处时小鸟能够向上移动,所以我们需要给背景图片一个点击事件:
var contain = document.querySelector(‘.contain’);
contain.addEventListener(‘click’,function() {
isDown = false;
speed = -8;
})
当我们点击屏幕时,小鸟要向上飞,所以isDown被赋为false,然后立刻给一个向上的位移距离为8
我们运行一下代码看看效果:
是不是已经有那么点感觉了,但是小鸟点击屏幕的时候头会向上抬,所以还得在点击屏幕的时候改小鸟的背景图片
我们创建两个类,一个birddown类里面是小鸟头向下的图片,一个birdup类里面是小鸟头向上的。
.birddown {
background: url(./img/down_bird0.png);
}
.bird_up {
background: url(./img/up_bird0.png);
}
然后给bird添加默认样式类birddown,这样当我们点击屏幕时,就修改bird的类为birdup:
contain.addEventListener(‘click’,function() {
isDown = false;
speed = -8;
var bird = document.querySelector(‘#bird’);
bird.className = ‘birdup’;
})
这样的话我们在点击屏幕的时候小鸟就从头向下变成头向上了,但是如果不点击屏幕的时候小鸟还是应该回到默认向下的样式,因为不点击屏幕小鸟就会向下飞,那我们想想这个怎么实现呢?
那小鸟什么时候向下飞呢,就是speed为0的时候,我们每次点击屏幕的时候小鸟的速度都是-8,但是我们一直在调用birdmove,每次speed都加0.7,这样向上的速度总会越来越小然后当大于0 的时候小鸟就向下飞。
这样我们就在birdmove里补全代码实现点击屏幕小鸟就抬头向上飞,下降就低头向下飞:
function birdMove() {
var bird = document.querySelector(‘#bird’);
if(isDown) {
speed += 0.4;
speed = speed > 8 ? 8 : speed;
}
else{
speed += 0.7;
if(speed>=0) {
speed = 0;
isDown = true;
bird.className = ‘birddown’;
}
}
var bird = document.querySelector(‘#bird’);
bird.style.top = bird.offsetTop +speed + ‘px’;
}
我们看一下效果:
这样我们小鸟的动作就基本写完了,现在需要当小鸟触顶和触底的时候应该让游戏gameover。
把下面判断边界的代码放在birdmove里,这样每次先判断一下是否超出边界,如果超出的话就直接gameover并清除定时器然后执行again函数重新开始游戏。
if(bird.offsetTop<0||bird.offsetTop>sh-30) {
alert(‘gameover’);
clearInterval(timer);
again();
return;
}
再外面创建这个again函数来实现重新开始游戏:
function again() {
bgDis = 0;//bg的移动距离
count = 0; //管道的计数
isDown = true;//判断是否向下飞
speed = 0;//控制小鸟的速度
var ul = document.querySelector(‘ul’);
ul.innerHTML = ‘’;//清空管道
var bird = document.querySelector(‘#bird’);
bird.style.top = 100+‘px’;
start()
}
我们在again函数里要重新初始化一些变量,这里有些变量如管道间隔或者背景宽高是不需要再次初始化的。并且不用带var,因为如果带var了就是局部变量,但这里我们要改变的是全局变量。
然后我们需要清空所有画面中的管道,也就是ul里的内容。然后把小鸟的高度恢复到最开始的距离顶端20像素的位置。
最后调用了一个start函数,这个start函数就是把最开始的计时器给封装了:
function start() {
timer = setInterval(function(){
bgMove();
pipeMove();
birdMove();
},30)
}
因为在游戏结束的时候我们清空了计时器,所以重新开始的时候我们得再次调用这个计时器。
注意:计时器变量timer不应该加var,因为封装后加了var的在函数里就不是全局变量了
这样当我们的小鸟在触顶或者触底的时候就会弹出gameover对话框点击确定然后就重新开始游戏
然后我们把原来写的birddown类和birdup类修改为动画
@keyframes birddown {
from {
background-image: url(img/down_bird0.png);
}
to {
background-image: url(img/down_bird1.png);
}
}
@keyframes birdup {
from {
background-image: url(img/up_bird0.png);
}
to {
background-image: url(img/up_bird1.png);
}
}
从bird0到bird1就是小鸟的翅膀有个变化,这样加上动画后小鸟就像在飞动翅膀。
.birddown {
animation: birddown 0.05s linear infinite;
}
.birdup {
animation: birdup 0.05s linear infinite;
}
5.碰撞检测💨
如何判断触顶或者触底对我们来说并不难,但是如何判断小鸟和管道相撞呢?
下面我们回到public.js文件里写一下这个碰撞检测函数isCrash,这个函数同样是复用性很高的。
function isCrash(a,b) {
var l1 = a.offsetLeft;
var t1 = a.offsetTop;
var r1 = l1 + a.offsetWidth;
var b1 = t1 + a.offsetHeight;
var l2 = b.offsetLeft;
var t2 = b.offsetTop;
var r2 = l2 + b.offsetWidth;
var b2 = t2 + b.offsetHeight;
if (r2<l1 || b2<t1 || r1<l2 || b1<t2) {
// 不碰撞
return false;
} else {
最后
除了简历做到位,面试题也必不可少,整理了些题目,前面有117道汇总的面试到的题目,后面包括了HTML、CSS、JS、ES6、vue、微信小程序、项目类问题、笔试编程类题等专题。