学习class时,老师讲了个flappy bird的小游戏,自己试着写了一下。
天空和土地的移动以及小鸟的飞翔使用animation实现。 用class分别创建MoveReact(移动的长方体,小鸟和水管组的父类),Bird(小鸟),Pipe(水管),PipeGroup(水管组,上下为一组),PipeGroupFactory(水管组工厂类,生成和移除水管组), Game(控制小鸟、水管组的移动,并且检测碰撞,判断游戏是否结束)
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1.0,user-scalable=no,maximum-scale=1">
<style>
html,
body {
height: 100%;
margin: 0;
}
table {
width: 100%;
height: 100%;
}
.main {
width: 800px;
height: 610px;
margin: auto;
}
.game {
position: relative;
height: 100%;
overflow: hidden;
}
.sky {
width: 200%;
height: 500px;
background-image: url(./img/sky.png);
position: relative;
overflow: hidden;
}
.land {
width: 200%;
height: 110px;
background-image: url(./img//land.png);
bottom: 0;
}
.bird {
position: absolute;
width: 52px;
height: 35px;
background-position: 0 0;
background: url(./img/bird.png) no-repeat;
top: 20px;
left: 20px;
}
.bird.close {
background-position: -156px 0;
}
.move .bird {
animation: fly .5s steps(3, end) infinite;
}
.move .sky {
animation: skymove 7s linear infinite;
}
.move .land {
animation: skymove 7s linear infinite;
}
.pipe-group {
position: absolute;
height: 100%;
top: 0;
left: 100%;
}
.pipe-group .pipe {
width: 100%;
position: absolute;
overflow: hidden;
}
.pipe-group .pipe.up {
top: 0;
background-image: url(./img/pipeDown.png);
background-position: bottom;
}
.pipe-group .pipe.down {
bottom: 0;
background-image: url(./img/pipeUp.png);
}
@keyframes fly {
from {
background-position: 0 0;
}
to {
background-position: -156px 0;
}
}
@keyframes skymove {
from {
background-position: 0 0;
}
to {
background-position: -100% 0
}
}
</style>
</head>
<body>
<table>
<tr>
<td>
<div class="main">
<div class="game move">
<div class="sky"></div>
<div class="land"></div>
<div class="bird"></div>
</div>
</div>
</td>
</tr>
</table>
<script>
class MoveReact {
constructor(dom, left, top, speedX, speedY, maxTop) {
this.dom = dom;
this.left = left;
this.top = top;
this.speedX = speedX;
this.speedY = speedY;
this.maxTop = maxTop;
}
render() {
this.dom.style.top = this.top + 'px'
this.dom.style.left = this.left + 'px';
}
move(duration) {
this.left += this.speedX * duration;
this.top = Math.min(this.top + this.speedY * duration, this.maxTop)
this.render();
}
resetSpeed({
speedX,
speedY
}) {
speedX && (this.speedX = speedX);
speedY && (this.speedY = speedY);
}
}
class Bird extends MoveReact {
constructor(dom, width, height, left, top, speedX, speedY, maxTop) {
super(dom, left, top, speedX, speedY, maxTop)
this.width = width;
this.height = height;
this.g = 0.09;
}
move(duration) {
super.move(duration);
this.speedY += this.g * duration;
}
jump(){
this.speedY = -3
}
}
class Pipe {
constructor(height, type) {
let div = document.createElement('div')
div.className = Pipe.className + type;
div.style.height = height + 'px'
this.type = type;
this.pipedom = div;
}
static className = 'pipe '
}
class PipeGroup extends MoveReact {
constructor(dom, height, left, speedX, gapHeight, pipeWidth) {
super(dom, left, 0, speedX, 0, 0)
this.height = height;
this.maxPipeHeight = 410;
this.gapHeight = gapHeight;
let upHeight = parseInt(Math.random() * this.maxPipeHeight);
this.upHeight = upHeight;
this.downHeight = height - upHeight - gapHeight;
this.upPipe = new Pipe(upHeight, 'up');
this.downPipe = new Pipe(this.downHeight, 'down');
this.width = pipeWidth;
dom.className = PipeGroup.className;
dom.style.width = pipeWidth + 'px';
dom.appendChild(this.downPipe.pipedom)
dom.appendChild(this.upPipe.pipedom)
}
static className = 'pipe-group '
}
class PipeGroupFactory {
constructor(dom, width, height) {
this.group = [];
this.groupHeight = height;
this.gapHeight = 140;
this.startLeft = width;
this.container = dom;
this.speedX = -2;
this.pipeWidth = 52
}
move(duration) {
this.group.forEach(item => {
item.move(duration)
})
while (this.group[0] && this.group[0].left < -80) {
let pipe = this.group.shift();
pipe.dom.remove()
}
}
createPipe() {
let div = document.createElement('div'),
pipeGroup = new PipeGroup(div, this.groupHeight, this.startLeft, this.speedX, this.gapHeight,
this.pipeWidth);
this.container.appendChild(div);
this.add(pipeGroup)
}
add(pipeGroup) {
if (pipeGroup instanceof PipeGroup) {
this.group.push(pipeGroup)
}
}
startProduce() {
if (this.timer) {
return
}
this.timer = setInterval(() => {
this.createPipe()
}, 1500);
}
stopProduce() {
clearInterval(this.timer);
this.timer = null
}
}
class Game {
constructor(gamedom, skydom, birddom) {
let bird = Game.getElementRect(birddom),
game = Game.getElementRect(gamedom),
sky = Game.getElementRect(skydom);
this.dom = game.dom;
this.gameHeight = sky.height;
this.bird = new Bird(bird.dom, bird.width, bird.height, bird.left, bird.top, 0.02, 0, sky
.height - bird.height);
this.pipeFactory = new PipeGroupFactory(sky.dom, game.width, sky.height);
this.pipeFactory.createPipe();
}
gaming() {
if (this.timer) {
return
}
this.pipeFactory.startProduce();
this.dom.classList.add('move')
this.timer = setInterval(() => {
this.bird.move(4);
this.pipeFactory.move(2)
this.isgameOver() && this.gameOver();
}, 30)
}
stopGaming() {
clearInterval(this.timer)
this.timer = null;
this.dom.classList.remove('move');
this.pipeFactory.stopProduce();
}
isgameOver() {
let bird = this.bird,
group = this.pipeFactory.group;
if (bird.top + bird.width >= this.gameHeight) {
return true;
}
for (let index = 0, length = group.length; index < length; index++) {
let pipe = group[index];
if (pipe.pass) {
continue
}
let obj = Game.isHit(bird, pipe)
pipe.pass = obj.pass;
if (obj.ispassing || !obj.pass) {
return obj.hit;
} else {
continue;
}
}
}
gameOver() {
this.stopGaming();
}
regEvent() {
window.onkeydown = (e) => {
if (e.key === "Enter") {
if (this.timer) {
this.stopGaming();
} else {
this.gaming();
}
} else if (e.key === " ") {
this.bird.jump();
}
}
}
static getElementRect(selector) {
let dom = document.querySelector(selector),
style = getComputedStyle(dom);
return {
dom,
width: parseInt(style.width),
height: parseInt(style.height),
top: parseInt(style.top),
left: parseInt(style.left)
}
}
static isHit(rec1, rec2) {
console.log(rec1,rec2)
if (rec1.left + rec1.width - rec2.left< 20) {
return {
hit: false,
pass: false,
ispassing: false
}
}
if (20 > rec2.left + rec2.width - rec1.left) {
return {
hit: false,
pass: true,
ispassing: false
}
}
if (rec1.top - rec2.upHeight > -20 && rec1.top + rec1.height - rec2.upHeight - rec2.gapHeight < 20) {
return {
hit: false,
pass: false,
ispassing: true
}
}
return {
hit: true,
pass: false,
ispassing: true
}
}
}
let game = new Game('.game', '.sky', '.bird');
game.regEvent();
game.gaming();
</script>
</body>
</html>
实现图
|