PIXIJS实现豌豆侠大战愤怒鸟(简易版飞机大战)

PIXIJS简易教程

发现一款超好用的2D渲染引擎—PixiJS,非常适合制作H5小游戏。

pixiJS学习网址如下:
https://aitrade.ga/pixi.js-cn/PIXI.Sprite.html
https://www.bookstack.cn/read/LearningPixi/introduction


学习一门技术最好的方法就是做个demo,所以写了个简易版的飞机大战。效果如下:
在这里插入图片描述
视频不太好放就弄张截图。豌豆侠用豌豆去击落小鸟,小鸟碰到豌豆就game Over。用PixiJS开发H5小小游戏速度是很快,而且画面流畅。大致实现如下:



创建应用和画布

let app = new PIXI.Application({width: 256, height: 256});
document.body.appendChild(app.view);

这段代码相当于创建了一个黑色背景的canvas标签,其他设置可以参考官网。


加载图片资源

画布建好后,需要往里面添加图像,我们需要创建精灵来使这些图像可以被代码控制。精灵是如何被创建和显示的呢?

先加载图片资源,我们需要的图片资源有:一张背景图、豌豆侠、充当子弹的豌豆、愤怒鸟。图片加载完成后,在load里进行后续的操作。

let Application = PIXI.Application;
loader.add(
        [
            {name: "cover", url: "images/cover.jpg"},
            {name: "snake", url: "images/my.png"},
            {name: "bullet", url: "images/pea.png"},
            {name: "enemy", url: "images/bird.gif"}
        ]
    ).load(setup)



添加精灵

精灵是能用代码控制图像的基础,我们需要创建游戏需要的精灵,以豌豆侠为例:
从纹理缓存中取出豌豆侠图片,为该图片创建一个精灵,并设置该图片的位置大小等信息,再将该精灵添加到舞台中。

let my = TextureCache["my"];
let mySprite = new Sprite(my);
mySprite.width = 50;
mySprite.height = 50;
mySprite.x = 0;
mySprite.y = ch - mySprite.height;
mySprite.vx = 0;
mySprite.vy = 0;
app.stage.addChild(mySprite);  // 将精灵添加到舞台



按键移动豌豆侠

Pixi的ticker可以实现精灵移动,ticker中的代码一秒可以执行60次,将需要改变的位置信息放入里面。左键(左移)右键(右移)上键(发射),以右移为例:

right.press = () => {
    mySprite.vx = 5;  // 速度
    mySprite.vy = 0;
};

right.release = () => {
    if (right.isDown) {
        mySprite.vx = 0;
    }
};

app.ticker.add(delta => gameLoop(delta));
function gameLoop(delta) {
	mySprite.x += mySprite.vx;
}



发射豌豆

豌豆的创建和发射也是类似道理:为豌豆创建一个精灵,设置该精灵的大小和其他信息,位置设置为豌豆侠的位置。由于玩家可以持续按发射键,画布上会出现多粒豌豆的情况,所以创建一个数组放置多颗豌豆,记录点击次数,当次数超过豌豆数组长度时,点击次数重新置为0。

// 发射子弹
up.press = () => {
     bullets[bulletIndex].visible = up;
     bullets[bulletIndex].x = mySprite.x;
     bullets[bulletIndex].y = mySprite.y;
     bulletIndex++;

     if (bulletIndex == bulletNum - 1) {
         bulletIndex = 0;
     }
 };



创建愤怒鸟

循环创建愤怒鸟,并随机设置其位置信息。

let enemy = TextureCache["enemy"];
for (let i = 0; i < enemyNum; i++) {
    let enemySprite = new Sprite(enemy);
    enemySprite.width = 40;
    enemySprite.height = 40;
    enemySprite.y = getRandom(-1000, 0);
    enemySprite.x = getRandom(0, cw);  // 随机出现位置
    enemySprite.vx = 0;
    enemySprite.vy = -2;
    app.stage.addChild(enemySprite);
    enemys.push(enemySprite);
}



碰撞检测

几处地方需要检测碰撞:愤怒鸟碰到豌豆侠游戏结束、豌豆碰到愤怒鸟双双消失、豌豆侠只能左右移动且无法穿墙。注意图片的消失只是图片暂时被隐藏了,当它运动出画框还是会被重新显示。



一个比较粗糙的H5小游戏就完成了。代码也很粗糙,先贴出来,后续再完善:

window.onload = function () {
    initGame();
}

