原生js像素鸟游戏

明年转前端,看了一下渡一教育像素鸟游戏,之后自己写了一遍,算是回顾这节课

文件目录如下:
原生js像素鸟游戏_第1张图片

html结构

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>

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

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();


采用面向对象的方法编写,虽然很简单,但是还是觉得有价值

你可能感兴趣的:(web小游戏开发,javascript,游戏,小游戏)