這兩天用pixi開發了一款小遊戲,屬於射擊遊戲範疇。開局很簡單,上來玩就是了。javascript
但其實須要注意的事項也很多。下面簡單總結一下,有5點須要注意的地方,若是不注意,容易出現錯誤,或致使chrome崩潰。html
1、使用 requestAnimationFrame
代替定時器java
requestAnimationFrame比定時器更平滑,同時在性能上也更穩定。jquery
requestAnimationFrame(update); function update(){ //對 Sprite 的一些操做 requestAnimationFrame(update); }
2、碰撞檢測用bump
git
因爲pixi本身並無碰撞檢測庫,因此不少人須要本身寫。但本身寫不免會出現問題。而bump是一個很成熟的碰撞檢測庫。github
對於一些並不是方形(邊角是空白的精靈)元素的碰撞,能夠採用hitTestCircle
函數:chrome
if (b.hitTestCircle(SpriteA, SpriteB, true)) { //這裏是發生碰撞後的代碼 }
但須要在精靈上面定義circular屬性爲true
canvas
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。