明年转前端,看了一下渡一教育像素鸟游戏,之后自己写了一遍,算是回顾这节课
DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>像素鸟title>
<link rel="stylesheet" href="./css/index.css">
head>
<body>
<div class="game">
<div class="sky">div>
<div class="land">div>
<div class="bird swing1">div>
div>
<p class="info">
回车键开始/暂停,空格键跳跃
p>
body>
<script src="./script/index.js">script>
html>
.game {
width: 800px;
height: 600px;
border: 2px solid;
margin: 0 auto;
position: relative;
overflow: hidden;
}
.game .sky {
background: url("../img/sky.png");
height: 100%;
width: 200%;
position: absolute;
top: 0;
left: 0;
}
.game .land {
background: url("../img/land.png");
height: 112px;
width: 200%;
position: absolute;
left: 0;
bottom: 0;
}
.game .bird {
background: url("../img/bird.png");
position: absolute;
width: 33px;
height: 26px;
left: 150px;
top: 150px;
}
.game .bird.swing1 {
background-position: -8px -10px;
}
.game .bird.swing2 {
background-position: -60px -10px;
}
.game .bird.swing3 {
background-position: -113px -10px;
}
.pipe {
position: absolute;
height: 100px;
width: 52px;
left: 500px;
}
.pipe.up {
background: url("../img/pipeDown.png");
top: 0;
background-position: bottom;
}
.pipe.down {
background: url("../img/pipeUp.png");
bottom: 112px;
}
.info {
text-align: center;
}
const gameDom = document.querySelector(".game");
const landDom = document.querySelector(".land");
const gameStyles = getComputedStyle(gameDom);
const landStyles = getComputedStyle(landDom);
const landHeight = parseFloat(landStyles.height);
const landTop = parseFloat(landStyles.top);
const gameHeight = parseFloat(gameStyles.height);
const gameWidth = parseFloat(gameStyles.width);
// 矩形抽象类
class Rectangle {
/**
* 举行抽象类的构造器,用于创建矩形
* @param {*} width 矩形长度
* @param {*} height 矩形高度
* @param {*} left 矩形left
* @param {*} top 矩形top
* @param {*} xSpeed 矩形x方向的速度
* @param {*} ySpeed 矩形y方向的速度
* @param {*} dom 矩形对应的dom
*/
constructor(width, height, left, top, xSpeed, ySpeed, dom) {
this.width = width;
this.height = height;
this.left = left;
this.top = top;
this.xSpeed = xSpeed;
this.ySpeed = ySpeed;
this.dom = dom;
this.render();
}
// 矩形的渲染方法,用于设置矩形的定位和长款
render() {
this.dom.style.width = this.width + "px";
this.dom.style.height = this.height + "px";
this.dom.style.left = this.left + "px";
this.dom.style.top = this.top + "px";
}
// 矩形的移动方法,用来改变矩形的定位,加入一个onMoving的生命周期函数
move(duration) {
this.left = this.left + this.xSpeed * duration;
this.top = this.top + this.ySpeed * duration;
typeof (this.onMoving) === "function" && this.onMoving();
this.render();
}
}
// 天空对象,继承自矩形
class Sky extends Rectangle {
constructor() {
const dom = document.querySelector(".sky");
const styles = getComputedStyle(dom);
super(parseFloat(styles.width), parseFloat(styles.height), 0, 0, -50, 0, dom);
}
// 天空每次运动到自身的一半,为避免超出(露馅)重新设置其left值
onMoving() {
if (this.left <= - this.width / 2) {
this.left = 0;
}
}
}
// 大地的对象,和天空类似
class Land extends Rectangle {
constructor(speed) {
const styles = getComputedStyle(landDom);
super(parseFloat(styles.width), parseFloat(styles.height), 0, parseFloat(styles.top), speed, 0, landDom);
}
onMoving() {
if (this.left <= - this.width / 2) {
this.left = 0;
}
}
}
// 鸟的对象,鸟没有横向的速度,移动的天空、大地和管子
class Bird extends Rectangle {
constructor() {
const dom = document.querySelector(".bird");
const styles = getComputedStyle(dom);
super(parseFloat(styles.width), parseFloat(styles.height), parseFloat(styles.left), parseFloat(styles.top), 0, 0, dom);
this.maxY = gameHeight - this.height - landHeight;
this.timer = null;
this.curSwingStatus = 1;
this.g = 1500; // 每毫秒下降15像素
this.render();
}
// 飞翔的方法,用于设置鸟飞翔的动作
startFly() {
if (this.timer) return;
this.timer = setInterval(() => {
this.curSwingStatus = (this.curSwingStatus + 1) == 4 ? 1 : (this.curSwingStatus + 1);
this.render();
}, 100);
}
// 停止飞翔
stopFly() {
clearInterval(this.timer);
this.timer = null;
}
// 每一次渲染都要设置一下鸟的翅膀状态
render() {
this.dom.className = `bird swing${
this.curSwingStatus}`;
super.render();
}
// 鸟不能超出游戏区域
onMoving() {
if (this.top < 0) {
this.top = 0;
}
else if (this.top > this.maxY) {
this.top = this.maxY;
}
}
// 鸟跳跃的方法
jump() {
this.ySpeed = -450;
}
// 鸟向下移动的方法
move(duration) {
super.move(duration);
this.ySpeed += this.g * duration;
}
}
// 管子对象
class Pipe extends Rectangle {
constructor(height, top, speed, dom) {
super(52, height, gameWidth, top, speed, 0, dom);
}
onMoving() {
if (this.left < -this.width) {
this.dom.remove();
}
}
}
function getRandomHeight(min, max) {
return Math.floor(Math.random() * (max - min) + min);
}
// 管子对的对象
class PipePair {
constructor(speed) {
this.spaceHeight = 150;
this.minHeight = 80;
this.maxHeight = landTop - this.minHeight - this.spaceHeight;
const upPipeHeight = getRandomHeight(this.minHeight, this.maxHeight);
const upPipeDom = document.createElement("div");
upPipeDom.className = "pipe up";
this.upPipe = new Pipe(upPipeHeight, 0, speed, upPipeDom);
const downPipeHeight = landTop - upPipeHeight - this.spaceHeight;
const downPipeDom = document.createElement("div");
downPipeDom.className = "pipe down";
const downPipeDomTop = landTop - downPipeHeight;
this.downPipe = new Pipe(downPipeHeight, downPipeDomTop, speed, downPipeDom);
gameDom.appendChild(upPipeDom);
gameDom.appendChild(downPipeDom);
}
// 管子对是否不可见(用于管子对工厂定期移除不可见的管子对)
get hidden() {
return this.upPipe.left <= -this.upPipe.width;
}
// 管子对移动的时候,需要上下两根管子一起东
move(duration) {
this.upPipe.move(duration);
this.downPipe.move(duration);
}
}
// 管子工厂
class PipePairFactory {
constructor(speed) {
this.speed = speed;
this.pipePairs = [];
this.newTimer = null;
this.moveTimer = null;
this.tick = 1500;
}
// 开始生产管子的方法
startNew() {
if (this.newTimer) return;
this.newTimer = setInterval(() => {
this.pipePairs.push(new PipePair(this.speed));
for (let o = 0; o < this.pipePairs.length; o++) {
const pipePair = this.pipePairs[o];
if (pipePair.hidden) {
this.pipePairs.splice(o, 1);
o--;
}
}
}, this.tick);
this.startRun();
}
// 管子对开始运动的方法
startRun() {
if (this.moveTimer) return;
this.moveTimer = setInterval(() => {
this.pipePairs.forEach(pipePair => {
pipePair.move(16 / 1000);
});
}, 16);
}
// 停止生产管子对的方法
stopNew() {
clearInterval(this.newTimer);
clearInterval(this.moveTimer);
this.newTimer = null;
this.moveTimer = null;
}
}
class Game {
constructor() {
const speed = -100;
this.sky = new Sky();
this.land = new Land(speed);
this.bird = new Bird();
this.pipePireFactory = new PipePairFactory(speed);
this.tick = 16;
this.speed = this.tick / 1000;
this.timer = null;
this.gameOver = false;
this.bindEvent();
}
isGameOver() {
if (this.bird.top == this.bird.maxY) {
return true;
}
// 获取在小鸟区域的柱子对
const pipePair = this.pipePireFactory.pipePairs.find(pipePair => {
const pipePairLeft = pipePair.upPipe.left;
const pipePairRight = pipePair.upPipe.left + pipePair.upPipe.width;
return pipePairLeft <= this.bird.left && this.bird.left <= pipePairRight;
});
return pipePair && (this.isConflict(this.bird, pipePair.upPipe) || this.isConflict(this.bird, pipePair.downPipe));
}
isConflict(rec1, rec2) {
// 碰撞检测:两矩形中心点距离小于两矩形长度的一半
const centerX1 = rec1.left + rec1.width / 2;
const centerY1 = rec1.top + rec1.height / 2;
const centerX2 = rec2.left + rec2.width / 2;
const centerY2 = rec2.top + rec2.height / 2;
const disX = Math.abs(centerX1 - centerX2);
const disY = Math.abs(centerY1 - centerY2);
return disX < (rec1.width + rec2.width) / 2 &&
disY < (rec1.height + rec2.height) / 2;
}
start() {
if (this.timer) return;
if (this.gameOver) {
location.reload();
return;
}
this.bird.startFly();
this.pipePireFactory.startNew();
this.timer = setInterval(() => {
this.sky.move(this.speed);
this.land.move(this.speed);
this.bird.move(this.speed);
if (this.isGameOver()) {
this.stop();
this.gameOver = true;
}
}, this.tick);
}
stop() {
clearInterval(this.timer);
this.timer = null;
this.bird.stopFly();
this.pipePireFactory.stopNew();
}
bindEvent() {
window.addEventListener("keyup", e => {
if (e.key == "Enter") {
if (this.timer) {
this.stop();
}
else {
this.start();
}
}
else if (e.key == " ") {
this.bird.jump();
}
});
}
}
var game = new Game();
game.start();
采用面向对象的方法编写,虽然很简单,但是还是觉得有价值