Bootstrap

JS小游戏-像素鸟#源码#Javascript

1、游戏图片

像素鸟小游戏
在这里插入图片描述

2、HTML部分

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <style>
        body{
            margin: 0;
        }
        .game{
            position: relative;
            width: 800px;
            height: 600px;
            margin: 0 auto;
            overflow: hidden;
        }
        .sky {
            position: absolute;
            width: 200%;
            height: 100%;
            background-image: url('./img/sky.png');
            margin: 0 auto;
        }

        .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;
        }
        .pipeDown{
            position: absolute;
            background-image: url('./img/pipeUp.png');
            width: 52px;
            height: 100px;
            left: 500px;
            bottom: 112px;
        }

        .pipeUp{
            position: absolute;
            background-image: url('./img/pipeDown.png');
            background-position: bottom;
            width: 52px;
            height: 100px;
            left: 500px;
            top: 0;
        }
        .ground{
            position: absolute;
            background-image: url('./img/land.png');
            width: 200%;
            height: 112px;
            left: 0;
            bottom: 0;
        }

        .score{
            position: absolute;
            width: 100px;
            height: 36px;
            background-color: lightblue;
            right: 0;
            top: 0;
            text-align: center;
            line-height: 36px;
            font-size: 24px;
            z-index: 100;
        }
        p{
            text-align: center;
        }


    </style>
</head>
<body>
    <div class="game">
        <div class="sky"></div>
        <div class="bird swing1"></div>
        <div class="ground"></div>
        <div class="score">0</div>
    </div>
    <p>按w或者按上开始游戏</p>

    <script src="./JS/Rectangle.js"></script>
    <script src="./JS/Sky.js"></script>
    <script src="./JS/Land.js"></script>
    <script src="./JS/Bird.js"></script>
    <script src="./JS/Pipe.js"></script>
    <script src="./JS/Game.js"></script>
</body>
</html>

3、JS部分

baseGame class
/**
 * 基础的游戏类
 * 属性: 宽度 高度、横坐标、纵坐标、横向速度、纵向速度、对应的dom元素
 */
class Rectangle {
    constructor(width,height,x,y,vx,vy,dom){
        this.width = width;
        this.height = height;
        this.x = x;
        this.y = y;
        this.vx = vx;
        this.vy = vy;
        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.x + "px";
        this.dom.style.top = this.y + "px";
    }
    onMove(){

    }
    /**
     *  在duration时间下物体移动
     * @param {number} duration  间隔
     */
    move(duration){
        this.x += this.vx * duration;
        this.y += this.vy * duration;
        if(this.onMove) this.onMove();
        this.render();
    }
}
sky ground class
const skyDom = document.querySelector('.sky');
skyStyle = getComputedStyle(skyDom);
const widthSky = parseFloat(skyStyle.width);
const heightSky = parseFloat(skyStyle.width);

class Sky extends Rectangle{
    constructor(){
        super(widthSky, heightSky, 0, 0, -100, 0, skyDom);
    }
    onMove(){
        if(this.x <= -widthSky / 2) {
            this.x = 0;
        }
    }
}
const landDom = document.querySelector('.ground');
landStyle = getComputedStyle(landDom);
const widthLand = parseFloat(landStyle.width);
const heightLand = parseFloat(landStyle.width);


class Land extends Rectangle{
    constructor(){
        super(widthLand, heightLand, 0, 488, -100, 0, landDom);
    }

    onMove(){
        if(this.x <= -widthLand / 2) {
            this.x = 0;
        }
    }
}
bird class
const birdDom = document.querySelector('.bird');
birdStyle = getComputedStyle(birdDom);
const widthBird = parseFloat(birdStyle.width);
const heightBird = parseFloat(birdStyle.height);

class Bird extends Rectangle{
    gravity = 1000;   
    constructor(){
        super(widthBird, heightBird, 150, 200, 0, 100, birdDom);
        this.swingState = 1;
        this.bindEvent();
    }
    
    // 綁定鼠標按下
    bindEvent(){
        document.addEventListener('keydown', (e) => {
            if(e.key === 'w' || e.key === 'ArrowUp'){
                this.vy += -550;
            }
        })
        this.startSwing();
    }

    // 小鳥扇翅膀
    startSwing(){
        if(this.timer){
            return ;
        }
        this.timer = setInterval( () => {
            birdDom.classList.remove('swing'+this.swingState);
            this.swingState = (++this.swingState % 3) + 1;
            birdDom.classList.add('swing'+this.swingState);
        },300)
    }
    stopSwing(){
        clearInterval(this.timer);
        this.timer = null;
    }

    onMove(){
        if(this.y >= 463) {
            this.y = 463;
            this.vy =0;
        }
        if(this.y <= 0 ){
            this.y = 0;
            this.vy = 0;
        }
    }