// 初始化游戏
function initGame() {
    let Application = PIXI.Application,
        Container = PIXI.Container,
        loader = PIXI.loader,
        resources = PIXI.loader.resources,
        TextureCache = PIXI.utils.TextureCache,
        Sprite = PIXI.Sprite,
        Rectangle = PIXI.Rectangle;

    let cw = document.documentElement.clientWidth;
    let ch = document.documentElement.clientHeight;

    let enemys = [];

    let bulletNum = 100; // 页面最多可以显示100个子弹
    let enemyNum = 10; // 页面最多可以显示100个敌机

    let app = new Application({
            width: cw,
            height: ch,
            antialias: true,
            transparent: false,
            resolution: 1
        }
    );

    document.body.appendChild(app.view);

    loader.add(
        [
            {name: "cover", url: "images/cover.jpg"},
            {name: "my", url: "images/my.png"},
            {name: "bullet", url: "images/pea.png"},
            {name: "enemy", url: "images/bird.gif"}
        ]
    ).load(setup)


    function setup() {
        let cover = TextureCache["cover"];
        let coverSprite = new Sprite(cover);
        app.stage.addChild(coverSprite);

        let state;
        let bullets = [];
        let bulletIndex = 0; // 第几个子弹

        // 我方
        let my = TextureCache["my"];
        let mySprite = new Sprite(my);
        mySprite.width = 50;
        mySprite.height = 50;
        mySprite.x = 0;
        mySprite.y = ch - mySprite.height;
        mySprite.vx = 0;
        mySprite.vy = 0;
        app.stage.addChild(mySprite);


        initBullet(bullets);
        initEnemy();


        let left = keyboard(37),
            up = keyboard(38),
            right = keyboard(39),
            down = keyboard(40);

        // 我方移动(左右)
        left.press = () => {
            mySprite.vx = -5;
            mySprite.vy = 0;

        };

        left.release = () => {
            if (left.isDown) {
                mySprite.vx = 0;
            }
        };

        right.press = () => {
            mySprite.vx = 5;  // 速度
            mySprite.vy = 0;
        };

        right.release = () => {
            if (right.isDown) {
                mySprite.vx = 0;
            }
        };

        // 发射子弹
        up.press = () => {
            bullets[bulletIndex].visible = up;
            bullets[bulletIndex].x = mySprite.x;
            bullets[bulletIndex].y = mySprite.y;
            bulletIndex++;

            if (bulletIndex == bulletNum - 1) {
                bulletIndex = 0;
            }
        };


        state = play;
        app.ticker.add(delta => gameLoop(delta));

        function gameLoop(delta) {
            state(delta);
        }

        // 页面1秒刷新60次
        function play(delta) {
            mySprite.x += mySprite.vx;
            // 墙壁触碰
            if (mySprite.x <= 0) {
                mySprite.x = 0
            }
            if (mySprite.x >= cw - mySprite.width) {
                mySprite.x = cw - mySprite.width
            }
            bullets.forEach(m => {
                m.y += m.vy;
            })
            enemys.forEach(m => {
                m.y -= m.vy;
                // 出框则位置改变
                if (m.y > ch) {
                    m.y = getRandom(-1000, 0);
                    m.visible = true;
                }

                // 我方触到敌机
                if (isCollision(mySprite, m) && m.visible) {
                    alert('Game Over!');
                }
            })

            bullets.forEach(m => {
                for (let i = 0; i < enemys.length; i++) {
                    if (isCollision(m, enemys[i]) && m.visible && enemys[i].visible) {
                        m.visible = false;
                        enemys[i].visible = false;
                    }
                }
            })

        }
    }

    // 初始化子弹
    function initBullet(bullets) {
        let bullet = TextureCache["bullet"];
        for (let i = 0; i < bulletNum; i++) {
            let bulletSprite = new Sprite(bullet);
            bulletSprite.width = 30;
            bulletSprite.height = 30;
            bulletSprite.vx = 0;
            bulletSprite.vy = -5;
            bulletSprite.visible = false;
            app.stage.addChild(bulletSprite);
            bullets.push(bulletSprite);
        }
    }

    // 初始化敌机
    function initEnemy() {
        // 敌机
        let enemy = TextureCache["enemy"];
        for (let i = 0; i < enemyNum; i++) {
            let enemySprite = new Sprite(enemy);
            enemySprite.width = 40;
            enemySprite.height = 40;
            enemySprite.y = getRandom(-1000, 0);
            enemySprite.x = getRandom(0, cw);  // 随机出现位置
            enemySprite.vx = 0;
            enemySprite.vy = -2;
            app.stage.addChild(enemySprite);
            enemys.push(enemySprite);
        }
    }

    // 碰撞检测
    function isCollision(r1, r2) {

        let hit, combinedHalfWidths, combinedHalfHeights, vx, vy;

        hit = false;

        r1.centerX = r1.x + r1.width / 2;
        r1.centerY = r1.y + r1.height / 2;
        r2.centerX = r2.x + r2.width / 2;
        r2.centerY = r2.y + r2.height / 2;

        r1.halfWidth = r1.width / 2;
        r1.halfHeight = r1.height / 2;
        r2.halfWidth = r2.width / 2;
        r2.halfHeight = r2.height / 2;

        vx = r1.centerX - r2.centerX;
        vy = r1.centerY - r2.centerY;

        combinedHalfWidths = r1.halfWidth + r2.halfWidth;
        combinedHalfHeights = r1.halfHeight + r2.halfHeight;

        if (Math.abs(vx) < combinedHalfWidths) {

            if (Math.abs(vy) < combinedHalfHeights) {
                hit = true;
            } else {
                hit = false;
            }
        } else {
            hit = false;
        }
        return hit;
    }
}

function keyboard(keyCode) {
    let key = {};
    key.code = keyCode;
    key.isDown = false;
    key.isUp = true;
    key.press = undefined;
    key.release = undefined;

    key.downHandler = event => {
        if (event.keyCode === key.code) {
            if (key.isUp && key.press) key.press();
            key.isDown = true;
            key.isUp = false;
        }
        event.preventDefault();
    };

    key.upHandler = event => {
        if (event.keyCode === key.code) {
            if (key.isDown && key.release) key.release();
            key.isDown = false;
            key.isUp = true;
        }
        event.preventDefault();
    };

    window.addEventListener(
        "keydown", key.downHandler.bind(key), false
    );
    window.addEventListener(
        "keyup", key.upHandler.bind(key), false
    );
    return key;
}

// 随机数生成
function getRandom(min, max) {
    return Math.round(Math.random() * (max - min)) + min
}
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值