使用pixi.js開發小遊戲須要注意的5個地方

這兩天用pixi開發了一款小遊戲,屬於射擊遊戲範疇。開局很簡單,上來玩就是了。javascript

demo.gif

但其實須要注意的事項也很多。下面簡單總結一下,有5點須要注意的地方,若是不注意,容易出現錯誤,或致使chrome崩潰。html

1、使用 requestAnimationFrame 代替定時器java

requestAnimationFrame比定時器更平滑,同時在性能上也更穩定。jquery

requestAnimationFrame(update);
function update(){
    //對 Sprite 的一些操做
    requestAnimationFrame(update);
}

2、碰撞檢測用bumpgit

因爲pixi本身並無碰撞檢測庫,因此不少人須要本身寫。但本身寫不免會出現問題。而bump是一個很成熟的碰撞檢測庫。github

對於一些並不是方形(邊角是空白的精靈)元素的碰撞,能夠採用hitTestCircle函數:chrome

if (b.hitTestCircle(SpriteA, SpriteB, true)) {
    //這裏是發生碰撞後的代碼
}

但須要在精靈上面定義circular屬性爲truecanvas

player.circular = true;

3、每隔一段時間出現一架敵機數組

不要使用定時器,而是要在requestAnimationFrame的update中,用一個自增的值來作判斷:app

//每隔一段時間,增長一個敵機
time++;
if (time >= 100) {
    //建立一個新的敵機
    time = 0;
}

這樣能夠避免定時器形成的性能消耗。

4、在精靈對象上設置時間屬性,實現定時開火

flyobj = new PIXI.Sprite(flyobjTexture);
flyobj.width = 50;
flyobj.height = 50;
flyobj.position.x = r(0, 512);
flyobj.position.y = 0;
flyobj.circular = true;
flyobj.time=0;//用來計算時間,進行開火
flyobjs.addChild(flyobj);

相應地,在循環敵機精靈羣的時候,也會對這個時間屬性進行自增,經過數值來增長髮射炮彈的效果:

//遍歷敵機
flyobjs.children.forEach(flyobj = >{
    flyobj.position.y += 2;
    flyobj.time += 1;
    if (flyobj.time >= 60) {
        //開火
        obj = new PIXI.Sprite(objTexture);
        obj.width = 20;
        obj.height = 20;
        obj.position.x = flyobj.position.x + flyobj.width / 2 - obj.width / 2;
        obj.position.y = flyobj.position.y + flyobj.height;
        obj.circular = true;
        objs.addChild(obj);
        flyobj.time = 1;
    }
    if (flyobj.position.y >= 500) {
        flyobjs.removeChild(flyobj);
    }
    //碰撞檢測
    if (b.hitTestCircle(flyobj, player, true)) {
        setTimeout(() = >{
            loop = false;
            gameover();
        },
        100);
    }
});

5、被消滅的敵機、超出畫布邊界的精靈、由於碰撞消失的精靈,只要從Container中移除就能夠了。

flyobjs.children.forEach(flyobj = >{
    if (b.hitTestCircle(flyobj, bullet, true)) {
        bullets.removeChild(bullet);
        flyobjs.removeChild(flyobj);

        //消滅的敵機+1
        number += 1;
        numberText.text = number;
    }
});

最後放上所有代碼:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script src="../pixi.min.js"></script>
    <script src="../bump.js"></script>
    <script src="../jquery-3.5.1.js"></script>
    
    <style>
        * {
            margin: 0;
            padding: 0;
        }

        .box {
            margin: 0 auto;
            width: 500px;
            height: 500px;
            position: relative;
            text-align: center;
        }
        #gameover{
            display: none;
        }
        .flex{
            position: absolute;
            width:100%;
            height: 100%;
            display: flex;
            flex-flow: column;
            justify-content: center;
        }
        h1{
            color:#fff;
        }
        button{
            width:200px;
            height:40px;
            border-radius: 10px;
            margin:10px auto;
        }
    </style>
</head>
<body onload="init();">
    <div id="box" class="box">
        <div id="gameover">
            <div class="flex">
                <h1>GAME OVER</h1>
                <button id="restart">restart</button>
            </div>
        </div>
    </div>