    move(duration){
        super.move(duration);
        this.vy += this.gravity * duration;
    }

}
pipe class
const game = document.querySelector('.game');
const gameWidth = game.clientWidth;
class Pipe extends Rectangle{
    isExited = true;
    constructor(height, top, speed, dom){
        super(52, height, gameWidth, top, speed, 0, dom);
    }
    onMove(){

        if(this.x < -this.width){
            this.dom.remove();
            this.isExited = false;
        }
    }

    
}

function getRandomNumber(min, max){
    return Math.floor(Math.random() * (max - min)) + min;
}

class PipePare{
    stopTimer = null;
    
    constructor(speed){
        this.up = document.createElement('div');
        this.down = document.createElement('div');
        this.up.classList.add('pipeUp');
        this.down.classList.add('pipeDown');
        game.appendChild(this.up);
        game.appendChild(this.down);

        this.spaceHeihgt = 150;  // 柱子之間的空隙 
        const fristPipeHeight = getRandomNumber(0, 488-150);

        this.upPipe = new Pipe(fristPipeHeight, 0, speed, this.up);
        this.downPipe = new Pipe(338 - fristPipeHeight, 150 + fristPipeHeight, speed, this.down);
    }
    stop(){
        if(this.stopTimer){
            clearInterval(this.stopTimer);
            this.stopTimer = null;
        }
        this.down.remove();
        this.up.remove();
    }

    move(duration){
        this.stopTimer = setInterval( () => {
            this.upPipe.move(duration);
            this.downPipe.move(duration);
        },10)
    }

    isCollision(bird){
        const birdStyle = getComputedStyle(document.querySelector('.bird'))
        let y = Number.parseInt(birdStyle.top);
        if(this.upPipe.x < bird.x + bird.width && this.upPipe.x > bird.x){
            return y < this.upPipe.height || y + bird.height > this.upPipe.height + this.spaceHeihgt;
        }
    }
}

class GamePipePair{
    pipeTimer = null;
    pipeArr = [];  // 用于显示的管道数组
    // 记录小鸟越过的数组
    scoreArr = [];
    constructor(speed){
        this.speed = speed;
        this.pair = new PipePare(speed);
        this.init();
    }

    init(){
        this.pipeTimer = setInterval( ()=> {
            let tmp = new PipePare(this.speed);
            tmp.move(0.01);
            this.pipeArr.push(tmp);
            this.scoreArr.push(tmp);
        }, 2000) 
    }

    getScore(bird){
        return this.scoreArr.filter(item => item.upPipe.x <= bird.x).length;
    }

    stop(){
        this.pipeArr.forEach(item => {
            item.stop();
        })
        if(this.pipeTimer){
            clearInterval(this.pipeTimer);
            this.pipeTimer = null;
        }
    }

    collisionDetection(bird){  // 小鸟对象传入
        this.pipeArr = this.pipeArr.filter(item => item.upPipe.isExited); // 过滤掉了不存在的
        // 检查小鸟是否碰撞到柱子
        for(let i = 0 ;i < this.pipeArr.length;i++){
            if(this.pipeArr[i].isCollision(bird)){
                return true;
            }
        }
        return false;
    }
    
}
game class
class Game {
    score = 0;
    land;
    sky;
    bird;
    backgroundTimer = null;
    pipeController = null;
    
    constructor(){
        this.land = new Land();
        this.sky = new Sky();
        this.bird = new Bird();
        this.state = 0;   // 0 遊戲結束  1遊戲開始進行中
        this.init();   
    }

    init(){
        document.onkeydown =  (e) => {
            if((e.key === 'w' || e.key === 'ArrowUp') && this.state === 0){
                console.log('開始遊戲');
                this.state = 1;
                this.startGame();
            }
        }
    }

    startGame(){
        this.pipeController = new GamePipePair(-100);
        if(this.backgroundTimer) return ;
        this.backgroundTimer = setInterval(() => {
            this.land.move(0.01);
            this.sky.move(0.01);
            this.bird.move(0.01);
            this.updateScore();
            if(this.pipeController){
                if(this.pipeController.collisionDetection(this.bird)){
                    this.endGame();
                    console.log('game ended');
                }
            }
        }, 10)


    }

    updateScore(){
        const score = document.querySelector('.score');
        this.score = this.pipeController.getScore(this.bird)
        score.innerHTML = this.score;
    }

    endGame(){
        if(this.backgroundTimer && this.state === 1){
            this.pipeController.stop();
            this.state = 0;
            this.bird.stopSwing();
            clearInterval(this.backgroundTimer);
            this.backgroundTimer = null;
            document.onkeydown = null;
            if(confirm(`游戏结束!! 
                你最后的得分是${this.score}分
                你想要再玩一局嘛 想玩点确定哦!!!
                `)){
                    this.init();
                }
        }
    }

}

const birdGame = new Game();

4、源码+静态资源

像素鸟源码地址

悦读

道可道,非常道;名可名,非常名。 无名,天地之始,有名,万物之母。 故常无欲,以观其妙,常有欲,以观其徼。 此两者,同出而异名,同谓之玄,玄之又玄,众妙之门。

;