Next year, I turned to the front and watched the game of Doyi Education pixel bird, and then I wrote it again to review this lesson

The file directories are as follows:

HTML structure

<! DOCTYPEhtml>
<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>Pixel bird</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">Enter key to start/pause, space bar jumps</p>
</body>
<script src="./script/index.js"></script>
</html>
Copy the code

css

.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;
}

Copy the code
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);

// Rectangle abstract class
class Rectangle {
	/** * holds the constructor for the abstract class used to create the rectangle *@param {*} Width The length of the rectangle *@param {*} Height Height of the rectangle *@param {*} Left Rectangle left *@param {*} Top Rectangle top star@param {*} XSpeed Speed of the rectangle in the x direction *@param {*} YSpeed Speed of the rectangle in the y direction *@param {*} The dom rectangle corresponds to 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();
    }

	// The rectangle rendering method is used to set the position and length of the rectangle
    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";
    }

    // The rectangle move method is used to change the position of the rectangle, adding an onMoving lifecycle function
    move(duration) {
        this.left = this.left + this.xSpeed * duration;
        this.top = this.top + this.ySpeed * duration;
        typeof (this.onMoving) === "function" && this.onMoving();
        this.render(); }}// The sky object, inherited from the rectangle
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);
    }
	
	// The sky moves half of itself at a time, resetting its left value to avoid overshooting
    onMoving() {
        if (this.left <= - this.width / 2) {
            this.left = 0; }}}// The object of the earth is similar to the sky
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; }}}// Bird objects, birds have no horizontal speed, moving sky, earth and pipes
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; // Drop 15 pixels per millisecond
        this.render();
    }

	// The method of flying is used to set the action of the bird flying
    startFly() {
        if (this.timer) return;
        this.timer = setInterval(() = > {
            this.curSwingStatus = (this.curSwingStatus + 1) = =4 ? 1 : (this.curSwingStatus + 1);
            this.render();
        }, 100);
    }

	// Stop flying
    stopFly() {
        clearInterval(this.timer);
        this.timer = null;
    }

	// Set the bird's wing state for each render
    render() {
        this.dom.className = `bird swingThe ${this.curSwingStatus}`;
        super.render();
    }

	// Birds must not be outside the game area
    onMoving() {
        if (this.top < 0) {
            this.top = 0;
        }
        else if (this.top > this.maxY) {
            this.top = this.maxY; }}// How birds jump
    jump() {
        this.ySpeed = -450;
    }

	// The way the bird moves down
    move(duration) {
        super.move(duration);
        this.ySpeed += this.g * duration; }}// Pipe object
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);
}

// The object of the tube pair
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);
    }

	// Whether the pipe pair is not visible (used for the pipe pair factory to periodically remove the invisible pipe pair)
    get hidden() {
        return this.upPipe.left <= -this.upPipe.width;
    }

	// When a pair of pipes moves, the two pipes need to move together
    move(duration) {
        this.upPipe.move(duration);
        this.downPipe.move(duration); }}// Pipe factory
class PipePairFactory {
    constructor(speed) {
        this.speed = speed;
        this.pipePairs = [];
        this.newTimer = null;
        this.moveTimer = null;
        this.tick = 1500;
    }

	// Start the process of producing pipes
    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();
    }

	// The method by which the pipe pair starts to move
    startRun() {
        if (this.moveTimer) return;
        this.moveTimer = setInterval(() = > {
            this.pipePairs.forEach(pipePair= > {
                pipePair.move(16 / 1000);
            });
        }, 16);
    }


	// Stop the process of producing pipe pairs
    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;
        }

        // Get the column pairs in the bird area
        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) {
        // Collision detection: the distance between the center points of two rectangles is less than half the length of the two rectangles
        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();


Copy the code

Using object-oriented writing, although very simple, but still feel valuable