</body>
    <script type="text/javascript">
        function keyboard(keyCode) {
            let key = {};
            key.code = keyCode;
            key.isDown = false;
            key.isUp = true;
            key.press = undefined;
            key.release = undefined;
            //The `downHandler`
            key.downHandler = event => {
                if (event.keyCode === key.code) {
                    if (key.isUp && key.press) key.press();
                    key.isDown = true;
                    key.isUp = false;
                }
                event.preventDefault();
            };
            //The `upHandler`
            key.upHandler = event => {
                if (event.keyCode === key.code) {
                    if (key.isDown && key.release) key.release();
                    key.isDown = false;
                    key.isUp = true;
                }
                event.preventDefault();
            };
            //Attach event listeners
            window.addEventListener(
                "keydown", key.downHandler.bind(key), false
            );
            window.addEventListener(
                "keyup", key.upHandler.bind(key), false
            );
            return key;
        }
        function init() {
            let type = "WebGL"
            if (!PIXI.utils.isWebGLSupported()) {
                type = "canvas"
            }
            
            //碰撞檢測的
            b = new Bump(PIXI);
            
            PIXI.utils.sayHello(type);
            app = new PIXI.Application({
                width: 500,         // default: 800
                height: 500,        // default: 600
                antialias: true,    // default: false
                transparent: false, // default: false
                resolution: 1       // default: 1
            });
            app.renderer.backgroundColor = 0x061639;
            document.getElementById('box').appendChild(app.view);
            PIXI.loader
                .add([
                    "../images/player.png",
                    "../images/flyobj.png",
                    "../images/bullet.png",
                    "../images/obj.png"
                ])
                .on("progress", loadProgressHandler)
                .load(setup);
            function loadProgressHandler(loader, resource) {
                console.log("loading: " + resource.url);
                console.log("progress: " + loader.progress + "%");
            }
            function setup() {
                console.log("All files loaded");
                var playerTexture = PIXI.Texture.from("../images/player.png");                
                player = new PIXI.Sprite(playerTexture);
                player.circular = true;
                player.width = 50;
                player.height = 70;
                player.position.x = 512 / 2;
                player.position.y = 512 / 2;
                player.vx = 0;
                player.vy = 0;
                
                //消滅多少個敵機
                const style = new PIXI.TextStyle({
                    fontSize: 20,
                    fill: "white"
                });
                numberText = new PIXI.Text('0',style);
                numberText.position.x = 30;
                numberText.position.y = 30;
                app.stage.addChild(numberText);
                
                
                //敵機資源
                flyobjTexture = PIXI.Texture.from("../images/flyobj.png");
                //敵機數組
                flyobjs = new PIXI.Container();
                app.stage.addChild(flyobjs);
                
                
                //子彈資源
                bulletTexture = PIXI.Texture.from("../images/bullet.png");
                //子彈數組
                bullets = new PIXI.Container();
                app.stage.addChild(bullets);
                
                
                //敵機子彈資源
                objTexture = PIXI.Texture.from("../images/obj.png");
                //敵機子彈數組
                objs = new PIXI.Container();
                app.stage.addChild(objs);
                
                app.stage.addChild(player);
                //鍵盤事件
                var left = keyboard(37);
                var up = keyboard(38);
                var right = keyboard(39);
                var down = keyboard(40);
                //空格,開火
                var space = keyboard(32);
                space.press =()=>{
                    bullet = new PIXI.Sprite(bulletTexture);
                    bullet.width = 6;
                    bullet.height = 10;
                    bullet.position.x = player.position.x+player.width/2-bullet.width/2;
                    bullet.position.y = player.position.y;
                    bullet.circular = true;
                    bullets.addChild(bullet);
                }
                
                left.press = () => {
                    player.vx = -4;
                    player.vy = 0;
                };
                left.release = () => {
                    player.vx = 0;
                    player.vy = 0;
                };

                right.press = () => {
                    player.vx = 4;
                    player.vy = 0;
                };
                right.release = () => {
                    player.vx = 0;
                    player.vy = 0;
                };

                up.press = () => {
                    player.vx = 0;
                    player.vy = -4;
                };
                up.release = () => {
                    player.vx = 0;
                    player.vy = 0;
                };

                down.press = () => {
                    player.vx = 0;
                    player.vy = 4;
                };
                down.release = () => {
                    player.vx = 0;
                    player.vy = 0;
                };
                
                restart();
                
                requestAnimationFrame(update);
            }
            
            function r(min, max) {
                return Math.floor(Math.random() * (max - min)) + min
            }
            
            function gameover(){
                $('#gameover').show();
            }
            
            function restart(){
                //消滅的敵機數量
                number=0;
                numberText.text=number;
                
                //建立敵機的time
                create_flyobj_time=0;
                //loop 控制遊戲是否進行的全局
                loop=true;
                player.position.x=500/2-player.width/2;
                player.position.y=500-player.height;
                
                flyobjs.children=[];
                objs.children=[];
                bullets.children=[];
                loop=true;
                $('#gameover').hide();
            }
            
            function update() {
                console.log(flyobjs.children.length,objs.children.length,bullets.children.length)
                if(loop){
                    player.position.x += player.vx;
                    player.position.y += player.vy;

                    //避免超出畫布
                    if(player.position.x<=0){
                       player.position.x=0;
                    }
                    if(player.position.x>=500-player.width){
                       player.position.x=500-player.width;
                    }
                    if(player.position.y<=0){
                       player.position.y=0;
                    }
                    if(player.position.y>=500-player.height){
                       player.position.y=500-player.height;
                    }

                    //遍歷敵機
                    flyobjs.children.forEach(flyobj => {
                        flyobj.position.y += 2;
                        flyobj.time+=1;
                        if(flyobj.time>=60){
                            //開火
                            obj = new PIXI.Sprite(objTexture);
                            obj.width = 20;
                            obj.height = 20;
                            obj.position.x = flyobj.position.x+flyobj.width/2-obj.width/2;
                            obj.position.y = flyobj.position.y+flyobj.height;
                            obj.circular = true;
                            objs.addChild(obj);
                            flyobj.time=1;
                        }
                        if (flyobj.position.y >= 500) {
                            flyobjs.removeChild(flyobj);
                        }
                        //碰撞檢測
                        if (b.hitTestCircle(flyobj, player, true)) {
                            setTimeout(() => {
                                loop = false;
                                gameover();
                            }, 100);
                        }
                    });
                    
                    //遍歷子彈
                    bullets.children.forEach(bullet => {
                        bullet.position.y -= 4;
                        if (bullet.position.y <= 0) {
                            bullets.removeChild(bullet);
                        }
                        //碰撞檢測
                        flyobjs.children.forEach(flyobj => {
                            if (b.hitTestCircle(flyobj, bullet, true)) {
                                bullets.removeChild(bullet);
                                flyobjs.removeChild(flyobj);
                                
                                //消滅的敵機+1
                                number+=1;
                                numberText.text=number;
                            }
                        });
                        //
                    });
                    
                    //遍歷敵機子彈
                    objs.children.forEach(obj => {
                        obj.position.y += 4;
                        if (obj.position.y >= 500) {
                            objs.removeChild(obj);
                        }
                        //碰撞檢測
                        bullets.children.forEach(bullet => {
                            if (b.hitTestCircle(obj, bullet, true)) {
                                bullets.removeChild(bullet);
                                objs.removeChild(obj);
                            }
                        });
                        if (b.hitTestCircle(obj, player, true)) {
                            loop=false;
                            gameover();
                        }
                        //
                    });

                    //每隔一段時間,增長一個敵機
                    create_flyobj_time++;
                    if (create_flyobj_time >= 100) {
                        console.log('new fly obj');
                        flyobj = new PIXI.Sprite(flyobjTexture);
                        flyobj.width = 50;
                        flyobj.height = 50;
                        flyobj.position.x = r(0, 512);
                        flyobj.position.y = 0;
                        flyobj.circular = true;
                        flyobj.time=0;//用來計算時間,進行開火
                        flyobjs.addChild(flyobj);
                        create_flyobj_time = 0;
                    }
                }
                
                requestAnimationFrame(update);
            }
            
            
            //
            $('#restart').click(function(){
                restart();
            });
        }
    </script>
</html>

後記:這個遊戲寫到這裏,的確是能夠玩了。但其實繼續完善和優化的空間很大。好比設置本身飛機有幾條命,顯示飛機的血值,不按期出現大禮包,吃到炸彈能夠瞬間讓整個屏幕的敵機都爆炸等。之後有時間我會繼續完善。

在線體驗:https://wbjs.github.io/pixi_g...

github 地址: https://github.com/wbjs/pixi_...

歡迎你們star。

相關文章
相關標籤/搜索